Convert Narya (most of the way) over to a Maven Ant task based build. The
ActionScript bits remain belligerent, but the Java stuff is mostly shipshape. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@6222 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd;
|
||||
|
||||
import com.samskivert.util.Logger;
|
||||
|
||||
/**
|
||||
* Contains a reference to the log object used by the Crowd services.
|
||||
*/
|
||||
public class Log
|
||||
{
|
||||
/** We dispatch our log messages through this logger. */
|
||||
public static Logger log = Logger.getLogger("com.threerings.crowd");
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
|
||||
/**
|
||||
* Provides a way for clients to speak on chat channels.
|
||||
*/
|
||||
public interface ChannelSpeakService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Requests to speak the supplied message on the specified channel.
|
||||
*/
|
||||
public void speak (Client client, ChatChannel channel, String message, byte mode);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatMessage;
|
||||
|
||||
/**
|
||||
* A chat display provides a means by which chat messages can be
|
||||
* displayed. The chat display will be notified when chat messages of
|
||||
* various sorts have been received by the client.
|
||||
*/
|
||||
public interface ChatDisplay
|
||||
{
|
||||
/**
|
||||
* Called to clear the chat display.
|
||||
*/
|
||||
void clear ();
|
||||
|
||||
/**
|
||||
* Called to display a chat message.
|
||||
*
|
||||
* @param alreadyDisplayed true if a previous chat display in the list has
|
||||
* already displayed this message, false otherwise.
|
||||
*
|
||||
* @return true if the message was displayed, false if not.
|
||||
*/
|
||||
boolean displayMessage (ChatMessage msg, boolean alreadyDisplayed);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Filters messages chat messages to or from the server.
|
||||
*/
|
||||
public interface ChatFilter
|
||||
{
|
||||
/**
|
||||
* Filter a chat message.
|
||||
* @param msg the message text to be filtered.
|
||||
* @param otherUser an optional argument that represents the target or the speaker, depending
|
||||
* on 'outgoing', and can be considered in filtering if it is provided.
|
||||
* @param outgoing true if the message is going out to the server.
|
||||
*
|
||||
* @return the filtered message, or null to block it completely.
|
||||
*/
|
||||
String filter (String msg, Name otherUser, boolean outgoing);
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* The chat services provide a mechanism by which the client can broadcast chat messages to all
|
||||
* clients that are subscribed to a particular place object or directly to a particular client.
|
||||
* These services should not be used directly, but instead should be accessed via the
|
||||
* {@link ChatDirector}.
|
||||
*/
|
||||
public interface ChatService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Used to communicate the response to a {@link ChatService#tell} request.
|
||||
*/
|
||||
public static interface TellListener extends InvocationListener
|
||||
{
|
||||
/**
|
||||
* Communicates the response to a {@link ChatService#tell} request.
|
||||
*
|
||||
* @param idleTime the number of ms the tellee has been idle or 0L if they are not idle.
|
||||
* @param awayMessage the away message configured by the told player or null if they have
|
||||
* no away message.
|
||||
*/
|
||||
void tellSucceeded (long idleTime, String awayMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that a tell message be delivered to the user with username equal to
|
||||
* <code>target</code>.
|
||||
*
|
||||
* @param client a connected, operational client instance.
|
||||
* @param target the username of the user to which the tell message should be delivered.
|
||||
* @param message the contents of the message.
|
||||
* @param listener the reference that will receive the tell response.
|
||||
*/
|
||||
void tell (Client client, Name target, String message, TellListener listener);
|
||||
|
||||
/**
|
||||
* Requests that a message be broadcast to all users in the system.
|
||||
*
|
||||
* @param client a connected, operational client instance.
|
||||
* @param message the contents of the message.
|
||||
* @param listener the reference that will receive a failure response.
|
||||
*/
|
||||
void broadcast (Client client, String message, InvocationListener listener);
|
||||
|
||||
/**
|
||||
* Sets this client's away message. If the message is null or the empty string, the away
|
||||
* message will be cleared.
|
||||
*/
|
||||
void away (Client client, String message);
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.samskivert.util.RandomUtil;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* A chat filter that can filter out curse words from user chat.
|
||||
*/
|
||||
public abstract class CurseFilter implements ChatFilter
|
||||
{
|
||||
/** Indicates how messages should be handled. */
|
||||
public enum Mode { DROP, COMIC, VERNACULAR, UNFILTERED; }
|
||||
|
||||
/**
|
||||
* Creates a curse filter. The curse words should be a string in the following format:
|
||||
*
|
||||
* <pre>
|
||||
* *penis*=John_Thomas shit*=barnacle muff=britches
|
||||
* </pre>
|
||||
*
|
||||
* The key/value pairs are separated by spaces, * matches word characters and the value after
|
||||
* the = is the string into which to convert the text when converting to the vernacular.
|
||||
* Underscores in the target string will be turned into spaces.
|
||||
*
|
||||
* <p> And stopWords should be in the following format:
|
||||
*
|
||||
* <pre>
|
||||
* *faggot* rape rapes raped raping
|
||||
* </pre>
|
||||
*
|
||||
* Words are separated by spaces and * matches any other word characters.
|
||||
*/
|
||||
public CurseFilter (String curseWords, String stopWords)
|
||||
{
|
||||
configureCurseWords(curseWords);
|
||||
configureStopWords(stopWords);
|
||||
}
|
||||
|
||||
/**
|
||||
* The client will need to provide a way to look up our current chat filter mode.
|
||||
*/
|
||||
public abstract Mode getFilterMode ();
|
||||
|
||||
// from interface ChatFilter
|
||||
public String filter (String msg, Name otherUser, boolean outgoing)
|
||||
{
|
||||
// first, check against the drop-always list
|
||||
_stopMatcher.reset(msg);
|
||||
if (_stopMatcher.find()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// then see what kind of curse filtering the user has configured
|
||||
Mode level = getFilterMode();
|
||||
if (level == Mode.UNFILTERED) {
|
||||
return msg;
|
||||
}
|
||||
|
||||
StringBuffer inbuf = new StringBuffer(msg);
|
||||
StringBuffer outbuf = new StringBuffer(msg.length());
|
||||
for (int ii=0, nn=_matchers.length; ii < nn; ii++) {
|
||||
Matcher m = _matchers[ii];
|
||||
m.reset(inbuf);
|
||||
while (m.find()) {
|
||||
switch (level) {
|
||||
case DROP:
|
||||
return null;
|
||||
|
||||
case COMIC:
|
||||
m.appendReplacement(outbuf,
|
||||
_replacements[ii].replace(" ", comicChars(_comicLength[ii])));
|
||||
break;
|
||||
|
||||
case VERNACULAR:
|
||||
String vernacular = _vernacular[ii];
|
||||
if (Character.isUpperCase(m.group(2).codePointAt(0))) {
|
||||
int firstCharLen = Character.charCount(vernacular.codePointAt(0));
|
||||
vernacular = vernacular.substring(0, firstCharLen).toUpperCase() +
|
||||
vernacular.substring(firstCharLen);
|
||||
}
|
||||
m.appendReplacement(outbuf, _replacements[ii].replace(" ", vernacular));
|
||||
break;
|
||||
|
||||
case UNFILTERED:
|
||||
// We returned the msg unadulterated above in this case, so it should be
|
||||
// impossible to wind up here, but let's enumerate it so we can let the compiler
|
||||
// scream about missing enum values in a switch
|
||||
log.warning("Omg? We're trying to filter chat even though we're unfiltered?");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
if (outbuf.length() == 0) {
|
||||
// optimization: if we didn't find a match, jump to the next
|
||||
// pattern without doing any StringBuilder jimmying
|
||||
continue;
|
||||
}
|
||||
m.appendTail(outbuf);
|
||||
|
||||
// swap the buffers around and clear the output
|
||||
StringBuffer temp = inbuf;
|
||||
inbuf = outbuf;
|
||||
outbuf = temp;
|
||||
outbuf.setLength(0);
|
||||
}
|
||||
|
||||
return inbuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the curse word portion of our filtering.
|
||||
*/
|
||||
protected void configureCurseWords (String curseWords)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(curseWords);
|
||||
int numWords = st.countTokens();
|
||||
|
||||
_matchers = new Matcher[numWords];
|
||||
_replacements = new String[numWords];
|
||||
_vernacular = new String[numWords];
|
||||
_comicLength = new int[numWords];
|
||||
|
||||
for (int ii=0; ii < numWords; ii++) {
|
||||
String mapping = st.nextToken();
|
||||
StringTokenizer st2 = new StringTokenizer(mapping, "=");
|
||||
if (st2.countTokens() != 2) {
|
||||
log.warning("Something looks wrong in the x.cursewords properties (" +
|
||||
mapping + "), skipping.");
|
||||
continue;
|
||||
}
|
||||
String curse = st2.nextToken();
|
||||
|
||||
String s = "";
|
||||
String p = "";
|
||||
if (curse.startsWith("*")) {
|
||||
curse = curse.substring(1);
|
||||
p += "([a-zA-Z]*)";
|
||||
s += "$1";
|
||||
} else {
|
||||
p += "()";
|
||||
}
|
||||
s += " ";
|
||||
p += " ";
|
||||
if (curse.endsWith("*")) {
|
||||
curse = curse.substring(0, curse.length() - 1);
|
||||
p += "([a-zA-Z]*)";
|
||||
s += "$3";
|
||||
}
|
||||
|
||||
String pattern = "\\b" + p.replace(" ", "(" + curse + ")") + "\\b";
|
||||
Pattern pat = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
|
||||
_matchers[ii] = pat.matcher("");
|
||||
_replacements[ii] = s;
|
||||
_vernacular[ii] = st2.nextToken().replace('_', ' ');
|
||||
_comicLength[ii] = curse.codePointCount(0, curse.length());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the words that will stop.
|
||||
*/
|
||||
protected void configureStopWords (String stopWords)
|
||||
{
|
||||
StringTokenizer st = new StringTokenizer(stopWords);
|
||||
|
||||
String pattern = "";
|
||||
while (st.hasMoreTokens()) {
|
||||
if ("".equals(pattern)) {
|
||||
pattern += "(";
|
||||
} else {
|
||||
pattern += "|";
|
||||
}
|
||||
pattern += getStopWordRegexp(st.nextToken());
|
||||
}
|
||||
pattern += ")";
|
||||
|
||||
setStopPattern(pattern);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets our stop word matcher to one for the given regular expression.
|
||||
*/
|
||||
protected void setStopPattern (String pattern)
|
||||
{
|
||||
_stopMatcher = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).matcher("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns a naughty word into a regular expression to catch it.
|
||||
*/
|
||||
protected String getStopWordRegexp (String word)
|
||||
{
|
||||
return "\\b" + word.replace("*", "[A-Za-z]*") + "\\b";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a comicy replacement of the specified length.
|
||||
*/
|
||||
protected String comicChars (int length)
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
for (int ii=0; ii < length; ii++) {
|
||||
buf.append(RandomUtil.pickRandom(COMIC_CHARS));
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** A matcher that will always cause a message to be dropped if it matches. */
|
||||
protected Matcher _stopMatcher;
|
||||
|
||||
/** Matchers for each curseword. */
|
||||
protected Matcher[] _matchers;
|
||||
|
||||
/** Length of comic-y replacements for each curseword. */
|
||||
protected int[] _comicLength;
|
||||
|
||||
/** Replacements. */
|
||||
protected String[] _replacements;
|
||||
|
||||
/** Replacements for each curseword "in the vernacular". */
|
||||
protected String[] _vernacular;
|
||||
|
||||
/** Comic replacement characters. */
|
||||
protected static final String[] COMIC_CHARS = { "!", "@", "#", "%", "&", "*" };
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatMessage;
|
||||
|
||||
/**
|
||||
* Stores chat history.
|
||||
*/
|
||||
public class HistoryList extends ArrayList<ChatMessage>
|
||||
implements ChatDisplay
|
||||
{
|
||||
/** An interface for chat history observers. */
|
||||
public interface Observer {
|
||||
/** Called when messages have been added or removed from the chat history.
|
||||
* @param adjustment the number of messages that have been added (+) or removed (-). */
|
||||
void historyUpdated (int adjustment);
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean displayMessage (ChatMessage msg, boolean alreadyDisplayed)
|
||||
{
|
||||
// see if we're full, and if so, clear out a bunch of old stuff
|
||||
int adjusted;
|
||||
if (size() == MAX_HISTORY) {
|
||||
removeRange(0, PRUNE_HISTORY);
|
||||
adjusted = PRUNE_HISTORY;
|
||||
|
||||
} else {
|
||||
adjusted = 0;
|
||||
}
|
||||
|
||||
// add the message to the history
|
||||
add(msg);
|
||||
|
||||
// notify observers that something changed
|
||||
notify(adjusted);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear ()
|
||||
{
|
||||
// see how many entries we're clearing out..
|
||||
int adjusted = size();
|
||||
super.clear();
|
||||
|
||||
// and notify the chat displays of that fact
|
||||
notify(adjusted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link Observer} that wants to know about changes to the history.
|
||||
*/
|
||||
public void addObserver (Observer obs)
|
||||
{
|
||||
_obs.add(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link Observer} from hearing about changes to the history.
|
||||
*/
|
||||
public void removeObserver (Observer obs)
|
||||
{
|
||||
_obs.remove(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies listening {@link Observer}s that there has been a change to this history.
|
||||
*/
|
||||
protected void notify (int adjustment)
|
||||
{
|
||||
_historyUpdatedOp.setAdjustment(adjustment);
|
||||
_obs.apply(_historyUpdatedOp);
|
||||
}
|
||||
|
||||
protected static class HistoryUpdatedOp
|
||||
implements ObserverList.ObserverOp<Observer>
|
||||
{
|
||||
public void setAdjustment (int adjustment) {
|
||||
_adjustment = adjustment;
|
||||
}
|
||||
|
||||
public boolean apply (Observer obs) {
|
||||
obs.historyUpdated(_adjustment);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected int _adjustment;
|
||||
}
|
||||
|
||||
/** A list of {@link Observer}s interested in history changes. */
|
||||
protected ObserverList<Observer> _obs = ObserverList.newFastUnsafe();
|
||||
|
||||
/** An operation used to notify observers of history updates. */
|
||||
protected HistoryList.HistoryUpdatedOp _historyUpdatedOp = new HistoryUpdatedOp();
|
||||
|
||||
/** The maximum number of history entries we'll keep. */
|
||||
protected static final int MAX_HISTORY = 2000;
|
||||
|
||||
/** The number of history entries we'll prune when we hit the max. */
|
||||
protected static final int PRUNE_HISTORY = 200;
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import com.threerings.util.MessageBundle;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.BasicDirector;
|
||||
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* Manages the mutelist.
|
||||
*
|
||||
* TODO: This class right now is pretty much just a placeholder.
|
||||
*/
|
||||
public class MuteDirector extends BasicDirector
|
||||
implements ChatFilter
|
||||
{
|
||||
/**
|
||||
* An interface that can be registered with the MuteDirector to
|
||||
* receive notifications to the mutelist.
|
||||
*/
|
||||
public static interface MuteObserver
|
||||
{
|
||||
/**
|
||||
* The specified player was added or removed from the mutelist.
|
||||
*/
|
||||
void muteChanged (Name playername, boolean nowMuted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be instantiated after the ChatDirector.
|
||||
*/
|
||||
public MuteDirector (CrowdContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the mute director with the specified list of initial mutees.
|
||||
*/
|
||||
public MuteDirector (CrowdContext ctx, Name[] list)
|
||||
{
|
||||
this(ctx);
|
||||
|
||||
Collections.addAll(_mutelist, list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to shut down the mute director.
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
if (_chatdir != null) {
|
||||
_chatdir.removeChatFilter(this);
|
||||
_chatdir = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the required ChatDirector.
|
||||
*/
|
||||
public void setChatDirector (ChatDirector chatdir)
|
||||
{
|
||||
if (_chatdir == null) {
|
||||
_chatdir = chatdir;
|
||||
_chatdir.addChatFilter(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified mutelist observer.
|
||||
*/
|
||||
public void addMuteObserver (MuteObserver obs)
|
||||
{
|
||||
_observers.add(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified mutelist observer.
|
||||
*/
|
||||
public void removeMuteObserver (MuteObserver obs)
|
||||
{
|
||||
_observers.remove(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the specified user is muted.
|
||||
*/
|
||||
public boolean isMuted (Name username)
|
||||
{
|
||||
return _mutelist.contains(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mute or unmute the specified user.
|
||||
*/
|
||||
public void setMuted (Name username, boolean mute)
|
||||
{
|
||||
boolean changed = mute ? _mutelist.add(username) : _mutelist.remove(username);
|
||||
String feedback;
|
||||
if (mute) {
|
||||
feedback = "m.muted";
|
||||
} else {
|
||||
feedback = changed ? "m.unmuted" : "m.notmuted";
|
||||
}
|
||||
|
||||
// always give some feedback to the user
|
||||
_chatdir.displayFeedback(null, MessageBundle.tcompose(feedback, username));
|
||||
|
||||
// if the mutelist actually changed, notify observers
|
||||
if (changed) {
|
||||
notifyObservers(username, mute);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a list of the currently muted players.
|
||||
*
|
||||
* This list may be out of date immediately upon returning from this method.
|
||||
*/
|
||||
public Name[] getMuted ()
|
||||
{
|
||||
return _mutelist.toArray(new Name[_mutelist.size()]);
|
||||
}
|
||||
|
||||
// documentation inherited from interface ChatFilter
|
||||
public String filter (String msg, Name otherUser, boolean outgoing)
|
||||
{
|
||||
// we are only concerned with filtering things going to or coming
|
||||
// from muted users
|
||||
if ((otherUser != null) && isMuted(otherUser)) {
|
||||
// if it was outgoing, explain the dropped message, otherwise
|
||||
// silently drop
|
||||
if (outgoing) {
|
||||
_chatdir.displayFeedback(null, "m.no_tell_mute");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify our observers of a change in the mutelist.
|
||||
*/
|
||||
protected void notifyObservers (final Name username, final boolean muted)
|
||||
{
|
||||
_observers.apply(new ObserverList.ObserverOp<MuteObserver>() {
|
||||
public boolean apply (MuteObserver observer) {
|
||||
observer.muteChanged(username, muted);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** The chat director that we're working hard for. */
|
||||
protected ChatDirector _chatdir;
|
||||
|
||||
/** The mutelist. */
|
||||
protected HashSet<Name> _mutelist = Sets.newHashSet();
|
||||
|
||||
/** List of mutelist observers. */
|
||||
protected ObserverList<MuteObserver> _observers =
|
||||
new ObserverList<MuteObserver>(ObserverList.FAST_UNSAFE_NOTIFY);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* Provides a means by which "speaking" can be allowed among subscribers
|
||||
* of a particular distributed object.
|
||||
*/
|
||||
public interface SpeakService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Issues a request to speak "on" the distributed object via which
|
||||
* this speak service was provided.
|
||||
*
|
||||
* @param message the message to be spoken.
|
||||
* @param mode the "mode" of the message. This is an opaque value that
|
||||
* will be passed back down via the {@link ChatDirector} to the {@link
|
||||
* ChatDisplay} implementations which can interpret it in an
|
||||
* application specific manner. It's useful for differentiating
|
||||
* between regular speech, emotes, etc.
|
||||
*/
|
||||
void speak (Client client, String message, byte mode);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChannelSpeakService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link ChannelSpeakService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from ChannelSpeakService.java.")
|
||||
public class ChannelSpeakMarshaller extends InvocationMarshaller
|
||||
implements ChannelSpeakService
|
||||
{
|
||||
/** The method id used to dispatch {@link #speak} requests. */
|
||||
public static final int SPEAK = 1;
|
||||
|
||||
// from interface ChannelSpeakService
|
||||
public void speak (Client arg1, ChatChannel arg2, String arg3, byte arg4)
|
||||
{
|
||||
sendRequest(arg1, SPEAK, new Object[] {
|
||||
arg2, arg3, Byte.valueOf(arg4)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* Represents a chat channel.
|
||||
*/
|
||||
public abstract class ChatChannel extends SimpleStreamableObject
|
||||
implements Comparable<ChatChannel>, DSet.Entry
|
||||
{
|
||||
// from interface Comparable<ChatChannel>
|
||||
public abstract int compareTo (ChatChannel other);
|
||||
|
||||
/**
|
||||
* Converts this channel into a unique name that can be used as the name of the distributed
|
||||
* lock used when resolving the channel.
|
||||
*/
|
||||
public abstract String getLockName ();
|
||||
|
||||
// from interface DSet.Entry
|
||||
public Comparable<?> getKey ()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
return compareTo((ChatChannel)other) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract int hashCode ();
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
import com.threerings.presents.data.Permission;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatDirector;
|
||||
import com.threerings.crowd.chat.client.SpeakService;
|
||||
|
||||
/**
|
||||
* Contains codes used by the chat invocation services.
|
||||
*/
|
||||
public interface ChatCodes extends InvocationCodes
|
||||
{
|
||||
/** A return value used by the ChatDirector and possibly other entities to indicate successful
|
||||
* processing of chat. */
|
||||
public static final String SUCCESS = "success";
|
||||
|
||||
/** The message identifier for a chat notification message. */
|
||||
public static final String CHAT_NOTIFICATION = "crowd.chat";
|
||||
|
||||
/** The message identifier for a chat channel notification message. */
|
||||
public static final String CHAT_CHANNEL_NOTIFICATION = "crowd.chat.channel";
|
||||
|
||||
/** The access control identifier for normal chat privileges. */
|
||||
public static final Permission CHAT_ACCESS = new Permission();
|
||||
|
||||
/** The access control identifier for broadcast chat privileges. */
|
||||
public static final Permission BROADCAST_ACCESS = new Permission();
|
||||
|
||||
/** The configuration key for idle time. */
|
||||
public static final String IDLE_TIME_KEY = "narya.chat.idle_time";
|
||||
|
||||
/** The default time after which a player is assumed idle. */
|
||||
public static final long DEFAULT_IDLE_TIME = 3 * 60 * 1000L;
|
||||
|
||||
/** The chat localtype code for chat messages delivered on the place object currently occupied
|
||||
* by the client. This is the only type of chat message that will be delivered unless the chat
|
||||
* director is explicitly provided with other chat message sources via {@link
|
||||
* ChatDirector#addAuxiliarySource}. */
|
||||
public static final String PLACE_CHAT_TYPE = "placeChat";
|
||||
|
||||
/** The chat localtype for messages received on the user object. */
|
||||
public static final String USER_CHAT_TYPE = "userChat";
|
||||
|
||||
/** The default mode used by {@link SpeakService#speak} requests. */
|
||||
public static final byte DEFAULT_MODE = 0;
|
||||
|
||||
/** A {@link SpeakService#speak} mode to indicate that the user is thinking what they're
|
||||
* saying, or is it that they're saying what they're thinking? */
|
||||
public static final byte THINK_MODE = 1;
|
||||
|
||||
/** A {@link SpeakService#speak} mode to indicate that a speak is actually an emote. */
|
||||
public static final byte EMOTE_MODE = 2;
|
||||
|
||||
/** A {@link SpeakService#speak} mode to indicate that a speak is actually a shout. */
|
||||
public static final byte SHOUT_MODE = 3;
|
||||
|
||||
/** A {@link SpeakService#speak} mode to indicate that a speak is actually a server-wide
|
||||
* broadcast. */
|
||||
public static final byte BROADCAST_MODE = 4;
|
||||
|
||||
/** The last chat mode defined in the interface. */
|
||||
public static final byte LAST_MODE = BROADCAST_MODE;
|
||||
|
||||
/** String translations for the various chat modes. */
|
||||
public static final String[] XLATE_MODES = {
|
||||
"default", "think", "emote", "shout", "broadcast"
|
||||
};
|
||||
|
||||
/** An error code delivered when the user targeted for a tell notification is not online. */
|
||||
public static final String USER_NOT_ONLINE = "m.user_not_online";
|
||||
|
||||
/** An error code delivered when the user targeted for a tell notification is disconnected. */
|
||||
public static final String USER_DISCONNECTED = "m.user_disconnected";
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link ChatService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from ChatService.java.")
|
||||
public class ChatMarshaller extends InvocationMarshaller
|
||||
implements ChatService
|
||||
{
|
||||
/**
|
||||
* Marshalls results to implementations of {@link ChatService.TellListener}.
|
||||
*/
|
||||
public static class TellMarshaller extends ListenerMarshaller
|
||||
implements TellListener
|
||||
{
|
||||
/** The method id used to dispatch {@link #tellSucceeded}
|
||||
* responses. */
|
||||
public static final int TELL_SUCCEEDED = 1;
|
||||
|
||||
// from interface TellMarshaller
|
||||
public void tellSucceeded (long arg1, String arg2)
|
||||
{
|
||||
_invId = null;
|
||||
omgr.postEvent(new InvocationResponseEvent(
|
||||
callerOid, requestId, TELL_SUCCEEDED,
|
||||
new Object[] { Long.valueOf(arg1), arg2 }, transport));
|
||||
}
|
||||
|
||||
@Override // from InvocationMarshaller
|
||||
public void dispatchResponse (int methodId, Object[] args)
|
||||
{
|
||||
switch (methodId) {
|
||||
case TELL_SUCCEEDED:
|
||||
((TellListener)listener).tellSucceeded(
|
||||
((Long)args[0]).longValue(), (String)args[1]);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchResponse(methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #away} requests. */
|
||||
public static final int AWAY = 1;
|
||||
|
||||
// from interface ChatService
|
||||
public void away (Client arg1, String arg2)
|
||||
{
|
||||
sendRequest(arg1, AWAY, new Object[] {
|
||||
arg2
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #broadcast} requests. */
|
||||
public static final int BROADCAST = 2;
|
||||
|
||||
// from interface ChatService
|
||||
public void broadcast (Client arg1, String arg2, InvocationService.InvocationListener arg3)
|
||||
{
|
||||
ListenerMarshaller listener3 = new ListenerMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, BROADCAST, new Object[] {
|
||||
arg2, listener3
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #tell} requests. */
|
||||
public static final int TELL = 3;
|
||||
|
||||
// from interface ChatService
|
||||
public void tell (Client arg1, Name arg2, String arg3, ChatService.TellListener arg4)
|
||||
{
|
||||
ChatMarshaller.TellMarshaller listener4 = new ChatMarshaller.TellMarshaller();
|
||||
listener4.listener = arg4;
|
||||
sendRequest(arg1, TELL, new Object[] {
|
||||
arg2, arg3, listener4
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import com.threerings.util.ActionScript;
|
||||
|
||||
/**
|
||||
* The abstract base class of all the client-side ChatMessage objects.
|
||||
*/
|
||||
public abstract class ChatMessage
|
||||
implements Streamable
|
||||
{
|
||||
/** The actual text of the message. */
|
||||
public String message;
|
||||
|
||||
/** The bundle to use when translating this message. */
|
||||
public String bundle;
|
||||
|
||||
/** The client side 'localtype' of this chat, set to the type registered with an auxiliary
|
||||
* source in the ChatDirector. */
|
||||
public transient String localtype;
|
||||
|
||||
/** The client time that this message was created. */
|
||||
@ActionScript(type="int")
|
||||
public transient long timestamp;
|
||||
|
||||
/**
|
||||
* For all your unserialization needs.
|
||||
*/
|
||||
public ChatMessage ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a ChatMessage.
|
||||
*/
|
||||
public ChatMessage (String message, String bundle)
|
||||
{
|
||||
this.message = message;
|
||||
this.bundle = bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Once this message reaches the client, the information contained within is changed around a
|
||||
* bit.
|
||||
*/
|
||||
public void setClientInfo (String msg, String ltype)
|
||||
{
|
||||
message = msg;
|
||||
localtype = ltype;
|
||||
bundle = null;
|
||||
timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the appropriate message format for this message.
|
||||
*/
|
||||
public String getFormat ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
return StringUtil.shortClassName(this) + StringUtil.fieldsToString(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.crowd.chat.server.SpeakUtil;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Marks a {@link Name} as disinterested in chat history such that {@link SpeakUtil} will keep no
|
||||
* messages sent to it.
|
||||
*/
|
||||
public interface KeepNoHistory
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.SpeakService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link SpeakService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from SpeakService.java.")
|
||||
public class SpeakMarshaller extends InvocationMarshaller
|
||||
implements SpeakService
|
||||
{
|
||||
/** The method id used to dispatch {@link #speak} requests. */
|
||||
public static final int SPEAK = 1;
|
||||
|
||||
// from interface SpeakService
|
||||
public void speak (Client arg1, String arg2, byte arg3)
|
||||
{
|
||||
sendRequest(arg1, SPEAK, new Object[] {
|
||||
arg2, Byte.valueOf(arg3)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Provides a mechanism by which the speak service can identify chat listeners so as to maintain a
|
||||
* recent history of all chat traffic on the server.
|
||||
*/
|
||||
public interface SpeakObject
|
||||
{
|
||||
/** Used in conjunction with {@link SpeakObject#applyToListeners}. */
|
||||
public static interface ListenerOp
|
||||
{
|
||||
/** Call this method if you only have access to body oids. */
|
||||
void apply (int bodyOid);
|
||||
|
||||
/** Call this method if you can provide usernames directly. */
|
||||
void apply (Name username);
|
||||
}
|
||||
|
||||
/**
|
||||
* The speak service will call this every time a chat message is delivered on this speak object
|
||||
* to note the listeners that received the message.
|
||||
*/
|
||||
void applyToListeners (ListenerOp op);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
/**
|
||||
* A ChatMessage that represents a message that came from the server and did not result from direct
|
||||
* user action.
|
||||
*/
|
||||
public class SystemMessage extends ChatMessage
|
||||
{
|
||||
/** Attention level constant to indicate that this message is merely providing the user with
|
||||
* information. */
|
||||
public static final byte INFO = 0;
|
||||
|
||||
/** Attention level constant to indicate that this message is the result of a user action. */
|
||||
public static final byte FEEDBACK = 1;
|
||||
|
||||
/** Attention level constant to indicate that some action is required. */
|
||||
public static final byte ATTENTION = 2;
|
||||
|
||||
/** The attention level of this message. */
|
||||
public byte attentionLevel;
|
||||
|
||||
// documentation inherited
|
||||
public SystemMessage ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a SystemMessage.
|
||||
*/
|
||||
public SystemMessage (String message, String bundle, byte attentionLevel)
|
||||
{
|
||||
super(message, bundle);
|
||||
this.attentionLevel = attentionLevel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* A feedback message to indicate that a tell succeeded.
|
||||
*/
|
||||
public class TellFeedbackMessage extends UserMessage
|
||||
{
|
||||
/**
|
||||
* A tell feedback message is only composed on the client.
|
||||
*/
|
||||
public TellFeedbackMessage (Name target, String message, boolean failure)
|
||||
{
|
||||
super(target, null, message, ChatCodes.DEFAULT_MODE);
|
||||
_failure = failure;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is a failure feedback, false if it is successful tell feedback.
|
||||
*/
|
||||
public boolean isFailure ()
|
||||
{
|
||||
return _failure;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat ()
|
||||
{
|
||||
return _failure ? null : "m.told_format";
|
||||
}
|
||||
|
||||
protected boolean _failure;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* A ChatMessage representing a message that came from another user.
|
||||
*/
|
||||
public class UserMessage extends ChatMessage
|
||||
{
|
||||
/** The user that the message came from. */
|
||||
public Name speaker;
|
||||
|
||||
/** The mode of the message. @see ChatCodes.DEFAULT_MODE */
|
||||
public byte mode;
|
||||
|
||||
/**
|
||||
* For unserialization.
|
||||
*/
|
||||
public UserMessage ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a user message.
|
||||
*/
|
||||
public UserMessage (Name speaker, String bundle, String message, byte mode)
|
||||
{
|
||||
super(message, bundle);
|
||||
this.speaker = speaker;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a user message for a player originated tell (which has no bundle and is in the
|
||||
* default mode).
|
||||
*/
|
||||
public UserMessage (Name speaker, String message)
|
||||
{
|
||||
super(message, null);
|
||||
this.speaker = speaker;
|
||||
this.mode = ChatCodes.DEFAULT_MODE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name to display for the speaker. Some types of messages may wish to not use the
|
||||
* canonical name for the speaker and should thus override this function.
|
||||
*/
|
||||
public Name getSpeakerDisplayName ()
|
||||
{
|
||||
return speaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat ()
|
||||
{
|
||||
switch (mode) {
|
||||
case ChatCodes.THINK_MODE: return "m.think_format";
|
||||
case ChatCodes.EMOTE_MODE: return "m.emote_format";
|
||||
case ChatCodes.SHOUT_MODE: return "m.shout_format";
|
||||
case ChatCodes.BROADCAST_MODE: return "m.broadcast_format";
|
||||
default: // fall through
|
||||
}
|
||||
|
||||
if (ChatCodes.USER_CHAT_TYPE.equals(localtype)) {
|
||||
return "m.tell_format";
|
||||
}
|
||||
return "m.speak_format";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* A system message triggered by the activity of another user. If the user is muted we can suppress
|
||||
* this message, unlike a normal system message.
|
||||
*/
|
||||
public class UserSystemMessage extends SystemMessage
|
||||
{
|
||||
/** The "speaker" of this message, the user that triggered that this message be sent to us. */
|
||||
public Name speaker;
|
||||
|
||||
/** Suitable for unserialization. */
|
||||
public UserSystemMessage ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a INFO-level UserSystemMessage.
|
||||
*/
|
||||
public UserSystemMessage (Name sender, String message, String bundle)
|
||||
{
|
||||
this(sender, message, bundle, INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a UserSystemMessage.
|
||||
*/
|
||||
public UserSystemMessage (Name sender, String message, String bundle, byte attentionLevel)
|
||||
{
|
||||
super(message, bundle, attentionLevel);
|
||||
this.speaker = sender;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChannelSpeakMarshaller;
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link ChannelSpeakProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from ChannelSpeakService.java.")
|
||||
public class ChannelSpeakDispatcher extends InvocationDispatcher<ChannelSpeakMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public ChannelSpeakDispatcher (ChannelSpeakProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelSpeakMarshaller createMarshaller ()
|
||||
{
|
||||
return new ChannelSpeakMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case ChannelSpeakMarshaller.SPEAK:
|
||||
((ChannelSpeakProvider)provider).speak(
|
||||
source, (ChatChannel)args[0], (String)args[1], ((Byte)args[2]).byteValue()
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChannelSpeakService;
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link ChannelSpeakService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from ChannelSpeakService.java.")
|
||||
public interface ChannelSpeakProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link ChannelSpeakService#speak} request.
|
||||
*/
|
||||
void speak (ClientObject caller, ChatChannel arg1, String arg2, byte arg3);
|
||||
}
|
||||
@@ -0,0 +1,503 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.primitives.Longs;
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.samskivert.util.ArrayIntSet;
|
||||
import com.samskivert.util.ResultListener;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.annotation.AnyThread;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import com.threerings.presents.server.PresentsDObjectMgr;
|
||||
|
||||
import com.threerings.presents.peer.data.ClientInfo;
|
||||
import com.threerings.presents.peer.data.NodeObject;
|
||||
import com.threerings.presents.peer.server.PeerManager;
|
||||
import com.threerings.presents.peer.server.PeerManager.NodeRequest;
|
||||
import com.threerings.presents.peer.server.NodeRequestsListener;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.chat.server.SpeakUtil.ChatHistoryEntry;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdCodes;
|
||||
import com.threerings.crowd.peer.data.CrowdClientInfo;
|
||||
import com.threerings.crowd.peer.data.CrowdNodeObject;
|
||||
import com.threerings.crowd.peer.server.CrowdPeerManager;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Handles chat channel services.
|
||||
*/
|
||||
public abstract class ChatChannelManager
|
||||
implements ChannelSpeakProvider
|
||||
{
|
||||
/**
|
||||
* Value asynchronously returned by {@link #collectChatHistory} after polling all peer nodes.
|
||||
*/
|
||||
public static class ChatHistoryResult
|
||||
{
|
||||
/** The set of nodes that either did not reply within the timeout, or had a failure. */
|
||||
public Set<String> failedNodes;
|
||||
|
||||
/** The things in the user's chat history, aggregated from all nodes and sorted by
|
||||
* timestamp. */
|
||||
public List<ChatHistoryEntry> history;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a body becomes a member of a channel, this method should be called so that any server
|
||||
* that happens to be hosting that channel can be told that the body in question is now a
|
||||
* participant.
|
||||
*/
|
||||
@AnyThread
|
||||
public void bodyAddedToChannel (ChatChannel channel, int bodyId)
|
||||
{
|
||||
_peerMan.invokeNodeAction(new ParticipantChanged(channel, bodyId, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a body loses channel membership, this method should be called so that any server that
|
||||
* happens to be hosting that channel can be told that the body in question is now a
|
||||
* participant.
|
||||
*/
|
||||
@AnyThread
|
||||
public void bodyRemovedFromChannel (ChatChannel channel, int bodyId)
|
||||
{
|
||||
_peerMan.invokeNodeAction(new ParticipantChanged(channel, bodyId, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all chat messages heard by the given user on all peers.
|
||||
*/
|
||||
@AnyThread
|
||||
public void collectChatHistory (Name user, final ResultListener<ChatHistoryResult> lner)
|
||||
{
|
||||
NodeRequestsListener<List<ChatHistoryEntry>> listener =
|
||||
new NodeRequestsListener<List<ChatHistoryEntry>>() {
|
||||
public void requestsProcessed (NodeRequestsResult<List<ChatHistoryEntry>> rRes) {
|
||||
ChatHistoryResult chRes = new ChatHistoryResult();
|
||||
chRes.failedNodes = rRes.getNodeErrors().keySet();
|
||||
chRes.history = Lists.newArrayList(
|
||||
Iterables.concat(rRes.getNodeResults().values()));
|
||||
Collections.sort(chRes.history, SORT_BY_TIMESTAMP);
|
||||
lner.requestCompleted(chRes);
|
||||
}
|
||||
public void requestFailed (String cause) {
|
||||
lner.requestFailed(new InvocationException(cause));
|
||||
}
|
||||
};
|
||||
_peerMan.invokeNodeRequest(new ChatCollectionRequest(user), listener);
|
||||
}
|
||||
|
||||
// from interface ChannelSpeakProvider
|
||||
public void speak (ClientObject caller, final ChatChannel channel, String message, byte mode)
|
||||
{
|
||||
final UserMessage umsg = new UserMessage(
|
||||
((BodyObject)caller).getVisibleName(), null, message, mode);
|
||||
|
||||
// if we're hosting this channel, dispatch it directly
|
||||
if (_channels.containsKey(channel)) {
|
||||
dispatchSpeak(channel, umsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// if we're resolving this channel, queue up our message for momentary deliver
|
||||
List<UserMessage> msgs = _resolving.get(channel);
|
||||
if (msgs != null) {
|
||||
msgs.add(umsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// forward the speak request to the server that hosts the channel in question
|
||||
_peerMan.invokeNodeAction(new ForwardChannelSpeak(channel, umsg), new Runnable() {
|
||||
public void run () {
|
||||
_resolving.put(channel, Lists.newArrayList(umsg));
|
||||
resolveAndDispatch(channel);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates our singleton manager and registers our invocation service.
|
||||
*/
|
||||
@Inject protected ChatChannelManager (PresentsDObjectMgr omgr, InvocationManager invmgr)
|
||||
{
|
||||
invmgr.registerDispatcher(new ChannelSpeakDispatcher(this), CrowdCodes.CROWD_GROUP);
|
||||
|
||||
// create and start our idle channel closer; this will run as long as omgr is alive
|
||||
omgr.newInterval(new Runnable() {
|
||||
public void run () {
|
||||
closeIdleChannels();
|
||||
}
|
||||
}).schedule(IDLE_CHANNEL_CHECK_PERIOD, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the channel specified in the supplied action and then dispatches it.
|
||||
*/
|
||||
protected void resolveAndDispatch (final ChatChannel channel)
|
||||
{
|
||||
NodeObject.Lock lock = new NodeObject.Lock("ChatChannel", channel.getLockName());
|
||||
_peerMan.performWithLock(lock, new PeerManager.LockedOperation() {
|
||||
public void run () {
|
||||
((CrowdNodeObject)_peerMan.getNodeObject()).addToHostedChannels(channel);
|
||||
finishResolveAndDispatch(channel);
|
||||
}
|
||||
public void fail (String peerName) {
|
||||
List<UserMessage> msgs = _resolving.remove(channel);
|
||||
if (peerName == null) {
|
||||
log.warning("Failed to resolve chat channel due to lock failure",
|
||||
"channel", channel);
|
||||
} else {
|
||||
// some other peer resolved this channel first, so forward any queued messages
|
||||
// directly to that node
|
||||
for (UserMessage msg : msgs) {
|
||||
_peerMan.invokeNodeAction(peerName, new ForwardChannelSpeak(channel, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the participant set for the specified chat channel and dispatches all pending
|
||||
* messages to the channel. End users of the chat channel system should override this method
|
||||
* and do what is necessary to resolve the channel's participant set and call {@link
|
||||
* #resolutionComplete} or {@link #resolutionFailed}.
|
||||
*/
|
||||
protected void finishResolveAndDispatch (ChatChannel channel)
|
||||
{
|
||||
resolutionComplete(channel, new ArrayIntSet());
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called when a channel's participant set has been resolved.
|
||||
*/
|
||||
protected void resolutionComplete (ChatChannel channel, Set<Integer> parts)
|
||||
{
|
||||
// map the participants of our now resolved channel
|
||||
ChannelInfo info = new ChannelInfo();
|
||||
info.channel = channel;
|
||||
info.participants = parts;
|
||||
_channels.put(channel, info);
|
||||
|
||||
// dispatch any pending messages now that we know where they go
|
||||
for (UserMessage msg : _resolving.remove(channel)) {
|
||||
dispatchSpeak(channel, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be called if channel resolution fails.
|
||||
*/
|
||||
protected void resolutionFailed (ChatChannel channel, Exception cause)
|
||||
{
|
||||
log.warning("Failed to resolve chat channel", "channel", channel, cause);
|
||||
|
||||
// alas, we just drop all pending messages because we're hosed
|
||||
_resolving.remove(channel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that we dispatch the supplied message to all participants of the specified chat
|
||||
* channel. The speaker will be validated prior to dispatching the message as the originating
|
||||
* server does not have the information it needs to validate the speaker and must leave that to
|
||||
* us, the channel hosting server.
|
||||
*/
|
||||
protected void dispatchSpeak (ChatChannel channel, UserMessage message)
|
||||
{
|
||||
final ChannelInfo info = _channels.get(channel);
|
||||
if (info == null) {
|
||||
// TODO: maybe we should just reresolve the channel...
|
||||
log.warning("Requested to dispatch speak on unhosted channel", "channel", channel,
|
||||
"msg", message);
|
||||
return;
|
||||
}
|
||||
|
||||
// validate the speaker
|
||||
if (!info.participants.contains(getBodyId(message.speaker))) {
|
||||
log.warning("Dropping channel chat message from non-speaker", "channel", channel,
|
||||
"message", message);
|
||||
return;
|
||||
}
|
||||
|
||||
// note that we're dispatching a message on this channel
|
||||
info.lastMessage = System.currentTimeMillis();
|
||||
|
||||
// generate a mapping from node name to an array of body ids for the participants that are
|
||||
// currently on the node in question
|
||||
final Map<String,int[]> partMap = Maps.newHashMap();
|
||||
for (NodeObject nodeobj : _peerMan.getNodeObjects()) {
|
||||
ArrayIntSet nodeBodyIds = new ArrayIntSet();
|
||||
for (ClientInfo clinfo : nodeobj.clients) {
|
||||
int bodyId = getBodyId(((CrowdClientInfo)clinfo).visibleName);
|
||||
if (info.participants.contains(bodyId)) {
|
||||
nodeBodyIds.add(bodyId);
|
||||
}
|
||||
}
|
||||
partMap.put(nodeobj.nodeName, nodeBodyIds.toIntArray());
|
||||
}
|
||||
|
||||
for (Map.Entry<String,int[]> entry : partMap.entrySet()) {
|
||||
_peerMan.invokeNodeAction(
|
||||
entry.getKey(), new DispatchChannelSpeak(channel, message, entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers the supplied chat channel message to the specified bodies.
|
||||
*/
|
||||
protected void deliverSpeak (ChatChannel channel, UserMessage message, int[] bodyIds)
|
||||
{
|
||||
channel = intern(channel);
|
||||
for (int bodyId : bodyIds) {
|
||||
BodyObject bobj = getBodyObject(bodyId);
|
||||
if (bobj != null && shouldDeliverSpeak(channel, message, bobj)) {
|
||||
SpeakUtil.recordToChatHistory(channel, message, bobj.getVisibleName());
|
||||
bobj.postMessage(ChatCodes.CHAT_CHANNEL_NOTIFICATION, channel, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called periodically to check for and close any channels that have been idle too long.
|
||||
*/
|
||||
protected void closeIdleChannels ()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
Iterator<Map.Entry<ChatChannel, ChannelInfo>> iter = _channels.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Map.Entry<ChatChannel, ChannelInfo> entry = iter.next();
|
||||
if (now - entry.getValue().lastMessage > IDLE_CHANNEL_CLOSE_TIME) {
|
||||
((CrowdNodeObject)_peerMan.getNodeObject()).removeFromHostedChannels(
|
||||
entry.getKey());
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ratifies the delivery of the supplied chat channel message to the specified body. Derived
|
||||
* classes can override this method to implement channel disabling, mute lists or any other
|
||||
* suppression they might need.
|
||||
*/
|
||||
protected boolean shouldDeliverSpeak (ChatChannel channel, UserMessage message, BodyObject body)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a widely referenced instance equivalent to the given channel, if one is available.
|
||||
* This reduces memory usage since clients send new channel instances with each message.
|
||||
*/
|
||||
protected ChatChannel intern (ChatChannel channel)
|
||||
{
|
||||
ChannelInfo chinfo = _channels.get(channel);
|
||||
if (chinfo != null) {
|
||||
return chinfo.channel;
|
||||
}
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a speaker's visible name into a unique integer id. This is not the oid for this
|
||||
* speaker but rather a persistent integer identifier that can be passed between servers and
|
||||
* used to look up the speaker on the target server via a call to {@link #getBodyObject}. We
|
||||
* use this rather than names to avoid having to send (large) {@link Name} objects for every
|
||||
* channel participant to each individual peer that will be forwarding messages.
|
||||
*/
|
||||
protected abstract int getBodyId (Name speaker);
|
||||
|
||||
/**
|
||||
* Locates a body object from the given unique id. May return null.
|
||||
*/
|
||||
protected abstract BodyObject getBodyObject (int bodyId);
|
||||
|
||||
/** Forwards a channel speak request from the server hosting the message originator to the
|
||||
* server that is hosting the channel. */
|
||||
protected abstract static class ChannelAction extends PeerManager.NodeAction
|
||||
{
|
||||
public ChannelAction (ChatChannel channel) {
|
||||
_channel = channel;
|
||||
}
|
||||
public ChannelAction () {
|
||||
}
|
||||
@Override public boolean isApplicable (NodeObject nodeobj) {
|
||||
return ((CrowdNodeObject)nodeobj).hostedChannels.contains(_channel);
|
||||
}
|
||||
protected ChatChannel _channel;
|
||||
@Inject protected transient ChatChannelManager _channelMan;
|
||||
}
|
||||
|
||||
/** Informs the server hosting a channel that a body has been added to or removed from the
|
||||
* channel's participants set. */
|
||||
protected static class ParticipantChanged extends ChannelAction
|
||||
{
|
||||
public ParticipantChanged (ChatChannel channel, int bodyId, boolean added) {
|
||||
super(channel);
|
||||
_bodyId = bodyId;
|
||||
_added = added;
|
||||
}
|
||||
public ParticipantChanged () {
|
||||
}
|
||||
@Override protected void execute () {
|
||||
ChannelInfo info = _channelMan._channels.get(_channel);
|
||||
if (info != null) {
|
||||
if (_added) {
|
||||
info.participants.add(_bodyId);
|
||||
} else {
|
||||
info.participants.remove(_bodyId);
|
||||
}
|
||||
} else if (_channelMan._resolving.containsKey(_channel)) {
|
||||
log.warning("Oh for fuck's sake, distributed systems are complicated",
|
||||
"channel", _channel);
|
||||
}
|
||||
}
|
||||
protected int _bodyId;
|
||||
protected boolean _added;
|
||||
}
|
||||
|
||||
protected static class ChatCollectionRequest extends NodeRequest
|
||||
{
|
||||
public ChatCollectionRequest (Name user)
|
||||
{
|
||||
_user = user;
|
||||
}
|
||||
|
||||
public ChatCollectionRequest ()
|
||||
{
|
||||
}
|
||||
|
||||
@Override public boolean isApplicable (NodeObject nodeobj)
|
||||
{
|
||||
// poll all nodes
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override protected void execute (InvocationService.ResultListener listener)
|
||||
{
|
||||
// find all the UserMessages for the given user and send them back
|
||||
listener.requestProcessed(Lists.newArrayList(Iterables.filter(
|
||||
SpeakUtil.getChatHistory(_user), IS_USER_MESSAGE)));
|
||||
}
|
||||
|
||||
protected Name _user;
|
||||
}
|
||||
|
||||
protected static final Predicate<ChatHistoryEntry> IS_USER_MESSAGE =
|
||||
new Predicate<ChatHistoryEntry>() {
|
||||
public boolean apply (ChatHistoryEntry entry) {
|
||||
return entry.message instanceof UserMessage;
|
||||
}
|
||||
};
|
||||
|
||||
protected static final Comparator<ChatHistoryEntry> SORT_BY_TIMESTAMP =
|
||||
new Comparator<ChatHistoryEntry>() {
|
||||
public int compare (ChatHistoryEntry e1, ChatHistoryEntry e2) {
|
||||
return Longs.compare(e1.message.timestamp, e2.message.timestamp);
|
||||
}
|
||||
};
|
||||
|
||||
/** Forwards a channel speak request from the server hosting the message originator to the
|
||||
* server that is hosting the channel. */
|
||||
protected static class ForwardChannelSpeak extends ChannelAction
|
||||
{
|
||||
public ForwardChannelSpeak (ChatChannel channel, UserMessage message) {
|
||||
super(channel);
|
||||
_message = message;
|
||||
}
|
||||
public ForwardChannelSpeak () {
|
||||
}
|
||||
@Override protected void execute () {
|
||||
_channelMan.dispatchSpeak(_channel, _message);
|
||||
}
|
||||
protected UserMessage _message;
|
||||
}
|
||||
|
||||
/** Forwards a chat channel message to the server to which some subset of the channel
|
||||
* participants are connected so that it can dispatch the message on their body objects. */
|
||||
protected static class DispatchChannelSpeak extends ForwardChannelSpeak
|
||||
{
|
||||
public DispatchChannelSpeak (ChatChannel channel, UserMessage message, int[] bodyIds) {
|
||||
super(channel, message);
|
||||
_bodyIds = bodyIds;
|
||||
}
|
||||
public DispatchChannelSpeak () {
|
||||
}
|
||||
@Override public boolean isApplicable (NodeObject nodeobj) {
|
||||
return true; // not used
|
||||
}
|
||||
@Override protected void execute () {
|
||||
_channelMan.deliverSpeak(_channel, _message, _bodyIds);
|
||||
}
|
||||
protected int[] _bodyIds;
|
||||
}
|
||||
|
||||
/** Contains metadata for a particular channel. */
|
||||
protected static class ChannelInfo
|
||||
{
|
||||
/** The channel this info is for. */
|
||||
public ChatChannel channel;
|
||||
|
||||
/** The body ids of the participants of this channel. */
|
||||
public Set<Integer> participants;
|
||||
|
||||
/** The time at which a message was last dispatched on this channel. */
|
||||
public long lastMessage;
|
||||
}
|
||||
|
||||
/** Contains pending messages for all channels currently being resolved. */
|
||||
protected Map<ChatChannel,List<UserMessage>> _resolving = Maps.newHashMap();
|
||||
|
||||
/** A map of resolved channels to metadata records. */
|
||||
protected Map<ChatChannel,ChannelInfo> _channels = Maps.newHashMap();
|
||||
|
||||
/** Provides peer services. */
|
||||
@Inject protected CrowdPeerManager _peerMan;
|
||||
|
||||
/** The period on which we check for idle channels. */
|
||||
protected static final long IDLE_CHANNEL_CHECK_PERIOD = 5 * 1000L;
|
||||
|
||||
/** The amount of idle time (in milliseconds) after which we close a channel. */
|
||||
protected static final long IDLE_CHANNEL_CLOSE_TIME = 5 * 60 * 1000L;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.ChatMarshaller;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link ChatProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from ChatService.java.")
|
||||
public class ChatDispatcher extends InvocationDispatcher<ChatMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public ChatDispatcher (ChatProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMarshaller createMarshaller ()
|
||||
{
|
||||
return new ChatMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case ChatMarshaller.AWAY:
|
||||
((ChatProvider)provider).away(
|
||||
source, (String)args[0]
|
||||
);
|
||||
return;
|
||||
|
||||
case ChatMarshaller.BROADCAST:
|
||||
((ChatProvider)provider).broadcast(
|
||||
source, (String)args[0], (InvocationService.InvocationListener)args[1]
|
||||
);
|
||||
return;
|
||||
|
||||
case ChatMarshaller.TELL:
|
||||
((ChatProvider)provider).tell(
|
||||
source, (Name)args[0], (String)args[1], (ChatService.TellListener)args[2]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,302 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.util.MessageBundle;
|
||||
import com.threerings.util.Name;
|
||||
import com.threerings.util.TimeUtil;
|
||||
|
||||
import com.threerings.presents.client.InvocationService.InvocationListener;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService.TellListener;
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.chat.data.SystemMessage;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdCodes;
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.server.BodyLocal;
|
||||
import com.threerings.crowd.server.BodyLocator;
|
||||
import com.threerings.crowd.server.PlaceRegistry;
|
||||
|
||||
/**
|
||||
* The chat provider handles the server side of the chat-related invocation services.
|
||||
*/
|
||||
@Singleton
|
||||
public class ChatProvider
|
||||
implements InvocationProvider
|
||||
{
|
||||
/** Interface to allow an auto response to a tell message. */
|
||||
public static interface TellAutoResponder
|
||||
{
|
||||
/**
|
||||
* Called following the delivery of <code>message</code> from <code>teller</code> to
|
||||
* <code>tellee</code>.
|
||||
*/
|
||||
void sentTell (BodyObject teller, BodyObject tellee, String message);
|
||||
}
|
||||
|
||||
/** Used to forward certain types of chat messages between servers in a multi-server setup. */
|
||||
public static interface ChatForwarder
|
||||
{
|
||||
/**
|
||||
* Requests that the supplied tell message be delivered to the appropriate destination.
|
||||
*
|
||||
* @return true if the tell was delivered, false otherwise.
|
||||
*/
|
||||
boolean forwardTell (UserMessage message, Name target, TellListener listener);
|
||||
|
||||
/**
|
||||
* Requests that the supplied broadcast message be delivered on other servers.
|
||||
*/
|
||||
void forwardBroadcast (Name from, byte levelOrMode, String bundle, String msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and registers this chat provider.
|
||||
*/
|
||||
@Inject public ChatProvider (InvocationManager invmgr)
|
||||
{
|
||||
// register a chat provider with the invocation manager
|
||||
invmgr.registerDispatcher(new ChatDispatcher(this), CrowdCodes.CROWD_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an object to which all broadcasts should be sent, rather than iterating over the place
|
||||
* objects and sending to each of them.
|
||||
*
|
||||
* @param object an object to send all broadcasts, or null to send to each place object
|
||||
* instead.
|
||||
*/
|
||||
public void setAlternateBroadcastObject (DObject object)
|
||||
{
|
||||
_broadcastObject = object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the auto tell responder for the chat provider. Only one auto responder is allowed.
|
||||
* <em>Note:</em> this only works for same-server tells. If the tell is forwarded to another
|
||||
* server, no auto-response opportunity is provided (because we never have both body objects in
|
||||
* the same place).
|
||||
*/
|
||||
public void setTellAutoResponder (TellAutoResponder autoRespond)
|
||||
{
|
||||
_autoRespond = autoRespond;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the chat forwarder. This is used by the Crowd peer services to forward messages
|
||||
* between servers in a multi-server cluster.
|
||||
*/
|
||||
public void setChatForwarder (ChatForwarder forwarder)
|
||||
{
|
||||
_chatForwarder = forwarder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a {@link ChatService#tell} request.
|
||||
*/
|
||||
public void tell (ClientObject caller, Name target, String message, TellListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
// ensure that the caller has normal chat privileges
|
||||
InvocationException.requireAccess(caller, ChatCodes.CHAT_ACCESS);
|
||||
|
||||
// deliver the tell message to the target
|
||||
BodyObject source = (BodyObject)caller;
|
||||
deliverTell(createTellMessage(source, message), target, listener);
|
||||
|
||||
// inform the auto-responder if needed
|
||||
BodyObject targobj;
|
||||
if (_autoRespond != null && (targobj = _locator.lookupBody(target)) != null) {
|
||||
_autoRespond.sentTell(source, targobj, message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a {@link ChatService#broadcast} request.
|
||||
*/
|
||||
public void broadcast (ClientObject caller, String message, InvocationListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
// make sure the requesting user has broadcast privileges
|
||||
InvocationException.requireAccess(caller, ChatCodes.BROADCAST_ACCESS);
|
||||
BodyObject body = (BodyObject)caller;
|
||||
broadcast(body.getVisibleName(), null, message, false, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a {@link ChatService#away} request.
|
||||
*/
|
||||
public void away (ClientObject caller, String message)
|
||||
{
|
||||
BodyObject body = (BodyObject)caller;
|
||||
// we modify this field via an invocation service request because a body object is not
|
||||
// modifiable by the client
|
||||
body.setAwayMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the specified message to all place objects in the system.
|
||||
*
|
||||
* @param from the user the broadcast is from, or null to send the message as a system message.
|
||||
* @param bundle the bundle, or null if the message needs no translation.
|
||||
* @param msg the content of the message to broadcast.
|
||||
* @param attention if true, the message is sent as ATTENTION level, otherwise as INFO. Ignored
|
||||
* if from is non-null.
|
||||
* @param forward if true, forward this broadcast on to any registered chat forwarder, if
|
||||
* false, deliver it only locally on this server.
|
||||
*/
|
||||
public void broadcast (Name from, String bundle, String msg, boolean attention, boolean forward)
|
||||
{
|
||||
byte levelOrMode = (from != null) ? ChatCodes.BROADCAST_MODE
|
||||
: (attention ? SystemMessage.ATTENTION : SystemMessage.INFO);
|
||||
broadcast(from, levelOrMode, bundle, msg, forward);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast with support for a customizable level or mode.
|
||||
* @param levelOrMode if from is null, it's an attentionLevel, else it's a mode code.
|
||||
*/
|
||||
public void broadcast (Name from, byte levelOrMode, String bundle, String msg, boolean forward)
|
||||
{
|
||||
if (_broadcastObject != null) {
|
||||
broadcastTo(_broadcastObject, from, levelOrMode, bundle, msg);
|
||||
|
||||
} else {
|
||||
for (Iterator<PlaceObject> iter = _plreg.enumeratePlaces(); iter.hasNext(); ) {
|
||||
PlaceObject plobj = iter.next();
|
||||
if (plobj.shouldBroadcast()) {
|
||||
broadcastTo(plobj, from, levelOrMode, bundle, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forward && _chatForwarder != null) {
|
||||
_chatForwarder.forwardBroadcast(from, levelOrMode, bundle, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers a tell message to the specified target and notifies the supplied listener of the
|
||||
* result. It is assumed that the teller has already been permissions checked.
|
||||
*/
|
||||
public void deliverTell (UserMessage message, Name target, TellListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
// make sure the target user is online
|
||||
BodyObject tobj = _locator.lookupBody(target);
|
||||
if (tobj == null) {
|
||||
// if we have a forwarder configured, try forwarding the tell
|
||||
if (_chatForwarder != null && _chatForwarder.forwardTell(message, target, listener)) {
|
||||
return;
|
||||
}
|
||||
throw new InvocationException(ChatCodes.USER_NOT_ONLINE);
|
||||
}
|
||||
|
||||
if (tobj.status == OccupantInfo.DISCONNECTED) {
|
||||
String errmsg = MessageBundle.compose(
|
||||
ChatCodes.USER_DISCONNECTED, TimeUtil.getTimeOrderString(
|
||||
System.currentTimeMillis() - tobj.getLocal(BodyLocal.class).statusTime,
|
||||
TimeUtil.SECOND));
|
||||
throw new InvocationException(errmsg);
|
||||
}
|
||||
|
||||
// deliver a tell notification to the target player
|
||||
deliverTell(tobj, message);
|
||||
|
||||
// let the teller know it went ok
|
||||
long idle = 0L;
|
||||
if (tobj.status == OccupantInfo.IDLE) {
|
||||
idle = System.currentTimeMillis() - tobj.getLocal(BodyLocal.class).statusTime;
|
||||
}
|
||||
String awayMessage = null;
|
||||
if (!StringUtil.isBlank(tobj.awayMessage)) {
|
||||
awayMessage = tobj.awayMessage;
|
||||
}
|
||||
listener.tellSucceeded(idle, awayMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers a tell notification to the specified target player. It is assumed that the message
|
||||
* is coming from some server entity and need not be permissions checked or notified of the
|
||||
* result.
|
||||
*/
|
||||
public void deliverTell (BodyObject target, UserMessage message)
|
||||
{
|
||||
SpeakUtil.sendMessage(target, message);
|
||||
|
||||
// note that the teller "heard" what they said
|
||||
SpeakUtil.noteMessage(message.speaker, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to create a {@link UserMessage} for the supplied sender.
|
||||
*/
|
||||
protected UserMessage createTellMessage (BodyObject source, String message)
|
||||
{
|
||||
return new UserMessage(source.getVisibleName(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct a broadcast to the specified object.
|
||||
*/
|
||||
protected void broadcastTo (
|
||||
DObject object, Name from, byte levelOrMode, String bundle, String msg)
|
||||
{
|
||||
if (from == null) {
|
||||
SpeakUtil.sendSystem(object, bundle, msg, levelOrMode /* level */);
|
||||
|
||||
} else {
|
||||
SpeakUtil.sendSpeak(object, from, bundle, msg, levelOrMode /* mode */);
|
||||
}
|
||||
}
|
||||
|
||||
/** Provides access to place managers. */
|
||||
@Inject protected PlaceRegistry _plreg;
|
||||
|
||||
/** Used to look up body objects by name. */
|
||||
@Inject protected BodyLocator _locator;
|
||||
|
||||
/** Generates auto-responses to tells. May be null. */
|
||||
protected TellAutoResponder _autoRespond;
|
||||
|
||||
/** Forwards chat between servers. May be null. */
|
||||
protected ChatForwarder _chatForwarder;
|
||||
|
||||
/** An alternative object to which broadcasts should be sent. */
|
||||
protected DObject _broadcastObject;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.data.SpeakMarshaller;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link SpeakProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from SpeakService.java.")
|
||||
public class SpeakDispatcher extends InvocationDispatcher<SpeakMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public SpeakDispatcher (SpeakProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeakMarshaller createMarshaller ()
|
||||
{
|
||||
return new SpeakMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case SpeakMarshaller.SPEAK:
|
||||
((SpeakProvider)provider).speak(
|
||||
source, (String)args[0], ((Byte)args[1]).byteValue()
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import com.threerings.util.MessageManager;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
|
||||
import com.threerings.crowd.chat.client.SpeakService;
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Wires up the {@link SpeakService} to a particular distributed object. A server entity can make
|
||||
* "speech" available among the subscribers of a particular distributed object by constructing a
|
||||
* speak handler and registering it with the {@link InvocationManager}, then placing the resulting
|
||||
* marshaller into the distributed object in question so that subscribers to that object can use it
|
||||
* to generate "speak" requests on that object.
|
||||
*/
|
||||
public class SpeakHandler
|
||||
implements SpeakProvider
|
||||
{
|
||||
/**
|
||||
* Used to prevent abitrary users from issuing speak requests.
|
||||
*/
|
||||
public static interface SpeakerValidator
|
||||
{
|
||||
/**
|
||||
* Should return true if the supplied speaker is allowed to speak via the speak provider
|
||||
* with which this validator was registered.
|
||||
*/
|
||||
boolean isValidSpeaker (DObject speakObj, ClientObject speaker, byte mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a handler that will provide speech on the supplied distributed object.
|
||||
*
|
||||
* @param speakObj the object for which speech requests will be processed.
|
||||
* @param validator an optional validator that can be used to prevent arbitrary users from
|
||||
* using the speech services on this object.
|
||||
*/
|
||||
public SpeakHandler (DObject speakObj, SpeakerValidator validator)
|
||||
{
|
||||
_speakObj = speakObj;
|
||||
_validator = validator;
|
||||
}
|
||||
|
||||
// from interface SpeakProvider
|
||||
public void speak (ClientObject caller, String message, byte mode)
|
||||
{
|
||||
// ensure that the caller has normal chat privileges
|
||||
BodyObject source = (BodyObject)caller;
|
||||
String errmsg = source.checkAccess(ChatCodes.CHAT_ACCESS, null);
|
||||
if (errmsg != null) {
|
||||
// we normally don't listen for responses to speak messages so we can't just throw an
|
||||
// InvocationException we have to specifically communicate the error to the user
|
||||
SpeakUtil.sendFeedback(source, MessageManager.GLOBAL_BUNDLE, errmsg);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: broadcast should be handled more like a system message rather than as a mode for a
|
||||
// user message so that we don't have to do this validation here. Or not.
|
||||
|
||||
// ensure that the speaker is valid
|
||||
if ((mode == ChatCodes.BROADCAST_MODE) ||
|
||||
(_validator != null && !_validator.isValidSpeaker(_speakObj, caller, mode))) {
|
||||
log.warning("Refusing invalid speak request", "caller", caller.who(),
|
||||
"speakObj", _speakObj.which(), "message", message, "mode", mode);
|
||||
|
||||
} else {
|
||||
// issue the speak message on our speak object
|
||||
SpeakUtil.sendSpeak(_speakObj, source.getVisibleName(), null, message, mode);
|
||||
}
|
||||
}
|
||||
|
||||
/** Our speech object. */
|
||||
protected DObject _speakObj;
|
||||
|
||||
/** The entity that will validate our speakers. */
|
||||
protected SpeakerValidator _validator;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.SpeakService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link SpeakService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from SpeakService.java.")
|
||||
public interface SpeakProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link SpeakService#speak} request.
|
||||
*/
|
||||
void speak (ClientObject caller, String arg1, byte arg2);
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.chat.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.chat.data.ChatMessage;
|
||||
import com.threerings.crowd.chat.data.KeepNoHistory;
|
||||
import com.threerings.crowd.chat.data.SpeakObject;
|
||||
import com.threerings.crowd.chat.data.SystemMessage;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.io.Streamable;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Provides the back-end of the chat speaking facilities.
|
||||
*/
|
||||
public class SpeakUtil
|
||||
{
|
||||
/**
|
||||
* An interface used to notify external systems whenever a chat message is spoken by one user
|
||||
* and heard by another.
|
||||
*/
|
||||
public static interface MessageObserver
|
||||
{
|
||||
/**
|
||||
* Called for each player that hears a particular chat message.
|
||||
*/
|
||||
void messageDelivered (Name hearer, UserMessage message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recorded parcel of chat for historical purposes, maintained by
|
||||
* {@link #recordToChatHistory(ChatChannel, UserMessage, Name...)},
|
||||
* {@link #getChatHistory(Name)}, and {@link #clearHistory(Name)}.
|
||||
*/
|
||||
public static class ChatHistoryEntry
|
||||
implements Streamable
|
||||
{
|
||||
/** The channel on which the message was sent, of null if the channel manager was not
|
||||
* used. */
|
||||
public ChatChannel channel;
|
||||
|
||||
/** The message sent. */
|
||||
public ChatMessage message;
|
||||
|
||||
/** For deserialization. */
|
||||
public ChatHistoryEntry ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new history entry.
|
||||
*/
|
||||
public ChatHistoryEntry (ChatChannel channel, ChatMessage message)
|
||||
{
|
||||
this.channel = channel;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a {@link MessageObserver} to be notified whenever a user-originated chat message
|
||||
* is heard by another user.
|
||||
*/
|
||||
public static void registerMessageObserver (MessageObserver obs)
|
||||
{
|
||||
_messageObs.add(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a registration made previously with {@link #registerMessageObserver}.
|
||||
*/
|
||||
public static void removeMessageObserver (MessageObserver obs)
|
||||
{
|
||||
_messageObs.remove(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a speak notification to the specified place object originating with the specified
|
||||
* speaker (the speaker optionally being a server entity that wishes to fake a "speak" message)
|
||||
* and with the supplied message content.
|
||||
*
|
||||
* @param speakObj the object on which to generate the speak message.
|
||||
* @param speaker the username of the user that generated the message (or some special speaker
|
||||
* name for server messages).
|
||||
* @param bundle null when the message originates from a real human, the bundle identifier that
|
||||
* will be used by the client to translate the message text when the message originates from a
|
||||
* server entity "faking" a chat message.
|
||||
* @param message the text of the speak message.
|
||||
*/
|
||||
public static void sendSpeak (DObject speakObj, Name speaker, String bundle, String message)
|
||||
{
|
||||
sendSpeak(speakObj, speaker, bundle, message, ChatCodes.DEFAULT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a speak notification to the specified place object originating with the specified
|
||||
* speaker (the speaker optionally being a server entity that wishes to fake a "speak" message)
|
||||
* and with the supplied message content.
|
||||
*
|
||||
* @param speakObj the object on which to generate the speak message.
|
||||
* @param speaker the username of the user that generated the message (or some special speaker
|
||||
* name for server messages).
|
||||
* @param bundle null when the message originates from a real human, the bundle identifier that
|
||||
* will be used by the client to translate the message text when the message originates from a
|
||||
* server entity "faking" a chat message.
|
||||
* @param message the text of the speak message.
|
||||
* @param mode the mode of the message, see {@link ChatCodes#DEFAULT_MODE}.
|
||||
*/
|
||||
public static void sendSpeak (DObject speakObj, Name speaker, String bundle, String message,
|
||||
byte mode)
|
||||
{
|
||||
sendMessage(speakObj, new UserMessage(speaker, bundle, message, mode));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system INFO message notification to the specified object with the supplied message
|
||||
* content. A system message is one that will be rendered where the speak messages are
|
||||
* rendered, but in a way that makes it clear that it is a message from the server.
|
||||
*
|
||||
* Info messages are sent when something happens that was neither directly triggered by the
|
||||
* user, nor requires direct action.
|
||||
*
|
||||
* @param speakObj the object on which to deliver the message.
|
||||
* @param bundle the name of the localization bundle that should be used to translate this
|
||||
* system message prior to displaying it to the client.
|
||||
* @param message the text of the message.
|
||||
*/
|
||||
public static void sendInfo (DObject speakObj, String bundle, String message)
|
||||
{
|
||||
sendSystem(speakObj, bundle, message, SystemMessage.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system FEEDBACK message notification to the specified object with the supplied
|
||||
* message content. A system message is one that will be rendered where the speak messages are
|
||||
* rendered, but in a way that makes it clear that it is a message from the server.
|
||||
*
|
||||
* Feedback messages are sent in direct response to a user action, usually to indicate success
|
||||
* or failure of the user's action.
|
||||
*
|
||||
* @param speakObj the object on which to deliver the message.
|
||||
* @param bundle the name of the localization bundle that should be used to translate this
|
||||
* system message prior to displaying it to the client.
|
||||
* @param message the text of the message.
|
||||
*/
|
||||
public static void sendFeedback (DObject speakObj, String bundle, String message)
|
||||
{
|
||||
sendSystem(speakObj, bundle, message, SystemMessage.FEEDBACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a system ATTENTION message notification to the specified object with the supplied
|
||||
* message content. A system message is one that will be rendered where the speak messages are
|
||||
* rendered, but in a way that makes it clear that it is a message from the server.
|
||||
*
|
||||
* Attention messages are sent when something requires user action that did not result from
|
||||
* direct action by the user.
|
||||
*
|
||||
* @param speakObj the object on which to deliver the message.
|
||||
* @param bundle the name of the localization bundle that should be used to translate this
|
||||
* system message prior to displaying it to the client.
|
||||
* @param message the text of the message.
|
||||
*/
|
||||
public static void sendAttention (DObject speakObj, String bundle, String message)
|
||||
{
|
||||
sendSystem(speakObj, bundle, message, SystemMessage.ATTENTION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the specified message on the specified object.
|
||||
*/
|
||||
public static void sendMessage (DObject speakObj, ChatMessage msg)
|
||||
{
|
||||
if (speakObj == null) {
|
||||
log.warning("Dropping speak message, no speak obj '" + msg + "'.", new Exception());
|
||||
return;
|
||||
}
|
||||
|
||||
// post the message to the relevant object
|
||||
speakObj.postMessage(ChatCodes.CHAT_NOTIFICATION, new Object[] { msg });
|
||||
|
||||
// if this is a user message; add it to the heard history of all users that can "hear" it
|
||||
if (!(msg instanceof UserMessage)) {
|
||||
return;
|
||||
|
||||
} else if (speakObj instanceof SpeakObject) {
|
||||
_messageMapper.omgr = (RootDObjectManager)speakObj.getManager();
|
||||
_messageMapper.message = (UserMessage)msg;
|
||||
((SpeakObject)speakObj).applyToListeners(_messageMapper);
|
||||
_messageMapper.omgr = null;
|
||||
_messageMapper.message = null;
|
||||
|
||||
} else {
|
||||
log.info("Unable to note listeners", "dclass", speakObj.getClass(), "msg", msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link ChatMessage} objects to which this user has been privy in the
|
||||
* recent past. If the given name implements {@link KeepNoHistory}, null is returned.
|
||||
*/
|
||||
public static List<ChatHistoryEntry> getChatHistory (Name username)
|
||||
{
|
||||
List<ChatHistoryEntry> history = getHistoryList(username);
|
||||
if (history != null) {
|
||||
pruneHistory(System.currentTimeMillis(), history);
|
||||
}
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to clear the chat history for the specified user.
|
||||
*/
|
||||
public static void clearHistory (Name username)
|
||||
{
|
||||
// Log.info("Clearing history for " + username + ".");
|
||||
_histories.remove(username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Records the specified channel and message to the specified users' chat histories. If {@link
|
||||
* ChatMessage#timestamp} is not already filled in, it will be.
|
||||
*/
|
||||
public static void recordToChatHistory (
|
||||
ChatChannel channel, UserMessage msg, Name... usernames)
|
||||
{
|
||||
// fill in the message's time stamp if necessary
|
||||
if (msg.timestamp == 0L) {
|
||||
msg.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
for (Name username : usernames) {
|
||||
// add the message to this user's chat history
|
||||
List<ChatHistoryEntry> history = getHistoryList(username);
|
||||
if (history == null) {
|
||||
continue;
|
||||
}
|
||||
history.add(new ChatHistoryEntry(channel, msg));
|
||||
|
||||
// if the history is big enough, potentially prune it (we always prune when asked for
|
||||
// the history, so this is just to balance memory usage with CPU expense)
|
||||
if (history.size() > 15) {
|
||||
pruneHistory(msg.timestamp, history);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notes that the specified user was privy to the specified message. If {@link
|
||||
* ChatMessage#timestamp} is not already filled in, it will be.
|
||||
*/
|
||||
protected static void noteMessage (Name username, UserMessage msg)
|
||||
{
|
||||
// fill in the message's time stamp if necessary
|
||||
if (msg.timestamp == 0L) {
|
||||
msg.timestamp = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
recordToChatHistory(null, msg, username);
|
||||
// Log.info("Noted that " + username + " heard " + msg + ".");
|
||||
|
||||
// notify any message observers
|
||||
_messageOp.init(username, msg);
|
||||
_messageObs.apply(_messageOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the specified system message on the specified dobj.
|
||||
*/
|
||||
protected static void sendSystem (DObject speakObj, String bundle, String message, byte level)
|
||||
{
|
||||
sendMessage(speakObj, new SystemMessage(message, bundle, level));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this user's chat history, creating one if necessary. If the given name implements
|
||||
* {@link KeepNoHistory}, null is returned.
|
||||
*/
|
||||
protected static List<ChatHistoryEntry> getHistoryList (Name username)
|
||||
{
|
||||
if (username instanceof KeepNoHistory) {
|
||||
return null;
|
||||
}
|
||||
List<ChatHistoryEntry> history = _histories.get(username);
|
||||
if (history == null) {
|
||||
_histories.put(username, history = Lists.newArrayList());
|
||||
}
|
||||
return history;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prunes all messages from this history which are expired.
|
||||
*/
|
||||
protected static void pruneHistory (long now, List<ChatHistoryEntry> history)
|
||||
{
|
||||
int prunepos = 0;
|
||||
for (int ll = history.size(); prunepos < ll; prunepos++) {
|
||||
ChatHistoryEntry entry = history.get(prunepos);
|
||||
if (now - entry.message.timestamp < HISTORY_EXPIRATION) {
|
||||
break; // stop when we get to the first valid message
|
||||
}
|
||||
}
|
||||
history.subList(0, prunepos).clear();
|
||||
}
|
||||
|
||||
/** Used to note the recipients of a chat message. */
|
||||
protected static class MessageMapper implements SpeakObject.ListenerOp
|
||||
{
|
||||
public RootDObjectManager omgr;
|
||||
public UserMessage message;
|
||||
|
||||
public void apply (int bodyOid) {
|
||||
DObject dobj = omgr.getObject(bodyOid);
|
||||
if (dobj != null && dobj instanceof BodyObject) {
|
||||
noteMessage(((BodyObject)dobj).getVisibleName(), message);
|
||||
}
|
||||
}
|
||||
|
||||
public void apply (Name username) {
|
||||
noteMessage(username, message);
|
||||
}
|
||||
}
|
||||
|
||||
/** Used to notify our {@link MessageObserver}s. */
|
||||
protected static class MessageObserverOp
|
||||
implements ObserverList.ObserverOp<MessageObserver>
|
||||
{
|
||||
public void init (Name hearer, UserMessage message) {
|
||||
_hearer = hearer;
|
||||
_message = message;
|
||||
}
|
||||
|
||||
public boolean apply (MessageObserver observer) {
|
||||
observer.messageDelivered(_hearer, _message);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Name _hearer;
|
||||
protected UserMessage _message;
|
||||
}
|
||||
|
||||
/** Recent chat history for the server. */
|
||||
protected static Map<Name, List<ChatHistoryEntry>> _histories = Maps.newHashMap();
|
||||
|
||||
/** Used to note the recipients of a chat message. */
|
||||
protected static MessageMapper _messageMapper = new MessageMapper();
|
||||
|
||||
/** A list of {@link MessageObserver}s. */
|
||||
protected static ObserverList<MessageObserver> _messageObs = ObserverList.newFastUnsafe();
|
||||
|
||||
/** Used to notify our {@link MessageObserver}s. */
|
||||
protected static MessageObserverOp _messageOp = new MessageObserverOp();
|
||||
|
||||
/** The amount of time before chat history becomes... history. */
|
||||
protected static final long HISTORY_EXPIRATION = 5L * 60L * 1000L;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
/**
|
||||
* The client side of the body-related invocation services.
|
||||
*/
|
||||
public interface BodyService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Requests to set the idle state of the client to the specified
|
||||
* value.
|
||||
*/
|
||||
void setIdle (Client client, boolean idle);
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* The location adapter makes life easier for a class that really only
|
||||
* cares about one or two of the location observer callbacks and doesn't
|
||||
* want to provide empty implementations of the others. One can either
|
||||
* extend location adapter, or create an anonymous instance that overrides
|
||||
* the desired callback(s). Note that the location adapter defaults to
|
||||
* ratifying any location change.
|
||||
*
|
||||
* @see LocationObserver
|
||||
*/
|
||||
public class LocationAdapter implements LocationObserver
|
||||
{
|
||||
// documentation inherited
|
||||
public boolean locationMayChange (int placeId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void locationDidChange (PlaceObject place)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void locationChangeFailed (int placeId, String reason)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.presents.client.InvocationDecoder;
|
||||
|
||||
/**
|
||||
* Dispatches calls to a {@link LocationReceiver} instance.
|
||||
*/
|
||||
public class LocationDecoder extends InvocationDecoder
|
||||
{
|
||||
/** The generated hash code used to identify this receiver class. */
|
||||
public static final String RECEIVER_CODE = "58f2830e027f4f3377e100ef12332497";
|
||||
|
||||
/** The method id used to dispatch {@link LocationReceiver#forcedMove}
|
||||
* notifications. */
|
||||
public static final int FORCED_MOVE = 1;
|
||||
|
||||
/**
|
||||
* Creates a decoder that may be registered to dispatch invocation
|
||||
* service notifications to the specified receiver.
|
||||
*/
|
||||
public LocationDecoder (LocationReceiver receiver)
|
||||
{
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getReceiverCode ()
|
||||
{
|
||||
return RECEIVER_CODE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchNotification (int methodId, Object[] args)
|
||||
{
|
||||
switch (methodId) {
|
||||
case FORCED_MOVE:
|
||||
((LocationReceiver)receiver).forcedMove(
|
||||
((Integer)args[0]).intValue()
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchNotification(methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,628 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.ResultListener;
|
||||
import com.samskivert.util.ObserverList.ObserverOp;
|
||||
|
||||
import com.threerings.presents.client.BasicDirector;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.ObjectAccessException;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.util.SafeSubscriber;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdCodes;
|
||||
import com.threerings.crowd.data.LocationCodes;
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* The location director provides a means by which entities on the client can request to move from
|
||||
* place to place and can be notified if other entities have caused the client to move to a new
|
||||
* place. It also provides a mechanism for ratifying a request to move to a new place before
|
||||
* actually issuing the request.
|
||||
*/
|
||||
public class LocationDirector extends BasicDirector
|
||||
implements LocationCodes, LocationReceiver
|
||||
{
|
||||
/**
|
||||
* Used to recover from a moveTo request that was accepted but resulted in a failed attempt to
|
||||
* fetch the place object to which we were moving.
|
||||
*/
|
||||
public static interface FailureHandler
|
||||
{
|
||||
/**
|
||||
* Should instruct the client to move to the last known working location (as well as clean
|
||||
* up after the failed moveTo request).
|
||||
*/
|
||||
void recoverFailedMove (int placeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a location director which will configure itself for operation using the supplied
|
||||
* context.
|
||||
*/
|
||||
public LocationDirector (CrowdContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
|
||||
// keep this around for later
|
||||
_ctx = ctx;
|
||||
|
||||
// register for location notifications
|
||||
_ctx.getClient().getInvocationDirector().registerReceiver(new LocationDecoder(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a location observer to the list. This observer will subsequently be notified of
|
||||
* potential, effected and failed location changes.
|
||||
*/
|
||||
public void addLocationObserver (LocationObserver observer)
|
||||
{
|
||||
_observers.add(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a location observer from the list.
|
||||
*/
|
||||
public void removeLocationObserver (LocationObserver observer)
|
||||
{
|
||||
_observers.remove(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the place object for the location we currently occupy or null if we're not currently
|
||||
* occupying any location.
|
||||
*/
|
||||
public PlaceObject getPlaceObject ()
|
||||
{
|
||||
return _plobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there is a pending move request.
|
||||
*/
|
||||
public boolean movePending ()
|
||||
{
|
||||
return (_pendingPlaceId > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that this client be moved to the specified place. A request will be made and when
|
||||
* the response is received, the location observers will be notified of success or failure.
|
||||
*
|
||||
* @return true if the move to request was issued, false if it was rejected by a location
|
||||
* observer or because we have another request outstanding.
|
||||
*/
|
||||
public boolean moveTo (int placeId)
|
||||
{
|
||||
// make sure the placeId is valid
|
||||
if (placeId < 0) {
|
||||
log.warning("Refusing moveTo(): invalid placeId " + placeId + ".");
|
||||
return false;
|
||||
}
|
||||
|
||||
// first check to see if our observers are happy with this move request
|
||||
if (!mayMoveTo(placeId, null)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// we need to call this both to mark that we're issuing a move request and to check to see
|
||||
// if the last issued request should be considered stale
|
||||
boolean refuse = checkRepeatMove();
|
||||
|
||||
// complain if we're over-writing a pending request
|
||||
if (_pendingPlaceId != -1) {
|
||||
// if the pending request has been outstanding more than a minute, go ahead and let
|
||||
// this new one through in an attempt to recover from dropped moveTo requests
|
||||
if (refuse) {
|
||||
log.warning("Refusing moveTo; We have a request outstanding",
|
||||
"ppid", _pendingPlaceId, "npid", placeId);
|
||||
return false;
|
||||
|
||||
} else {
|
||||
log.warning("Overriding stale moveTo request", "ppid", _pendingPlaceId,
|
||||
"npid", placeId);
|
||||
}
|
||||
}
|
||||
|
||||
// make a note of our pending place id
|
||||
_pendingPlaceId = placeId;
|
||||
|
||||
// issue a moveTo request
|
||||
log.info("Issuing moveTo(" + placeId + ").");
|
||||
_lservice.moveTo(_ctx.getClient(), placeId, new LocationService.MoveListener() {
|
||||
public void moveSucceeded (PlaceConfig config) {
|
||||
// handle the successful move
|
||||
didMoveTo(_pendingPlaceId, config);
|
||||
// and clear out the tracked pending oid
|
||||
_pendingPlaceId = -1;
|
||||
|
||||
handlePendingForcedMove();
|
||||
}
|
||||
public void requestFailed (String reason) {
|
||||
// clear out our pending request oid
|
||||
int placeId = _pendingPlaceId;
|
||||
_pendingPlaceId = -1;
|
||||
log.info("moveTo failed", "pid", placeId, "reason", reason);
|
||||
// let our observers know that something has gone horribly awry
|
||||
handleFailure(placeId, reason);
|
||||
handlePendingForcedMove();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests to move to the room that we last occupied, if such a room exists.
|
||||
*
|
||||
* @return true if we had a previous room and we requested to move to it, false if we had no
|
||||
* previous room.
|
||||
*/
|
||||
public boolean moveBack ()
|
||||
{
|
||||
if (_previousPlaceId == -1) {
|
||||
return false;
|
||||
|
||||
} else {
|
||||
moveTo(_previousPlaceId);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Issues a request to leave our current location.
|
||||
*
|
||||
* @return true if we were able to leave, false if we are in the middle of moving somewhere and
|
||||
* can't yet leave.
|
||||
*/
|
||||
public boolean leavePlace ()
|
||||
{
|
||||
if (_pendingPlaceId != -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_lservice.leavePlace(_ctx.getClient());
|
||||
didLeavePlace();
|
||||
|
||||
// let our observers know that we're no longer in a location
|
||||
_observers.apply(_didChangeOp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be called by cooperating directors that need to coopt the moving process to extend
|
||||
* it in some way or other. In such situations, they should call this method before moving to a
|
||||
* new location to check to be sure that all of the registered location observers are amenable
|
||||
* to a location change.
|
||||
*
|
||||
* @param placeId the place oid of our tentative new location.
|
||||
*
|
||||
* @return true if everyone is happy with the move, false if it was vetoed by one of the
|
||||
* location observers.
|
||||
*/
|
||||
public boolean mayMoveTo (final int placeId, ResultListener<PlaceConfig> rl)
|
||||
{
|
||||
final boolean[] vetoed = new boolean[1];
|
||||
_observers.apply(new ObserverOp<LocationObserver>() {
|
||||
public boolean apply (LocationObserver obs) {
|
||||
vetoed[0] = (vetoed[0] || !obs.locationMayChange(placeId));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// if we're actually going somewhere, let the controller know that we might be leaving
|
||||
mayLeavePlace();
|
||||
|
||||
// if we have a result listener, let it know if we failed or keep it for later if we're
|
||||
// still going
|
||||
if (rl != null) {
|
||||
if (vetoed[0]) {
|
||||
rl.requestFailed(new MoveVetoedException());
|
||||
} else {
|
||||
_moveListener = rl;
|
||||
}
|
||||
}
|
||||
// and return the result
|
||||
return !vetoed[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform our controller that we may be leaving the current place.
|
||||
*/
|
||||
protected void mayLeavePlace ()
|
||||
{
|
||||
if (_controller != null) {
|
||||
try {
|
||||
_controller.mayLeavePlace(_plobj);
|
||||
} catch (Exception e) {
|
||||
log.warning("Place controller choked in mayLeavePlace", "plobj", _plobj, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be called by cooperating directors that need to coopt the moving process to extend
|
||||
* it in some way or other. In such situations, they will be responsible for receiving the
|
||||
* successful move response and they should let the location director know that the move has
|
||||
* been effected.
|
||||
*
|
||||
* @param placeId the place oid of our new location.
|
||||
* @param config the configuration information for the new place.
|
||||
*/
|
||||
public void didMoveTo (int placeId, PlaceConfig config)
|
||||
{
|
||||
if (_moveListener != null) {
|
||||
_moveListener.requestCompleted(config);
|
||||
_moveListener = null;
|
||||
}
|
||||
|
||||
// keep track of our previous place id
|
||||
_previousPlaceId = _placeId;
|
||||
|
||||
// clear out our last request time
|
||||
_lastRequestTime = 0;
|
||||
|
||||
// do some cleaning up in case we were previously in a place
|
||||
didLeavePlace();
|
||||
|
||||
// make a note that we're now mostly in the new location
|
||||
_placeId = placeId;
|
||||
|
||||
// start up a new place controller to manage the new place
|
||||
try {
|
||||
_controller = createController(config);
|
||||
if (_controller == null) {
|
||||
log.warning("Place config returned null controller", "config", config);
|
||||
return;
|
||||
}
|
||||
_controller.init(_ctx, config);
|
||||
|
||||
// subscribe to our new place object to complete the move
|
||||
_subber = new SafeSubscriber<PlaceObject>(_placeId, new Subscriber<PlaceObject>() {
|
||||
public void objectAvailable (PlaceObject object) {
|
||||
gotPlaceObject(object);
|
||||
}
|
||||
public void requestFailed (int oid, ObjectAccessException cause) {
|
||||
// aiya! we were unable to fetch our new place object; something is badly wrong
|
||||
log.warning("Aiya! Unable to fetch place object for new location", "plid", oid,
|
||||
"reason", cause);
|
||||
// clear out our half initialized place info
|
||||
int placeId = _placeId;
|
||||
_placeId = -1;
|
||||
// let the kids know shit be fucked
|
||||
handleFailure(placeId, "m.unable_to_fetch_place_object");
|
||||
}
|
||||
});
|
||||
_subber.subscribe(_ctx.getDObjectManager());
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Failed to create place controller", "config", config, e);
|
||||
handleFailure(_placeId, LocationCodes.E_INTERNAL_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we're leaving our current location. Informs the location's controller that we're
|
||||
* departing, unsubscribes from the location's place object, and clears out our internal place
|
||||
* information.
|
||||
*/
|
||||
public void didLeavePlace ()
|
||||
{
|
||||
// unsubscribe from our old place object
|
||||
if (_subber != null) {
|
||||
_subber.unsubscribe(_ctx.getDObjectManager());
|
||||
_subber = null;
|
||||
}
|
||||
|
||||
// let the old controller know that things are going away
|
||||
if (_plobj != null && _controller != null) {
|
||||
try {
|
||||
_controller.didLeavePlace(_plobj);
|
||||
} catch (Exception e) {
|
||||
log.warning("Place controller choked in didLeavePlace", "plobj", _plobj, e);
|
||||
}
|
||||
}
|
||||
|
||||
// and clear out other bits
|
||||
_plobj = null;
|
||||
_controller = null;
|
||||
_placeId = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be called by cooperating directors that need to coopt the moving process to extend
|
||||
* it in some way or other. If the coopted move request fails, this failure can be propagated
|
||||
* to the location observers if appropriate.
|
||||
*
|
||||
* @param placeId the place oid to which we failed to move.
|
||||
* @param reason the reason code given for failure.
|
||||
*/
|
||||
public void failedToMoveTo (int placeId, String reason)
|
||||
{
|
||||
if (_moveListener != null) {
|
||||
_moveListener.requestFailed(new MoveFailedException(reason));
|
||||
_moveListener = null;
|
||||
}
|
||||
|
||||
// clear out our last request time
|
||||
_lastRequestTime = 0;
|
||||
|
||||
// let our observers know what's up
|
||||
handleFailure(placeId, reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to test and set a time stamp that we use to determine if a pending moveTo request is
|
||||
* stale.
|
||||
*/
|
||||
public boolean checkRepeatMove ()
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
if (now - _lastRequestTime < STALE_REQUEST_DURATION) {
|
||||
return true;
|
||||
|
||||
} else {
|
||||
_lastRequestTime = now;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
super.clientDidLogon(client);
|
||||
|
||||
// subscribe to our body object
|
||||
Subscriber<BodyObject> sub = new Subscriber<BodyObject>() {
|
||||
public void objectAvailable (BodyObject object) {
|
||||
gotBodyObject(object);
|
||||
}
|
||||
public void requestFailed (int oid, ObjectAccessException cause) {
|
||||
log.warning("Location director unable to fetch body object; all has gone " +
|
||||
"horribly wrong", "cause", cause);
|
||||
}
|
||||
};
|
||||
int cloid = client.getClientOid();
|
||||
client.getDObjectManager().subscribeToObject(cloid, sub);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clientDidLogoff (Client client)
|
||||
{
|
||||
super.clientDidLogoff(client);
|
||||
|
||||
// clear ourselves out and inform observers of our departure
|
||||
mayLeavePlace();
|
||||
didLeavePlace();
|
||||
|
||||
// let our observers know that we're no longer in a location
|
||||
_observers.apply(_didChangeOp);
|
||||
|
||||
// clear out everything else (it's possible that we were logged off in the middle of a
|
||||
// change location request)
|
||||
_pendingPlaceId = -1;
|
||||
_pendingForcedMoves.clear();
|
||||
_previousPlaceId = -1;
|
||||
_lastRequestTime = 0L;
|
||||
_lservice = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerServices (Client client)
|
||||
{
|
||||
client.addServiceGroup(CrowdCodes.CROWD_GROUP);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fetchServices (Client client)
|
||||
{
|
||||
// obtain our service handle
|
||||
_lservice = client.requireService(LocationService.class);
|
||||
}
|
||||
|
||||
protected void gotPlaceObject (PlaceObject object)
|
||||
{
|
||||
// yay, we have our new place object
|
||||
_plobj = object;
|
||||
|
||||
// fill in our manager caller
|
||||
_plobj.initManagerCaller(_ctx.getClient().getDObjectManager());
|
||||
|
||||
// let the place controller know that we're ready to roll
|
||||
if (_controller != null) {
|
||||
try {
|
||||
_controller.willEnterPlace(_plobj);
|
||||
} catch (Exception e) {
|
||||
log.warning("Controller choked in willEnterPlace", "place", _plobj, e);
|
||||
}
|
||||
}
|
||||
|
||||
// let our observers know that all is well on the western front
|
||||
_observers.apply(_didChangeOp);
|
||||
}
|
||||
|
||||
protected void gotBodyObject (BodyObject clobj)
|
||||
{
|
||||
// TODO? check to see if we are already in a location, in which case we'll want to be going
|
||||
// there straight away
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void forcedMove (final int placeId)
|
||||
{
|
||||
// if we're in the middle of a move, we can't abort it or we will screw everything up, so
|
||||
// just finish up what we're doing and assume that the repeated move request was the
|
||||
// spurious one as it would be in the case of lag causing rapid-fire repeat requests
|
||||
if (movePending()) {
|
||||
if (_pendingPlaceId == placeId) {
|
||||
log.info("Dropping forced move because we have a move pending",
|
||||
"pendId", _pendingPlaceId, "reqId", placeId);
|
||||
} else {
|
||||
log.info("Delaying forced move because we have a move pending",
|
||||
"pendId", _pendingPlaceId, "reqId", placeId);
|
||||
addPendingForcedMove(new Runnable() {
|
||||
public void run () {
|
||||
forcedMove(placeId);
|
||||
}
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Moving at request of server", "placeId", placeId);
|
||||
// clear out our old place information
|
||||
mayLeavePlace();
|
||||
didLeavePlace();
|
||||
// move to the new place
|
||||
moveTo(placeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the failure handler which will recover from place object fetching failures. In the
|
||||
* event that we are unable to fetch our place object after making a successful moveTo request,
|
||||
* we attempt to rectify the failure by moving back to the last known working location. Because
|
||||
* entites that cooperate with the location director may need to become involved in this
|
||||
* failure recovery, we provide this interface whereby they can interject themseves into the
|
||||
* failure recovery process and do their own failure recovery.
|
||||
*/
|
||||
public void setFailureHandler (FailureHandler handler)
|
||||
{
|
||||
if (_failureHandler != null) {
|
||||
log.warning("Requested to set failure handler, but we've already got one. The " +
|
||||
"conflicting entities will likely need to perform more sophisticated " +
|
||||
"coordination to deal with failures.",
|
||||
"old", _failureHandler, "new", handler);
|
||||
|
||||
} else {
|
||||
_failureHandler = handler;
|
||||
}
|
||||
}
|
||||
|
||||
protected void handleFailure (final int placeId, final String reason)
|
||||
{
|
||||
_observers.apply(new ObserverOp<LocationObserver>() {
|
||||
public boolean apply (LocationObserver obs) {
|
||||
obs.locationChangeFailed(placeId, reason);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// try to return to our previous location
|
||||
if (_failureHandler != null) {
|
||||
_failureHandler.recoverFailedMove(placeId);
|
||||
|
||||
} else if (_placeId <= 0) {
|
||||
// if we were previously somewhere (and that somewhere isn't where we just tried to
|
||||
// go), try going back to that happy place
|
||||
if (_previousPlaceId != -1 && _previousPlaceId != placeId) {
|
||||
moveTo(_previousPlaceId);
|
||||
}
|
||||
|
||||
} // else we're currently somewhere, so just stay there
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to create our place controller using the supplied place configuration. This lives in
|
||||
* a separate method so that derived instances can do funny class loader business if necessary
|
||||
* to load the place controller using a sandboxed class loader.
|
||||
*/
|
||||
protected PlaceController createController (PlaceConfig config)
|
||||
{
|
||||
return config.createController();
|
||||
}
|
||||
|
||||
public void addPendingForcedMove (Runnable move)
|
||||
{
|
||||
_pendingForcedMoves.add(move);
|
||||
}
|
||||
|
||||
protected void handlePendingForcedMove ()
|
||||
{
|
||||
if (!_pendingForcedMoves.isEmpty()) {
|
||||
_ctx.getClient().getRunQueue().postRunnable(_pendingForcedMoves.remove(0));
|
||||
}
|
||||
}
|
||||
|
||||
/** The context through which we access needed services. */
|
||||
protected CrowdContext _ctx;
|
||||
|
||||
/** Provides access to location services. */
|
||||
protected LocationService _lservice;
|
||||
|
||||
/** Our location observer list. */
|
||||
protected ObserverList<LocationObserver> _observers = new ObserverList<LocationObserver>(
|
||||
ObserverList.SAFE_IN_ORDER_NOTIFY);
|
||||
|
||||
/** Used to subscribe to our place object. */
|
||||
protected SafeSubscriber<PlaceObject> _subber;
|
||||
|
||||
/** The oid of the place we currently occupy. */
|
||||
protected int _placeId = -1;
|
||||
|
||||
/** The place object that we currently occupy. */
|
||||
protected PlaceObject _plobj;
|
||||
|
||||
/** The place controller in effect for our current place. */
|
||||
protected PlaceController _controller;
|
||||
|
||||
/** The oid of the place for which we have an outstanding moveTo request, or -1 if we have no
|
||||
* outstanding request. */
|
||||
protected int _pendingPlaceId = -1;
|
||||
|
||||
/** The oid of the place we previously occupied. */
|
||||
protected int _previousPlaceId = -1;
|
||||
|
||||
/** The last time we requested a move to. */
|
||||
protected long _lastRequestTime;
|
||||
|
||||
/** The entity that deals when we fail to subscribe to a place object. */
|
||||
protected FailureHandler _failureHandler;
|
||||
|
||||
/** A listener that wants to know if we succeeded or how we failed to move. */
|
||||
protected ResultListener<PlaceConfig> _moveListener;
|
||||
|
||||
/** Forced move actions we should take once we complete the move we're in the middle of. */
|
||||
protected ArrayList<Runnable> _pendingForcedMoves = Lists.newArrayList();
|
||||
|
||||
/** The operation used to inform observers that the location changed. */
|
||||
protected ObserverOp<LocationObserver> _didChangeOp = new ObserverOp<LocationObserver>() {
|
||||
public boolean apply (LocationObserver obs) {
|
||||
obs.locationDidChange(_plobj);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** We require that a moveTo request be outstanding for one minute before it is declared to be
|
||||
* stale. */
|
||||
protected static final long STALE_REQUEST_DURATION = 60L * 1000L;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* The location observer interface makes it possible for entities to be
|
||||
* notified when the client moves to a new location. It also provides a
|
||||
* means for an entity to participate in the ratification process of a new
|
||||
* location. Observers may opt to reject a request to change to a new
|
||||
* location, probably because something is going on in the previous
|
||||
* location that should not be abandoned.
|
||||
*
|
||||
* <p> Note that these location callbacks occur on the main thread and
|
||||
* should execute quickly and not block under any circumstance.
|
||||
*/
|
||||
public interface LocationObserver
|
||||
{
|
||||
/**
|
||||
* Called when someone has requested that we switch to a new location.
|
||||
* An observer may choose to veto the location change request for some
|
||||
* reason or other.
|
||||
*
|
||||
* @return true if it's OK for the location to change, false if the
|
||||
* change request should be aborted.
|
||||
*/
|
||||
boolean locationMayChange (int placeId);
|
||||
|
||||
/**
|
||||
* Called when we have switched to a new location.
|
||||
*
|
||||
* @param place the place object that represents the new location or
|
||||
* null if we have switched to no location.
|
||||
*/
|
||||
void locationDidChange (PlaceObject place);
|
||||
|
||||
/**
|
||||
* This is called on all location observers when a location change
|
||||
* request is rejected by the server or fails for some other reason.
|
||||
*
|
||||
* @param placeId the place id to which we attempted to relocate, but
|
||||
* failed.
|
||||
* @param reason the reason code that explains why the location change
|
||||
* request was rejected or otherwise failed.
|
||||
*/
|
||||
void locationChangeFailed (int placeId, String reason);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.presents.client.InvocationReceiver;
|
||||
|
||||
/**
|
||||
* Defines, for the location services, a set of notifications delivered
|
||||
* asynchronously by the server to the client.
|
||||
*/
|
||||
public interface LocationReceiver extends InvocationReceiver
|
||||
{
|
||||
/**
|
||||
* Used to communicate a required move notification to the client. The
|
||||
* server will have removed the client from their existing location
|
||||
* and the client is then responsible for generating a {@link
|
||||
* LocationService#moveTo} request to move to the new location.
|
||||
*/
|
||||
void forcedMove (int placeId);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
|
||||
/**
|
||||
* The location services provide a mechanism by which the client can request to move from place to
|
||||
* place in the server. These services should not be used directly, but instead should be accessed
|
||||
* via the {@link LocationDirector}.
|
||||
*/
|
||||
public interface LocationService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Used to communicate responses to {@link LocationService#moveTo} requests.
|
||||
*/
|
||||
public static interface MoveListener extends InvocationListener
|
||||
{
|
||||
/**
|
||||
* Called in response to a successful {@link LocationService#moveTo} request.
|
||||
*/
|
||||
void moveSucceeded (PlaceConfig config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that this client's body be moved to the specified location.
|
||||
*
|
||||
* @param client a reference to the client object that defines the context in which this
|
||||
* invocation service should be executed.
|
||||
* @param placeId the object id of the place object to which the body should be moved.
|
||||
* @param listener the listener that will be informed of success or failure.
|
||||
*/
|
||||
void moveTo (Client client, int placeId, MoveListener listener);
|
||||
|
||||
/**
|
||||
* Requests that we leave our current place and move to nowhere land.
|
||||
*/
|
||||
void leavePlace (Client client);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
/**
|
||||
* An exception that indicates that the server did not allow us to move.
|
||||
*/
|
||||
public class MoveFailedException extends Exception
|
||||
{
|
||||
public MoveFailedException (String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
/**
|
||||
* An exception that indicates that a LocationObserver vetoed our move request.
|
||||
*/
|
||||
public class MoveVetoedException extends Exception
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
|
||||
/**
|
||||
* The occupant adapter makes life easier for occupant observer classes
|
||||
* that only care about one or two of the occupant observer
|
||||
* callbacks. They can either extend occupant adapter or create an
|
||||
* anonymous class that extends it and overrides just the callbacks they
|
||||
* care about.
|
||||
*/
|
||||
public class OccupantAdapter implements OccupantObserver
|
||||
{
|
||||
// documentation inherited from interface
|
||||
public void occupantEntered (OccupantInfo info)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void occupantLeft (OccupantInfo info)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void occupantUpdated (OccupantInfo oinfo, OccupantInfo info)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.samskivert.util.ObserverList;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.BasicDirector;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.dobj.EntryAddedEvent;
|
||||
import com.threerings.presents.dobj.EntryRemovedEvent;
|
||||
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||
import com.threerings.presents.dobj.SetListener;
|
||||
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* The occupant director listens for occupants of places to enter and
|
||||
* exit, and dispatches notices to interested parties about these events.
|
||||
*
|
||||
* <p> It will eventually provide a framework for keeping track of
|
||||
* occupant information in a network efficient manner. The idea being that
|
||||
* we want to store as little information about occupants as possible in
|
||||
* the place object (probably just body oid and username), but upon
|
||||
* entering a place, this will be all we know about the occupants. We then
|
||||
* dispatch a request to get information about all of the occupants in the
|
||||
* room (things like avatar information for a graphical display or perhaps
|
||||
* their ratings in the game that is associated with a place for a gaming
|
||||
* site) which we then pass on to the occupant observers when it becomes
|
||||
* available.
|
||||
*
|
||||
* <p> This information would be cached and we could return cached
|
||||
* information for occupants for which we have cached info. We will
|
||||
* probably want to still make a request for the occupant info so that we
|
||||
* can update non-static occupant data rather than permanently using
|
||||
* what's in the cache.
|
||||
*/
|
||||
public class OccupantDirector extends BasicDirector
|
||||
implements LocationObserver, SetListener<OccupantInfo>
|
||||
{
|
||||
/**
|
||||
* Constructs a new occupant director with the supplied context.
|
||||
*/
|
||||
public OccupantDirector (CrowdContext ctx)
|
||||
{
|
||||
super(ctx);
|
||||
|
||||
// register ourselves as a location observer
|
||||
ctx.getLocationDirector().addLocationObserver(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified occupant observer to the list.
|
||||
*/
|
||||
public void addOccupantObserver (OccupantObserver obs)
|
||||
{
|
||||
_observers.add(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified occupant observer from the list.
|
||||
*/
|
||||
public void removeOccupantObserver (OccupantObserver obs)
|
||||
{
|
||||
_observers.remove(obs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the occupant info for the user in question if it exists in
|
||||
* the currently occupied place. Returns null if no occupant info
|
||||
* exists for the specified body.
|
||||
*/
|
||||
public OccupantInfo getOccupantInfo (int bodyOid)
|
||||
{
|
||||
// make sure we're somewhere
|
||||
return (_place == null) ? null : _place.occupantInfo.get(Integer.valueOf(bodyOid));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the occupant info for the user in question if it exists in
|
||||
* the currently occupied place. Returns null if no occupant info
|
||||
* exists with the specified username.
|
||||
*/
|
||||
public OccupantInfo getOccupantInfo (Name username)
|
||||
{
|
||||
return (_place == null) ? null : _place.getOccupantInfo(username);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clientDidLogoff (Client client)
|
||||
{
|
||||
// clear things out
|
||||
if (_place != null) {
|
||||
_place.removeListener(this);
|
||||
_place = null;
|
||||
}
|
||||
}
|
||||
|
||||
// inherit documentation
|
||||
public boolean locationMayChange (int placeId)
|
||||
{
|
||||
// we've got no opinion
|
||||
return true;
|
||||
}
|
||||
|
||||
// inherit documentation
|
||||
public void locationDidChange (PlaceObject place)
|
||||
{
|
||||
// unlisten to the old place object if there was one
|
||||
if (_place != null) {
|
||||
_place.removeListener(this);
|
||||
}
|
||||
|
||||
// listen to the new one
|
||||
_place = place;
|
||||
if (_place != null) {
|
||||
_place.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
// inherit documentation
|
||||
public void locationChangeFailed (int placeId, String reason)
|
||||
{
|
||||
// nothing to do here either
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with all of the processing when an occupant shows up.
|
||||
*/
|
||||
public void entryAdded (EntryAddedEvent<OccupantInfo> event)
|
||||
{
|
||||
// bail if this isn't for the OCCUPANT_INFO field
|
||||
if (!event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now let the occupant observers know what's up
|
||||
final OccupantInfo info = event.getEntry();
|
||||
_observers.apply(new ObserverList.ObserverOp<OccupantObserver>() {
|
||||
public boolean apply (OccupantObserver observer) {
|
||||
observer.occupantEntered(info);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with all of the processing when an occupant is updated.
|
||||
*/
|
||||
public void entryUpdated (EntryUpdatedEvent<OccupantInfo> event)
|
||||
{
|
||||
// bail if this isn't for the OCCUPANT_INFO field
|
||||
if (!event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// now let the occupant observers know what's up
|
||||
final OccupantInfo info = event.getEntry();
|
||||
final OccupantInfo oinfo = event.getOldEntry();
|
||||
_observers.apply(new ObserverList.ObserverOp<OccupantObserver>() {
|
||||
public boolean apply (OccupantObserver observer) {
|
||||
observer.occupantUpdated(oinfo, info);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with all of the processing when an occupant leaves.
|
||||
*/
|
||||
public void entryRemoved (EntryRemovedEvent<OccupantInfo> event)
|
||||
{
|
||||
// bail if this isn't for the OCCUPANT_INFO field
|
||||
if (!event.getName().equals(PlaceObject.OCCUPANT_INFO)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// let the occupant observers know what's up
|
||||
final OccupantInfo oinfo = event.getOldEntry();
|
||||
_observers.apply(new ObserverList.ObserverOp<OccupantObserver>() {
|
||||
public boolean apply (OccupantObserver observer) {
|
||||
observer.occupantLeft(oinfo);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** The occupant observers to keep abreast of occupant antics. */
|
||||
protected ObserverList<OccupantObserver> _observers = ObserverList.newSafeInOrder();
|
||||
|
||||
/** The user's current location. */
|
||||
protected PlaceObject _place;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
|
||||
/**
|
||||
* An entity that is interested in hearing about bodies that enter and leave a location (as well
|
||||
* as disconnect and reconnect) can implement this interface and register itself with the
|
||||
* {@link OccupantDirector}.
|
||||
*/
|
||||
public interface OccupantObserver
|
||||
{
|
||||
/**
|
||||
* Called when a body enters the place.
|
||||
*/
|
||||
void occupantEntered (OccupantInfo info);
|
||||
|
||||
/**
|
||||
* Called when a body leaves the place.
|
||||
*/
|
||||
void occupantLeft (OccupantInfo info);
|
||||
|
||||
/**
|
||||
* Called when an occupant is updated.
|
||||
*
|
||||
* @param oldinfo the occupant info prior to the update.
|
||||
* @param newinfo the newly update info record.
|
||||
*/
|
||||
void occupantUpdated (OccupantInfo oldinfo, OccupantInfo newinfo);
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import com.samskivert.swing.Controller;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* Controls the user interface that is used to display a place. When the client moves to a new
|
||||
* place, the appropriate place controller is constructed and requested to create and display the
|
||||
* user interface for that place.
|
||||
*/
|
||||
public abstract class PlaceController extends Controller
|
||||
{
|
||||
/**
|
||||
* Used to call methods in delegates.
|
||||
*/
|
||||
public static abstract class DelegateOp
|
||||
{
|
||||
public DelegateOp (Class<? extends PlaceControllerDelegate> delegateClass) {
|
||||
_delegateClass = delegateClass;
|
||||
}
|
||||
|
||||
/** Applies an operation to the supplied delegate. */
|
||||
public abstract void apply (PlaceControllerDelegate delegate);
|
||||
|
||||
public boolean shouldApply (PlaceControllerDelegate delegate) {
|
||||
return _delegateClass.isInstance(delegate);
|
||||
}
|
||||
|
||||
protected Class<? extends PlaceControllerDelegate> _delegateClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this place controller with a reference to the context that they can use to
|
||||
* access client services and to the configuration record for this place. The controller
|
||||
* should create as much of its user interface that it can without having access to the place
|
||||
* object because this will be invoked in parallel with the fetching of the place object. When
|
||||
* the place object is obtained, the controller will be notified and it can then finish the
|
||||
* user interface configuration and put the user interface into operation.
|
||||
*
|
||||
* @param ctx the client context.
|
||||
* @param config the place configuration for this place.
|
||||
*/
|
||||
public void init (CrowdContext ctx, PlaceConfig config)
|
||||
{
|
||||
// keep these around
|
||||
_ctx = ctx;
|
||||
_config = config;
|
||||
|
||||
// create our user interface
|
||||
_view = createPlaceView(_ctx);
|
||||
|
||||
// initialize our delegates
|
||||
applyToDelegates(new DelegateOp(PlaceControllerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
delegate.init(_ctx, _config);
|
||||
}
|
||||
});
|
||||
|
||||
// let the derived classes do any initialization stuff
|
||||
didInit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes can override this and perform any post-initialization processing they might
|
||||
* need. They should of course be sure to call <code>super.didInit()</code>.
|
||||
*/
|
||||
protected void didInit ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to the place view associated with this controller. This is only valid
|
||||
* after a call has been made to {@link #init}.
|
||||
*/
|
||||
public PlaceView getPlaceView ()
|
||||
{
|
||||
return _view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link PlaceConfig} associated with this place.
|
||||
*/
|
||||
public PlaceConfig getPlaceConfig ()
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the user interface that will be used to display this place. The view instance
|
||||
* returned will later be configured with the place object, once it becomes available.
|
||||
*
|
||||
* @param ctx a reference to the {@link CrowdContext} associated with this controller.
|
||||
*/
|
||||
protected PlaceView createPlaceView (CrowdContext ctx)
|
||||
{
|
||||
return createPlaceView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Obsolete but retained for runtime compatibility with the old and busted.
|
||||
*
|
||||
* @deprecated Use {@link #createPlaceView(CrowdContext)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected PlaceView createPlaceView ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the location director once the place object has been fetched. The place
|
||||
* controller will dispatch the place object to the user interface hierarchy via
|
||||
* {@link PlaceViewUtil#dispatchWillEnterPlace}. Derived classes can override this and perform
|
||||
* any other starting up that they need to do
|
||||
*/
|
||||
public void willEnterPlace (final PlaceObject plobj)
|
||||
{
|
||||
// keep a handle on our place object
|
||||
_plobj = plobj;
|
||||
|
||||
if (_view != null) {
|
||||
// let the UI hierarchy know that we've got our place
|
||||
PlaceViewUtil.dispatchWillEnterPlace(_view, plobj);
|
||||
// and display the user interface
|
||||
_ctx.setPlaceView(_view);
|
||||
}
|
||||
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceControllerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
delegate.willEnterPlace(plobj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before a request is submitted to the server to leave the current place. As such,
|
||||
* this method may be called multiple times before {@link #didLeavePlace} is finally called.
|
||||
* The request to leave may be rejected, but if a place controller needs to flush any
|
||||
* information to the place manager before it leaves, it should so do here. This is the only
|
||||
* place in which the controller is guaranteed to be able to communicate to the place manager,
|
||||
* as by the time {@link #didLeavePlace} is called, the place manager may have already been
|
||||
* destroyed.
|
||||
*/
|
||||
public void mayLeavePlace (final PlaceObject plobj)
|
||||
{
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceControllerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
delegate.mayLeavePlace(plobj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called by the location director when we are leaving this place and need to clean up
|
||||
* after ourselves and shutdown. Derived classes should override this method (being sure to
|
||||
* call <code>super.didLeavePlace</code>) and perform any necessary cleanup.
|
||||
*/
|
||||
public void didLeavePlace (final PlaceObject plobj)
|
||||
{
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceControllerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
delegate.didLeavePlace(plobj);
|
||||
}
|
||||
});
|
||||
|
||||
// let the UI hierarchy know that we're outta here
|
||||
if (_view != null) {
|
||||
PlaceViewUtil.dispatchDidLeavePlace(_view, plobj);
|
||||
_ctx.clearPlaceView(_view);
|
||||
_view = null;
|
||||
}
|
||||
|
||||
_plobj = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles basic place controller action events. Derived classes should be sure to call
|
||||
* <code>super.handleAction</code> for events they don't specifically handle.
|
||||
*/
|
||||
@Override
|
||||
public boolean handleAction (final ActionEvent action)
|
||||
{
|
||||
final boolean[] handled = new boolean[1];
|
||||
|
||||
// let our delegates have a crack at the action
|
||||
applyToDelegates(new DelegateOp(PlaceControllerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceControllerDelegate delegate) {
|
||||
// we take advantage of short-circuiting here
|
||||
handled[0] = handled[0] || delegate.handleAction(action);
|
||||
}
|
||||
});
|
||||
|
||||
// if they didn't handle it, pass it off to the super class
|
||||
return handled[0] || super.handleAction(action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied delegate to the list for this controller.
|
||||
*/
|
||||
protected void addDelegate (PlaceControllerDelegate delegate)
|
||||
{
|
||||
if (_delegates == null) {
|
||||
_delegates = Lists.newArrayList();
|
||||
}
|
||||
_delegates.add(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the supplied operation to the registered delegates.
|
||||
*/
|
||||
protected void applyToDelegates (DelegateOp op)
|
||||
{
|
||||
if (_delegates != null) {
|
||||
for (int ii = 0, ll = _delegates.size(); ii < ll; ii++) {
|
||||
PlaceControllerDelegate delegate = _delegates.get(ii);
|
||||
if (op.shouldApply(delegate)) {
|
||||
op.apply(delegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** A reference to the active client context. */
|
||||
protected CrowdContext _ctx;
|
||||
|
||||
/** A reference to our place configuration. */
|
||||
protected PlaceConfig _config;
|
||||
|
||||
/** A reference to the place object for which we're controlling a user
|
||||
* interface. */
|
||||
protected PlaceObject _plobj;
|
||||
|
||||
/** A reference to the root user interface component. */
|
||||
protected PlaceView _view;
|
||||
|
||||
/** A list of the delegates in use by this controller. */
|
||||
protected ArrayList<PlaceControllerDelegate> _delegates;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
import com.threerings.crowd.util.CrowdContext;
|
||||
|
||||
/**
|
||||
* Provides an extensible mechanism for encapsulating delegated
|
||||
* functionality that works with the place services.
|
||||
*
|
||||
* <p> Thanks to Java's lack of multiple inheritance, it will likely
|
||||
* become necessary to factor certain services that might be used by a
|
||||
* variety of {@link PlaceController} derived classes into delegate
|
||||
* classes because they do not fit into the single inheritance hierarchy
|
||||
* that makes sense for a particular application. To facilitate this
|
||||
* process, this delegate class is provided which the standard place
|
||||
* controller can be made to call out to for all of the standard methods.
|
||||
*/
|
||||
public class PlaceControllerDelegate
|
||||
{
|
||||
/**
|
||||
* Constructs the delegate with the controller for which it is
|
||||
* delegating.
|
||||
*/
|
||||
public PlaceControllerDelegate (PlaceController controller)
|
||||
{
|
||||
_controller = controller;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to initialize the delegate.
|
||||
*/
|
||||
public void init (CrowdContext ctx, PlaceConfig config)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to let the delegate know that we're entering a place.
|
||||
*/
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before a request is submitted to the server to leave the
|
||||
* current place. The request to leave may be rejected, but if a place
|
||||
* controller needs to make a final communication to the place manager
|
||||
* before it leaves, it should so do here. This is the only place in
|
||||
* which the controller is guaranteed to be able to communicate to the
|
||||
* place manager, as by the time {@link #didLeavePlace} is called, the
|
||||
* place manager may have already been destroyed.
|
||||
*/
|
||||
public void mayLeavePlace (final PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to let the delegate know that we've left the place.
|
||||
*/
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to give the delegate a chance to handle controller actions
|
||||
* that weren't handled by the main controller.
|
||||
*/
|
||||
public boolean handleAction (ActionEvent action)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** A reference to the controller for which we are delegating. */
|
||||
protected PlaceController _controller;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import com.samskivert.swing.Controller;
|
||||
import com.samskivert.swing.ControllerProvider;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* A useful base class for client interfaces which wish to make use of a
|
||||
* {@link JPanel} as their top-level {@link PlaceView}.
|
||||
*/
|
||||
public class PlacePanel extends JPanel
|
||||
implements ControllerProvider, PlaceView
|
||||
{
|
||||
/**
|
||||
* Constructs a place panel with the specified controller which will
|
||||
* be made availabel via the {@link ControllerProvider} interface.
|
||||
*/
|
||||
public PlacePanel (PlaceController controller)
|
||||
{
|
||||
_controller = controller;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public Controller getController ()
|
||||
{
|
||||
return _controller;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void willEnterPlace (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
/** A reference to the controller with which we interoperate. */
|
||||
protected PlaceController _controller;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* This interface provides a convenient means for decoupling user
|
||||
* interface components that interact with a place object and that need to
|
||||
* keep themselves up to date when the client moves from place to place.
|
||||
*
|
||||
* <p> In general, such components need to know when the client is about
|
||||
* to enter a place so that they can subscribe if necessary or at least
|
||||
* extract information about the place. They also need to know when a
|
||||
* client has left a place so that they can unsubscribe and clean up after
|
||||
* themselves. This is the information that the place view interface makes
|
||||
* available to them in a decoupled way.
|
||||
*
|
||||
* <p> The part of the client implementation that is responsible for the
|
||||
* main user interface can act as a location observer, and it can make use
|
||||
* of {@link PlaceViewUtil} to dispatch notification of place changes to
|
||||
* every <code>PlaceView</code> implementing user interface element in the
|
||||
* user interface hierarchy with calls to {@link
|
||||
* PlaceViewUtil#dispatchWillEnterPlace} and {@link
|
||||
* PlaceViewUtil#dispatchDidLeavePlace}. These functions traverse the UI
|
||||
* hierarchy (starting with the element provided which would generally be
|
||||
* the top-level UI element, and dispatch calls to {@link #willEnterPlace}
|
||||
* and {@link #didLeavePlace} respectively on any UI element they find
|
||||
* that implements <code>PlaceView</code>.
|
||||
*
|
||||
* <p> By doing this, the client code can simply create place-sensitive
|
||||
* user interface elements and stick them in the user interface and
|
||||
* essentially forget about them, knowing that they will all be notified
|
||||
* of place entering and exiting by virtue of the single dispatching
|
||||
* calls. It is useful to note that place-sensitive user interface
|
||||
* elements will also generally need a reference to the {@link
|
||||
* com.threerings.crowd.util.CrowdContext} derivative in use by
|
||||
* the client, but those are best supplied at construct time.
|
||||
*/
|
||||
public interface PlaceView
|
||||
{
|
||||
/**
|
||||
* Called when the client has entered a place and is about to display
|
||||
* the user interface for that place.
|
||||
*
|
||||
* @param plobj the place object that was just entered.
|
||||
*/
|
||||
void willEnterPlace (PlaceObject plobj);
|
||||
|
||||
/**
|
||||
* Called after the client has left a place and needs to clean up
|
||||
* after the user interface that was displaying that place.
|
||||
*
|
||||
* @param plobj the place object that was just left.
|
||||
*/
|
||||
void didLeavePlace (PlaceObject plobj);
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.client;
|
||||
|
||||
import java.awt.Container;
|
||||
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Provides a mechanism for dispatching notifications to all user interface elements in a
|
||||
* hierarchy that implement the {@link PlaceView} interface. Look at the documentation for
|
||||
* {@link PlaceView} for more explanation.
|
||||
*/
|
||||
public class PlaceViewUtil
|
||||
{
|
||||
/**
|
||||
* Dispatches a call to {@link PlaceView#willEnterPlace} to all UI elements in the hierarchy
|
||||
* rooted at the component provided via the <code>root</code> parameter.
|
||||
*
|
||||
* @param root the component at which to start traversing the UI hierarchy.
|
||||
* @param plobj the place object that is about to be entered.
|
||||
*/
|
||||
public static void dispatchWillEnterPlace (Object root, PlaceObject plobj)
|
||||
{
|
||||
// dispatch the call on this component if it implements PlaceView
|
||||
if (root instanceof PlaceView) {
|
||||
try {
|
||||
((PlaceView)root).willEnterPlace(plobj);
|
||||
} catch (Exception e) {
|
||||
log.warning("Component choked on willEnterPlace()", "component", root,
|
||||
"plobj", plobj, e);
|
||||
}
|
||||
}
|
||||
|
||||
// now traverse all of this component's children
|
||||
if (root instanceof Container) {
|
||||
Container cont = (Container)root;
|
||||
int ccount = cont.getComponentCount();
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
dispatchWillEnterPlace(cont.getComponent(ii), plobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a call to {@link PlaceView#didLeavePlace} to all UI elements in the hierarchy
|
||||
* rooted at the component provided via the <code>root</code> parameter.
|
||||
*
|
||||
* @param root the component at which to start traversing the UI hierarchy.
|
||||
* @param plobj the place object that is about to be entered.
|
||||
*/
|
||||
public static void dispatchDidLeavePlace (Object root, PlaceObject plobj)
|
||||
{
|
||||
// dispatch the call on this component if it implements PlaceView
|
||||
if (root instanceof PlaceView) {
|
||||
try {
|
||||
((PlaceView)root).didLeavePlace(plobj);
|
||||
} catch (Exception e) {
|
||||
log.warning("Component choked on didLeavePlace()", "component", root,
|
||||
"plobj", plobj, e);
|
||||
}
|
||||
}
|
||||
|
||||
// now traverse all of this component's children
|
||||
if (root instanceof Container) {
|
||||
Container cont = (Container)root;
|
||||
int ccount = cont.getComponentCount();
|
||||
for (int ii = 0; ii < ccount; ii++) {
|
||||
dispatchDidLeavePlace(cont.getComponent(ii), plobj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.client.BodyService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link BodyService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BodyService.java.")
|
||||
public class BodyMarshaller extends InvocationMarshaller
|
||||
implements BodyService
|
||||
{
|
||||
/** The method id used to dispatch {@link #setIdle} requests. */
|
||||
public static final int SET_IDLE = 1;
|
||||
|
||||
// from interface BodyService
|
||||
public void setIdle (Client arg1, boolean arg2)
|
||||
{
|
||||
sendRequest(arg1, SET_IDLE, new Object[] {
|
||||
Boolean.valueOf(arg2)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
|
||||
import com.threerings.crowd.chat.data.SpeakObject;
|
||||
|
||||
/**
|
||||
* The basic user object class for Crowd users. Bodies have a username, a location and a status.
|
||||
*/
|
||||
public class BodyObject extends ClientObject
|
||||
implements SpeakObject
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>location</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String LOCATION = "location";
|
||||
|
||||
/** The field name of the <code>status</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String STATUS = "status";
|
||||
|
||||
/** The field name of the <code>awayMessage</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String AWAY_MESSAGE = "awayMessage";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/**
|
||||
* Identifies the place currently occupied by this body. null if they currently occupy no
|
||||
* place.
|
||||
*/
|
||||
public Place location;
|
||||
|
||||
/**
|
||||
* The user's current status ({@link OccupantInfo#ACTIVE}, etc.).
|
||||
*/
|
||||
public byte status;
|
||||
|
||||
/**
|
||||
* If non-null, this contains a message to be auto-replied whenever another user delivers a
|
||||
* tell message to this user.
|
||||
*/
|
||||
public String awayMessage;
|
||||
|
||||
/**
|
||||
* Returns the oid of the place occupied by this body or -1 if we occupy no place.
|
||||
*/
|
||||
public int getPlaceOid ()
|
||||
{
|
||||
return (location == null) ? -1 : location.placeOid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this user's access control tokens.
|
||||
*/
|
||||
public TokenRing getTokens ()
|
||||
{
|
||||
return EMPTY_TOKENS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name that should be displayed to other users. The default is to use {@link
|
||||
* #username}.
|
||||
*/
|
||||
public Name getVisibleName ()
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a blank occupant info instance that will used to publish information about the
|
||||
* various bodies occupying a place.
|
||||
*/
|
||||
public OccupantInfo createOccupantInfo (PlaceObject plobj)
|
||||
{
|
||||
return new OccupantInfo(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this body is about to enter the specified place. Configures our {@link
|
||||
* #location} field.
|
||||
*
|
||||
* @param place the identifying information for the place we are entering.
|
||||
* @param plobj the distributed object for the place we are entering.
|
||||
*/
|
||||
public void willEnterPlace (Place place, PlaceObject plobj)
|
||||
{
|
||||
setLocation(place);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this body has left its occupied place. Clears our {@link #location} field.
|
||||
*
|
||||
* @param plobj the distributed object for the place we just departed. This might be null if
|
||||
* the place object has been destroyed.
|
||||
*/
|
||||
public void didLeavePlace (PlaceObject plobj)
|
||||
{
|
||||
setLocation(null);
|
||||
}
|
||||
|
||||
// from interface SpeakObject
|
||||
public void applyToListeners (ListenerOp op)
|
||||
{
|
||||
op.apply(getVisibleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String who ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder(username.toString());
|
||||
buf.append(" (");
|
||||
addWhoData(buf);
|
||||
return buf.append(")").toString();
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>location</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setLocation (Place value)
|
||||
{
|
||||
Place ovalue = this.location;
|
||||
requestAttributeChange(
|
||||
LOCATION, value, ovalue);
|
||||
this.location = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>status</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setStatus (byte value)
|
||||
{
|
||||
byte ovalue = this.status;
|
||||
requestAttributeChange(
|
||||
STATUS, Byte.valueOf(value), Byte.valueOf(ovalue));
|
||||
this.status = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>awayMessage</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setAwayMessage (String value)
|
||||
{
|
||||
String ovalue = this.awayMessage;
|
||||
requestAttributeChange(
|
||||
AWAY_MESSAGE, value, ovalue);
|
||||
this.awayMessage = value;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
|
||||
/**
|
||||
* Allows derived classes to add data to the who details.
|
||||
*/
|
||||
protected void addWhoData (StringBuilder buf)
|
||||
{
|
||||
buf.append(getOid());
|
||||
if (status != OccupantInfo.ACTIVE) {
|
||||
buf.append(" ").append(getStatusTranslation());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a translation suffix for this occupant's status.
|
||||
* Can be overridden to translate nonstandard statuses.
|
||||
*/
|
||||
protected String getStatusTranslation ()
|
||||
{
|
||||
return OccupantInfo.X_STATUS[status];
|
||||
}
|
||||
|
||||
/** The default (no tokens) access control. */
|
||||
protected static final TokenRing EMPTY_TOKENS = new TokenRing();
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
|
||||
/**
|
||||
* Codes and constants global to the Crowd services.
|
||||
*/
|
||||
public interface CrowdCodes extends InvocationCodes
|
||||
{
|
||||
/** Defines our invocation services group. */
|
||||
public static final String CROWD_GROUP = "crowd";
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.Permission;
|
||||
import com.threerings.presents.data.PermissionPolicy;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
|
||||
/**
|
||||
* Implements some Crowd permissions.
|
||||
*/
|
||||
public class CrowdPermissionPolicy extends PermissionPolicy
|
||||
{
|
||||
@Override // from PermissionPolicy
|
||||
public String checkAccess (ClientObject clobj, Permission perm, Object context)
|
||||
{
|
||||
if (!(clobj instanceof BodyObject)) {
|
||||
return super.checkAccess(clobj, perm, context);
|
||||
}
|
||||
|
||||
BodyObject body = (BodyObject)clobj;
|
||||
if (perm == ChatCodes.BROADCAST_ACCESS) {
|
||||
return body.getTokens().isAdmin() ? null : ACCESS_DENIED;
|
||||
} else if (perm == ChatCodes.CHAT_ACCESS) {
|
||||
return null;
|
||||
} else {
|
||||
return super.checkAccess(clobj, perm, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.presents.data.InvocationCodes;
|
||||
|
||||
/**
|
||||
* Contains codes used by the location invocation services.
|
||||
*/
|
||||
public interface LocationCodes extends InvocationCodes
|
||||
{
|
||||
/** An error code indicating that a place identified by a particular
|
||||
* place id does not exist. Usually generated by a failed moveTo
|
||||
* request. */
|
||||
public static final String NO_SUCH_PLACE = "m.no_such_place";
|
||||
|
||||
/** An error code sent when a user requests to move to a new place but
|
||||
* they are in the middle of moving somewhere already. */
|
||||
public static final String MOVE_IN_PROGRESS = "m.move_in_progress";
|
||||
|
||||
/** An error code sent when a user requests to move to a place, but
|
||||
* they are already in the requested place. */
|
||||
public static final String ALREADY_THERE = "m.already_there";
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.client.LocationService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.InvocationResponseEvent;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link LocationService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from LocationService.java.")
|
||||
public class LocationMarshaller extends InvocationMarshaller
|
||||
implements LocationService
|
||||
{
|
||||
/**
|
||||
* Marshalls results to implementations of {@link LocationService.MoveListener}.
|
||||
*/
|
||||
public static class MoveMarshaller extends ListenerMarshaller
|
||||
implements MoveListener
|
||||
{
|
||||
/** The method id used to dispatch {@link #moveSucceeded}
|
||||
* responses. */
|
||||
public static final int MOVE_SUCCEEDED = 1;
|
||||
|
||||
// from interface MoveMarshaller
|
||||
public void moveSucceeded (PlaceConfig arg1)
|
||||
{
|
||||
_invId = null;
|
||||
omgr.postEvent(new InvocationResponseEvent(
|
||||
callerOid, requestId, MOVE_SUCCEEDED,
|
||||
new Object[] { arg1 }, transport));
|
||||
}
|
||||
|
||||
@Override // from InvocationMarshaller
|
||||
public void dispatchResponse (int methodId, Object[] args)
|
||||
{
|
||||
switch (methodId) {
|
||||
case MOVE_SUCCEEDED:
|
||||
((MoveListener)listener).moveSucceeded(
|
||||
(PlaceConfig)args[0]);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchResponse(methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #leavePlace} requests. */
|
||||
public static final int LEAVE_PLACE = 1;
|
||||
|
||||
// from interface LocationService
|
||||
public void leavePlace (Client arg1)
|
||||
{
|
||||
sendRequest(arg1, LEAVE_PLACE, new Object[] {
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #moveTo} requests. */
|
||||
public static final int MOVE_TO = 2;
|
||||
|
||||
// from interface LocationService
|
||||
public void moveTo (Client arg1, int arg2, LocationService.MoveListener arg3)
|
||||
{
|
||||
LocationMarshaller.MoveMarshaller listener3 = new LocationMarshaller.MoveMarshaller();
|
||||
listener3.listener = arg3;
|
||||
sendRequest(arg1, MOVE_TO, new Object[] {
|
||||
Integer.valueOf(arg2), listener3
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
|
||||
/**
|
||||
* The occupant info object contains all of the information about an occupant of a place that
|
||||
* should be shared with other occupants of the place. These objects are stored in the place object
|
||||
* itself and are updated when bodies enter and exit a place.
|
||||
*
|
||||
* <p> A system that builds upon the Crowd framework can extend this class to include extra
|
||||
* information about their occupants. They will need to provide a derived {@link BodyObject} that
|
||||
* creates and configures their occupant info in {@link BodyObject#createOccupantInfo}.
|
||||
*
|
||||
* <p> Note also that this class implements {@link Cloneable} which means that if derived classes
|
||||
* add non-primitive attributes, they are responsible for adding the code to clone those attributes
|
||||
* when a clone is requested.
|
||||
*/
|
||||
public class OccupantInfo extends SimpleStreamableObject
|
||||
implements DSet.Entry, Cloneable
|
||||
{
|
||||
/** Constant value for {@link #status}. */
|
||||
public static final byte ACTIVE = 0;
|
||||
|
||||
/** Constant value for {@link #status}. */
|
||||
public static final byte IDLE = 1;
|
||||
|
||||
/** Constant value for {@link #status}. */
|
||||
public static final byte DISCONNECTED = 2;
|
||||
|
||||
/** Maps status codes to human readable strings. */
|
||||
public static final String[] X_STATUS = { "active", "idle", "discon" };
|
||||
|
||||
/** Used by PlaceManager.updateOccupantInfo. */
|
||||
public static interface Updater<T extends OccupantInfo>
|
||||
{
|
||||
/**
|
||||
* Make whatever changes are desired to your {@link OccupantInfo} here.
|
||||
*
|
||||
* @return true if the record was modified and should be published, false if no
|
||||
* modifications were made (it will not be published).
|
||||
*/
|
||||
public boolean update (T info);
|
||||
}
|
||||
|
||||
/** An update to dispatch when an occupant's name changes. */
|
||||
public static class NameUpdater implements Updater<OccupantInfo>
|
||||
{
|
||||
public NameUpdater (Name name) {
|
||||
_name = name;
|
||||
}
|
||||
public boolean update (OccupantInfo info) {
|
||||
// The behaviour here used to be to compare the names themselves against one another
|
||||
// using equals(), but was changed to accommodate the idea of display name changing
|
||||
// while fundamental identity stays the same -- case in point, Whirled's MemberName
|
||||
// bases equal()ity on an integer identifier. TODO: investigate whether this is a
|
||||
// reasonable assumption and whether the behaviour change might break something.
|
||||
if (info.username.getNormal().equals(_name.getNormal())) {
|
||||
return false;
|
||||
}
|
||||
info.username = _name;
|
||||
return true;
|
||||
}
|
||||
protected Name _name;
|
||||
}
|
||||
|
||||
/** The body object id of this occupant (and our entry key). */
|
||||
public Integer bodyOid;
|
||||
|
||||
/** The username of this occupant. */
|
||||
public Name username;
|
||||
|
||||
/** The status of this occupant. */
|
||||
public byte status = ACTIVE;
|
||||
|
||||
/**
|
||||
* Creates an occupant info with information from the specified occupant's body object.
|
||||
*/
|
||||
public OccupantInfo (BodyObject body)
|
||||
{
|
||||
bodyOid = Integer.valueOf(body.getOid());
|
||||
username = body.getVisibleName();
|
||||
status = body.status;
|
||||
}
|
||||
|
||||
/** A blank constructor used for unserialization. */
|
||||
public OccupantInfo ()
|
||||
{
|
||||
}
|
||||
|
||||
/** Access to the body object id as an int. */
|
||||
public int getBodyOid ()
|
||||
{
|
||||
return bodyOid.intValue();
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public Comparable<?> getKey ()
|
||||
{
|
||||
return bodyOid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OccupantInfo clone ()
|
||||
{
|
||||
try {
|
||||
return (OccupantInfo) super.clone();
|
||||
} catch (CloneNotSupportedException cnse) {
|
||||
throw new AssertionError(cnse);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
/**
|
||||
* Contains information on the current place occupied by a body.
|
||||
*/
|
||||
public class Place extends SimpleStreamableObject
|
||||
{
|
||||
/** The oid of this place's {@link PlaceObject}. */
|
||||
public int placeOid;
|
||||
|
||||
/** Used when unserializing. */
|
||||
public Place ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a place with the supplied oid.
|
||||
*/
|
||||
public Place (int placeOid)
|
||||
{
|
||||
this.placeOid = placeOid;
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
public boolean equals (Object other)
|
||||
{
|
||||
if (other == null) {
|
||||
return false;
|
||||
}
|
||||
return getClass().equals(other.getClass()) ? placeOid == ((Place)other).placeOid : false;
|
||||
}
|
||||
|
||||
@Override // from Object
|
||||
public int hashCode ()
|
||||
{
|
||||
return placeOid;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
import com.threerings.util.ActionScript;
|
||||
|
||||
import com.threerings.crowd.client.PlaceController;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* The place config class encapsulates the configuration information for a
|
||||
* particular type of place. The hierarchy of place config objects mimics
|
||||
* the hierarchy of place managers and controllers. Both the place manager
|
||||
* and place controller are provided with the place config object when the
|
||||
* place is created.
|
||||
*
|
||||
* <p> The place config object is also the mechanism used to instantiate the
|
||||
* appropriate place manager and controller. Every place must have an
|
||||
* associated place config derived class that overrides {@link
|
||||
* #createController} and {@link #getManagerClassName}, returning the
|
||||
* appropriate place controller and manager class for that place.
|
||||
*/
|
||||
public abstract class PlaceConfig extends SimpleStreamableObject
|
||||
{
|
||||
/**
|
||||
* Returns the class that should be used to create a controller for this
|
||||
* place. The controller class must derive from {@link PlaceController}.
|
||||
*
|
||||
* @deprecated Override {@link #createController} directly.
|
||||
*/
|
||||
@Deprecated
|
||||
public Class<?> getControllerClass ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the controller that should be used for this place.
|
||||
*/
|
||||
public PlaceController createController ()
|
||||
{
|
||||
Class<?> cclass = getControllerClass();
|
||||
if (cclass == null) {
|
||||
throw new RuntimeException(
|
||||
"PlaceConfig.createController() must be overridden.");
|
||||
}
|
||||
|
||||
log.warning("Providing backwards compatibility. PlaceConfig." +
|
||||
"createController() should be overridden directly.");
|
||||
try {
|
||||
return (PlaceController)cclass.newInstance();
|
||||
} catch (Exception e) {
|
||||
log.warning("Failed to instantiate controller class '" + cclass + "'.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the class that should be used to create a manager
|
||||
* for this place. The manager class must derive from {@link
|
||||
* com.threerings.crowd.server.PlaceManager}. <em>Note:</em> this method
|
||||
* differs from {@link #createController} because we want to avoid compile
|
||||
* time linkage of the place config object (which is used on the client) to
|
||||
* server code. This allows a code optimizer (DashO Pro, for example) to
|
||||
* remove the server code from the client, knowing that it is never used.
|
||||
*/
|
||||
public abstract String getManagerClassName ();
|
||||
|
||||
@Override
|
||||
@ActionScript(name="toStringBuilder")
|
||||
protected void toString (StringBuilder buf)
|
||||
{
|
||||
buf.append("type=").append(StringUtil.shortClassName(this));
|
||||
buf.append(", ");
|
||||
super.toString(buf);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,239 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DObjectManager;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
import com.threerings.presents.dobj.OidList;
|
||||
import com.threerings.presents.dobj.ServerMessageEvent;
|
||||
|
||||
import com.threerings.crowd.chat.data.SpeakMarshaller;
|
||||
import com.threerings.crowd.chat.data.SpeakObject;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* A distributed object that contains information on a place that is occupied by bodies. This place
|
||||
* might be a chat room, a game room, an island in a massively multiplayer piratical universe,
|
||||
* anything that has occupants that might want to chat with one another.
|
||||
*/
|
||||
public class PlaceObject extends DObject
|
||||
implements SpeakObject
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>occupants</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String OCCUPANTS = "occupants";
|
||||
|
||||
/** The field name of the <code>occupantInfo</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String OCCUPANT_INFO = "occupantInfo";
|
||||
|
||||
/** The field name of the <code>speakService</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String SPEAK_SERVICE = "speakService";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/**
|
||||
* Exists to make calls into the manager look sensible:
|
||||
*
|
||||
* <pre>_plobj.manager.invoke("someMethod", args);</pre>
|
||||
*
|
||||
* and to route events through the right distributed object manager if we are running in
|
||||
* standalone/single-player mode where both client and server are running in the same VM.
|
||||
*/
|
||||
public class ManagerCaller
|
||||
{
|
||||
public void invoke (String method, Object ... args) {
|
||||
_omgr.postEvent(new ServerMessageEvent(_oid, method, args));
|
||||
}
|
||||
protected ManagerCaller (DObjectManager omgr) {
|
||||
_omgr = omgr;
|
||||
}
|
||||
protected DObjectManager _omgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the client to call methods on the manager.
|
||||
*/
|
||||
public transient ManagerCaller manager;
|
||||
|
||||
/**
|
||||
* Tracks the oid of the body objects of all of the occupants of this place.
|
||||
*/
|
||||
public OidList occupants = new OidList();
|
||||
|
||||
/**
|
||||
* Contains an info record (of type {@link OccupantInfo}) for each occupant that contains
|
||||
* information about that occupant that needs to be known by everyone in the place.
|
||||
* <em>Note:</em> Don't obtain occupant info records directly from this set when on the server,
|
||||
* use <code>PlaceManager.getOccupantInfo()</code> instead (along with
|
||||
* <code>PlaceManager.updateOccupantInfo()</code>) because it does some special processing to
|
||||
* ensure that readers and updaters don't step on one another even if they make rapid fire
|
||||
* changes to a user's occupant info.
|
||||
*/
|
||||
public DSet<OccupantInfo> occupantInfo = new DSet<OccupantInfo>();
|
||||
|
||||
/** Used to generate speak requests on this place object. */
|
||||
public SpeakMarshaller speakService;
|
||||
|
||||
/**
|
||||
* Called on the client when the location director receives this place object to configure our
|
||||
* manager caller using the client's distributed object manager.
|
||||
*/
|
||||
public void initManagerCaller (DObjectManager omgr)
|
||||
{
|
||||
manager = new ManagerCaller(omgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to indicate whether broadcast chat messages should be dispatched on this place object.
|
||||
*/
|
||||
public boolean shouldBroadcast ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a user's occupant info by name.
|
||||
*
|
||||
* @return the occupant info record for the named user or null if no user in the room has that
|
||||
* username.
|
||||
*/
|
||||
public OccupantInfo getOccupantInfo (Name username)
|
||||
{
|
||||
try {
|
||||
for (OccupantInfo info : occupantInfo) {
|
||||
if (info.username.equals(username)) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.warning("PlaceObject.getOccupantInfo choked.", t);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void applyToListeners (ListenerOp op)
|
||||
{
|
||||
for (int ii = 0, ll = occupants.size(); ii < ll; ii++) {
|
||||
op.apply(occupants.get(ii));
|
||||
}
|
||||
}
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that <code>oid</code> be added to the <code>occupants</code>
|
||||
* oid list. The list will not change until the event is actually
|
||||
* propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void addToOccupants (int oid)
|
||||
{
|
||||
requestOidAdd(OCCUPANTS, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that <code>oid</code> be removed from the
|
||||
* <code>occupants</code> oid list. The list will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void removeFromOccupants (int oid)
|
||||
{
|
||||
requestOidRemove(OCCUPANTS, oid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>occupantInfo</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void addToOccupantInfo (OccupantInfo elem)
|
||||
{
|
||||
requestEntryAdd(OCCUPANT_INFO, occupantInfo, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>occupantInfo</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void removeFromOccupantInfo (Comparable<?> key)
|
||||
{
|
||||
requestEntryRemove(OCCUPANT_INFO, occupantInfo, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>occupantInfo</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void updateOccupantInfo (OccupantInfo elem)
|
||||
{
|
||||
requestEntryUpdate(OCCUPANT_INFO, occupantInfo, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>occupantInfo</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set value. The local value will be
|
||||
* updated immediately and an event will be propagated through the
|
||||
* system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setOccupantInfo (DSet<OccupantInfo> value)
|
||||
{
|
||||
requestAttributeChange(OCCUPANT_INFO, value, this.occupantInfo);
|
||||
DSet<OccupantInfo> clone = (value == null) ? null : value.clone();
|
||||
this.occupantInfo = clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>speakService</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setSpeakService (SpeakMarshaller value)
|
||||
{
|
||||
SpeakMarshaller ovalue = this.speakService;
|
||||
requestAttributeChange(
|
||||
SPEAK_SERVICE, value, ovalue);
|
||||
this.speakService = value;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.data;
|
||||
|
||||
import com.threerings.io.SimpleStreamableObject;
|
||||
|
||||
/**
|
||||
* Defines access control tokens that convey certain privileges to users.
|
||||
*
|
||||
* @see CrowdPermissionPolicy
|
||||
*/
|
||||
public class TokenRing extends SimpleStreamableObject
|
||||
{
|
||||
/** Indicates that this user is an administrator. */
|
||||
public static final int ADMIN = (1 << 0);
|
||||
|
||||
/**
|
||||
* A default constructor, used when unserializing token rings.
|
||||
*/
|
||||
public TokenRing ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a token ring with the supplied set of tokens.
|
||||
*/
|
||||
public TokenRing (int tokens)
|
||||
{
|
||||
_tokens = tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this token ring contains the specified token or tokens, exactly. For
|
||||
* example, if you pass in the OR of two or more tokens, then the ring must contain all of
|
||||
* those tokens.
|
||||
*/
|
||||
public boolean holdsToken (int token)
|
||||
{
|
||||
return (_tokens & token) == token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this token ring contains any one of the specified tokens.
|
||||
*/
|
||||
public boolean holdsAnyToken (int tokens)
|
||||
{
|
||||
return (_tokens & tokens) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function for checking whether this ring holds the {@link #ADMIN} token.
|
||||
*/
|
||||
public boolean isAdmin ()
|
||||
{
|
||||
return holdsToken(ADMIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bitmask that stores the various tokens.
|
||||
*/
|
||||
public int getTokens ()
|
||||
{
|
||||
return _tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the specified token to be on or off.
|
||||
*/
|
||||
public void setToken (int token, boolean on)
|
||||
{
|
||||
if (on) {
|
||||
setToken(token);
|
||||
} else {
|
||||
clearToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the specified token to this ring.
|
||||
*/
|
||||
public void setToken (int token)
|
||||
{
|
||||
_tokens |= token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the specified token from this ring.
|
||||
*/
|
||||
public void clearToken (int token)
|
||||
{
|
||||
_tokens &= ~token;
|
||||
}
|
||||
|
||||
/** The tokens contained in this ring (composed together bitwise). */
|
||||
protected int _tokens;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!-- $Id: package.html 617 2001-11-13 00:12:20Z mdb $ -->
|
||||
</head>
|
||||
|
||||
<body bgcolor="white">
|
||||
|
||||
Builds on the Presents framework to provide services uesful for chatting
|
||||
and moving from place to place in a distributed space.
|
||||
|
||||
<p> The main Crowd concepts are that of Body and Place and the notion of a
|
||||
PlaceManager on the server and a pairing of PlaceController and PlaceView
|
||||
on the client. </p>
|
||||
|
||||
<ul>
|
||||
<li><a href="client/package-summary.html">client</a> - the client side of
|
||||
the Crowd services.
|
||||
<li><a href="data/package-summary.html">data</a> - classes that are shared
|
||||
between client and server.
|
||||
<li><a href="server/package-summary.html">server</a> - the server side of
|
||||
the Crowd services.
|
||||
</ul>
|
||||
|
||||
<p> Crowd also provides a mechanism for Chat in Places and between
|
||||
individuals that are logged on. </p>
|
||||
|
||||
<ul>
|
||||
<li><a href="chat/client/package-summary.html">client</a> - the client side of
|
||||
the Crowd Chat services.
|
||||
<li><a href="chat/data/package-summary.html">data</a> - classes that are
|
||||
shared between client and server.
|
||||
<li><a href="chat/server/package-summary.html">server</a> - the server side of
|
||||
the Crowd Chat services.
|
||||
</ul>
|
||||
|
||||
<p> Crowd also integrates with the Presents peer system to make its Chat
|
||||
services work in a peer environment. </p>
|
||||
|
||||
<ul>
|
||||
<li><a href="chat/client/package-summary.html">client</a> - the client side of
|
||||
the Crowd Peer Chat services.
|
||||
<li><a href="chat/data/package-summary.html">data</a> - classes that are
|
||||
shared between servers.
|
||||
<li><a href="chat/server/package-summary.html">server</a> - the server side of
|
||||
the Crowd Peer Chat services.
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.client;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.client.InvocationService;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
|
||||
/**
|
||||
* Bridges certain Crowd services between peers in a cluster configuration.
|
||||
*/
|
||||
public interface CrowdPeerService extends InvocationService
|
||||
{
|
||||
/**
|
||||
* Used to forward a tell request to the server on which the destination user actually
|
||||
* occupies.
|
||||
*/
|
||||
void deliverTell (Client client, UserMessage message, Name target,
|
||||
ChatService.TellListener listener);
|
||||
|
||||
/**
|
||||
* Dispatches a broadcast message on this peer.
|
||||
*/
|
||||
void deliverBroadcast (Client client, Name from, byte levelOrMode, String bundle, String msg);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.data;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.peer.data.ClientInfo;
|
||||
|
||||
/**
|
||||
* Extends the standard {@link ClientInfo} with Crowd bits.
|
||||
*/
|
||||
public class CrowdClientInfo extends ClientInfo
|
||||
{
|
||||
/** The client's visible name, which is used for chatting. */
|
||||
public Name visibleName;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
import com.threerings.presents.peer.data.NodeObject;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatChannel;
|
||||
|
||||
/**
|
||||
* Extends the basic {@link NodeObject} with Crowd bits.
|
||||
*/
|
||||
public class CrowdNodeObject extends NodeObject
|
||||
{
|
||||
// AUTO-GENERATED: FIELDS START
|
||||
/** The field name of the <code>crowdPeerService</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String CROWD_PEER_SERVICE = "crowdPeerService";
|
||||
|
||||
/** The field name of the <code>hostedChannels</code> field. */
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public static final String HOSTED_CHANNELS = "hostedChannels";
|
||||
// AUTO-GENERATED: FIELDS END
|
||||
|
||||
/** Used to coordinate tells between servers. */
|
||||
public CrowdPeerMarshaller crowdPeerService;
|
||||
|
||||
/** The chat channels hosted on this server. */
|
||||
public DSet<ChatChannel> hostedChannels = new DSet<ChatChannel>();
|
||||
|
||||
// AUTO-GENERATED: METHODS START
|
||||
/**
|
||||
* Requests that the <code>crowdPeerService</code> field be set to the
|
||||
* specified value. The local value will be updated immediately and an
|
||||
* event will be propagated through the system to notify all listeners
|
||||
* that the attribute did change. Proxied copies of this object (on
|
||||
* clients) will apply the value change when they received the
|
||||
* attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setCrowdPeerService (CrowdPeerMarshaller value)
|
||||
{
|
||||
CrowdPeerMarshaller ovalue = this.crowdPeerService;
|
||||
requestAttributeChange(
|
||||
CROWD_PEER_SERVICE, value, ovalue);
|
||||
this.crowdPeerService = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be added to the
|
||||
* <code>hostedChannels</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void addToHostedChannels (ChatChannel elem)
|
||||
{
|
||||
requestEntryAdd(HOSTED_CHANNELS, hostedChannels, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the entry matching the supplied key be removed from
|
||||
* the <code>hostedChannels</code> set. The set will not change until the
|
||||
* event is actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void removeFromHostedChannels (Comparable<?> key)
|
||||
{
|
||||
requestEntryRemove(HOSTED_CHANNELS, hostedChannels, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the specified entry be updated in the
|
||||
* <code>hostedChannels</code> set. The set will not change until the event is
|
||||
* actually propagated through the system.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void updateHostedChannels (ChatChannel elem)
|
||||
{
|
||||
requestEntryUpdate(HOSTED_CHANNELS, hostedChannels, elem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests that the <code>hostedChannels</code> field be set to the
|
||||
* specified value. Generally one only adds, updates and removes
|
||||
* entries of a distributed set, but certain situations call for a
|
||||
* complete replacement of the set value. The local value will be
|
||||
* updated immediately and an event will be propagated through the
|
||||
* system to notify all listeners that the attribute did
|
||||
* change. Proxied copies of this object (on clients) will apply the
|
||||
* value change when they received the attribute changed notification.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenDObjectTask"})
|
||||
public void setHostedChannels (DSet<ChatChannel> value)
|
||||
{
|
||||
requestAttributeChange(HOSTED_CHANNELS, value, this.hostedChannels);
|
||||
DSet<ChatChannel> clone = (value == null) ? null : value.clone();
|
||||
this.hostedChannels = clone;
|
||||
}
|
||||
// AUTO-GENERATED: METHODS END
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.data;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.ChatMarshaller;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.peer.client.CrowdPeerService;
|
||||
import com.threerings.presents.client.Client;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Provides the implementation of the {@link CrowdPeerService} interface
|
||||
* that marshalls the arguments and delivers the request to the provider
|
||||
* on the server. Also provides an implementation of the response listener
|
||||
* interfaces that marshall the response arguments and deliver them back
|
||||
* to the requesting client.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from CrowdPeerService.java.")
|
||||
public class CrowdPeerMarshaller extends InvocationMarshaller
|
||||
implements CrowdPeerService
|
||||
{
|
||||
/** The method id used to dispatch {@link #deliverBroadcast} requests. */
|
||||
public static final int DELIVER_BROADCAST = 1;
|
||||
|
||||
// from interface CrowdPeerService
|
||||
public void deliverBroadcast (Client arg1, Name arg2, byte arg3, String arg4, String arg5)
|
||||
{
|
||||
sendRequest(arg1, DELIVER_BROADCAST, new Object[] {
|
||||
arg2, Byte.valueOf(arg3), arg4, arg5
|
||||
});
|
||||
}
|
||||
|
||||
/** The method id used to dispatch {@link #deliverTell} requests. */
|
||||
public static final int DELIVER_TELL = 2;
|
||||
|
||||
// from interface CrowdPeerService
|
||||
public void deliverTell (Client arg1, UserMessage arg2, Name arg3, ChatService.TellListener arg4)
|
||||
{
|
||||
ChatMarshaller.TellMarshaller listener4 = new ChatMarshaller.TellMarshaller();
|
||||
listener4.listener = arg4;
|
||||
sendRequest(arg1, DELIVER_TELL, new Object[] {
|
||||
arg2, arg3, listener4
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.peer.data.CrowdPeerMarshaller;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link CrowdPeerProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from CrowdPeerService.java.")
|
||||
public class CrowdPeerDispatcher extends InvocationDispatcher<CrowdPeerMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public CrowdPeerDispatcher (CrowdPeerProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrowdPeerMarshaller createMarshaller ()
|
||||
{
|
||||
return new CrowdPeerMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case CrowdPeerMarshaller.DELIVER_BROADCAST:
|
||||
((CrowdPeerProvider)provider).deliverBroadcast(
|
||||
source, (Name)args[0], ((Byte)args[1]).byteValue(), (String)args[2], (String)args[3]
|
||||
);
|
||||
return;
|
||||
|
||||
case CrowdPeerMarshaller.DELIVER_TELL:
|
||||
((CrowdPeerProvider)provider).deliverTell(
|
||||
source, (UserMessage)args[0], (Name)args[1], (ChatService.TellListener)args[2]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.peer.data.ClientInfo;
|
||||
import com.threerings.presents.peer.data.NodeObject;
|
||||
import com.threerings.presents.peer.server.PeerManager;
|
||||
import com.threerings.presents.peer.server.PeerNode;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.PresentsSession;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.chat.server.ChatProvider;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.peer.data.CrowdClientInfo;
|
||||
import com.threerings.crowd.peer.data.CrowdNodeObject;
|
||||
|
||||
/**
|
||||
* Extends the standard peer manager and bridges certain Crowd services.
|
||||
*/
|
||||
public abstract class CrowdPeerManager extends PeerManager
|
||||
implements CrowdPeerProvider, ChatProvider.ChatForwarder
|
||||
{
|
||||
/**
|
||||
* Creates an uninitialized peer manager.
|
||||
*/
|
||||
@Inject public CrowdPeerManager (Lifecycle cycle)
|
||||
{
|
||||
super(cycle);
|
||||
}
|
||||
|
||||
// from interface CrowdPeerProvider
|
||||
public void deliverTell (ClientObject caller, UserMessage message,
|
||||
Name target, ChatService.TellListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
// we just forward the message as if it originated on this server
|
||||
_chatprov.deliverTell(message, target, listener);
|
||||
}
|
||||
|
||||
// from interface CrowdPeerProvider
|
||||
public void deliverBroadcast (
|
||||
ClientObject caller, Name from, byte levelOrMode, String bundle, String msg)
|
||||
{
|
||||
// deliver the broadcast locally on this server
|
||||
_chatprov.broadcast(from, levelOrMode, bundle, msg, false);
|
||||
}
|
||||
|
||||
// from interface ChatProvider.ChatForwarder
|
||||
public boolean forwardTell (UserMessage message, Name target,
|
||||
ChatService.TellListener listener)
|
||||
{
|
||||
// look up their auth username from their visible name
|
||||
Name username = authFromViz(target);
|
||||
if (username == null) {
|
||||
return false; // sorry kid, don't know ya
|
||||
}
|
||||
|
||||
// look through our peers to see if the target user is online on one of them
|
||||
for (PeerNode peer : _peers.values()) {
|
||||
CrowdNodeObject cnobj = (CrowdNodeObject)peer.nodeobj;
|
||||
if (cnobj == null) {
|
||||
continue;
|
||||
}
|
||||
// we have to use auth username to look up their ClientInfo
|
||||
CrowdClientInfo cinfo = (CrowdClientInfo)cnobj.clients.get(username);
|
||||
if (cinfo != null) {
|
||||
cnobj.crowdPeerService.deliverTell(peer.getClient(), message, target, listener);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// from interface ChatProvider.ChatForwarder
|
||||
public void forwardBroadcast (Name from, byte levelOrMode, String bundle, String msg)
|
||||
{
|
||||
for (PeerNode peer : _peers.values()) {
|
||||
if (peer.nodeobj != null) {
|
||||
((CrowdNodeObject)peer.nodeobj).crowdPeerService.deliverBroadcast(
|
||||
peer.getClient(), from, levelOrMode, bundle, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override // from PeerManager
|
||||
public void shutdown ()
|
||||
{
|
||||
super.shutdown();
|
||||
|
||||
// unregister our invocation service
|
||||
if (_nodeobj != null) {
|
||||
_invmgr.clearDispatcher(((CrowdNodeObject)_nodeobj).crowdPeerService);
|
||||
}
|
||||
|
||||
// clear our chat forwarder registration
|
||||
_chatprov.setChatForwarder(null);
|
||||
}
|
||||
|
||||
@Override // from PeerManager
|
||||
protected NodeObject createNodeObject ()
|
||||
{
|
||||
return new CrowdNodeObject();
|
||||
}
|
||||
|
||||
@Override // from PeerManager
|
||||
protected ClientInfo createClientInfo ()
|
||||
{
|
||||
return new CrowdClientInfo();
|
||||
}
|
||||
|
||||
@Override // from PeerManager
|
||||
protected void initClientInfo (PresentsSession client, ClientInfo info)
|
||||
{
|
||||
super.initClientInfo(client, info);
|
||||
((CrowdClientInfo)info).visibleName =
|
||||
((BodyObject)client.getClientObject()).getVisibleName();
|
||||
}
|
||||
|
||||
@Override // from PeerManager
|
||||
protected void didInit ()
|
||||
{
|
||||
super.didInit();
|
||||
|
||||
// register and initialize our invocation service
|
||||
CrowdNodeObject cnobj = (CrowdNodeObject)_nodeobj;
|
||||
cnobj.setCrowdPeerService(_invmgr.registerDispatcher(new CrowdPeerDispatcher(this)));
|
||||
|
||||
// register ourselves as a chat forwarder
|
||||
_chatprov.setChatForwarder(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a visible name to an authentication name. If this method returns null, the chat
|
||||
* system will act as if the vizname in question is not online.
|
||||
*/
|
||||
protected abstract Name authFromViz (Name vizname);
|
||||
|
||||
@Inject protected ChatProvider _chatprov;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.peer.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatService;
|
||||
import com.threerings.crowd.chat.data.UserMessage;
|
||||
import com.threerings.crowd.peer.client.CrowdPeerService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
import com.threerings.util.Name;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link CrowdPeerService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from CrowdPeerService.java.")
|
||||
public interface CrowdPeerProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link CrowdPeerService#deliverBroadcast} request.
|
||||
*/
|
||||
void deliverBroadcast (ClientObject caller, Name arg1, byte arg2, String arg3, String arg4);
|
||||
|
||||
/**
|
||||
* Handles a {@link CrowdPeerService#deliverTell} request.
|
||||
*/
|
||||
void deliverTell (ClientObject caller, UserMessage arg1, Name arg2, ChatService.TellListener arg3)
|
||||
throws InvocationException;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.data.BodyMarshaller;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link BodyProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BodyService.java.")
|
||||
public class BodyDispatcher extends InvocationDispatcher<BodyMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public BodyDispatcher (BodyProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyMarshaller createMarshaller ()
|
||||
{
|
||||
return new BodyMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case BodyMarshaller.SET_IDLE:
|
||||
((BodyProvider)provider).setIdle(
|
||||
source, ((Boolean)args[0]).booleanValue()
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.presents.server.ClientLocal;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
|
||||
/**
|
||||
* Contains information tracked for each body by the server.
|
||||
*/
|
||||
public class BodyLocal extends ClientLocal
|
||||
{
|
||||
/**
|
||||
* The time at which the {@link BodyObject#status} field was last updated.
|
||||
*/
|
||||
public long statusTime;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.annotation.EventThread;
|
||||
import com.threerings.presents.server.ClientManager;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
|
||||
/**
|
||||
* Used to lookup {@link BodyObject} instances by name.
|
||||
*/
|
||||
@Singleton
|
||||
public class BodyLocator
|
||||
{
|
||||
/**
|
||||
* Returns the body object for the user with the specified visible name, or null if they are
|
||||
* not online.
|
||||
*/
|
||||
@EventThread
|
||||
public BodyObject lookupBody (Name visibleName)
|
||||
{
|
||||
// by default visibleName is username
|
||||
return (BodyObject)_clmgr.getClientObject(visibleName);
|
||||
}
|
||||
|
||||
@Inject protected ClientManager _clmgr;
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdCodes;
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Handles body related services.
|
||||
*/
|
||||
@Singleton
|
||||
public class BodyManager
|
||||
implements BodyProvider
|
||||
{
|
||||
/**
|
||||
* Constructs and initializes the body manager.
|
||||
*/
|
||||
@Inject public BodyManager (InvocationManager invmgr)
|
||||
{
|
||||
invmgr.registerDispatcher(new BodyDispatcher(this), CrowdCodes.CROWD_GROUP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the specified body's occupant info in the specified location, applies the supplied
|
||||
* occupant info operation to it and then broadcasts the updated info (assuming the occop
|
||||
* returned true indicating that an update was made).
|
||||
*/
|
||||
public <T extends OccupantInfo> boolean updateOccupantInfo (
|
||||
BodyObject body, OccupantInfo.Updater<T> updater)
|
||||
{
|
||||
PlaceManager pmgr = _plreg.getPlaceManager(body.getPlaceOid());
|
||||
return (pmgr == null) ? false : pmgr.updateOccupantInfo(body.getOid(), updater);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the connection status for the given body object's occupant info in the specified
|
||||
* location.
|
||||
*/
|
||||
public void updateOccupantStatus (BodyObject body, final byte status)
|
||||
{
|
||||
// no need to NOOP
|
||||
if (body.status != status) {
|
||||
// update the status in their body object
|
||||
body.setStatus(status);
|
||||
body.getLocal(BodyLocal.class).statusTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
updateOccupantInfo(body, new OccupantInfo.Updater<OccupantInfo>() {
|
||||
public boolean update (OccupantInfo info) {
|
||||
if (info.status == status) {
|
||||
return false;
|
||||
}
|
||||
info.status = status;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// from interface BodyProvider
|
||||
public void setIdle (ClientObject caller, boolean idle)
|
||||
{
|
||||
BodyObject bobj = (BodyObject)caller;
|
||||
|
||||
// determine the body's proposed new status
|
||||
byte nstatus = (idle) ? OccupantInfo.IDLE : OccupantInfo.ACTIVE;
|
||||
if (bobj.status == nstatus) {
|
||||
return; // ignore NOOP attempts
|
||||
}
|
||||
|
||||
// update their status!
|
||||
log.debug("Setting user idle state", "user", bobj.username, "status", nstatus);
|
||||
updateOccupantStatus(bobj, nstatus);
|
||||
}
|
||||
|
||||
/** Provides access to place managers. */
|
||||
@Inject protected PlaceRegistry _plreg;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.client.BodyService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link BodyService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from BodyService.java.")
|
||||
public interface BodyProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link BodyService#setIdle} request.
|
||||
*/
|
||||
void setIdle (ClientObject caller, boolean arg1);
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.PermissionPolicy;
|
||||
import com.threerings.presents.server.ClientLocal;
|
||||
import com.threerings.presents.server.ClientResolver;
|
||||
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdPermissionPolicy;
|
||||
|
||||
/**
|
||||
* Used to configure crowd-specific client object data.
|
||||
*/
|
||||
public class CrowdClientResolver extends ClientResolver
|
||||
{
|
||||
@Override // from ClientResolver
|
||||
public ClientObject createClientObject ()
|
||||
{
|
||||
return new BodyObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientLocal createLocalAttribute ()
|
||||
{
|
||||
return new BodyLocal();
|
||||
}
|
||||
|
||||
@Override // from ClientResolver
|
||||
public PermissionPolicy createPermissionPolicy ()
|
||||
{
|
||||
return new CrowdPermissionPolicy();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.AccessController;
|
||||
import com.threerings.presents.dobj.DEvent;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.ProxySubscriber;
|
||||
import com.threerings.presents.dobj.Subscriber;
|
||||
import com.threerings.presents.server.PresentsObjectAccess;
|
||||
|
||||
import com.threerings.bureau.data.BureauClientObject;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* Defines the various object access controllers used by the Crowd server.
|
||||
*/
|
||||
public class CrowdObjectAccess
|
||||
{
|
||||
/**
|
||||
* Provides access control for place objects. The default behavior is to allow place occupants
|
||||
* to subscribe to the place object and to use the {@link PresentsObjectAccess#DEFAULT}
|
||||
* modification policy.
|
||||
*/
|
||||
public static AccessController PLACE = new AccessController()
|
||||
{
|
||||
// documentation inherited from interface
|
||||
public boolean allowSubscribe (DObject object, Subscriber<?> sub)
|
||||
{
|
||||
if (sub instanceof ProxySubscriber) {
|
||||
ClientObject co = ((ProxySubscriber)sub).getClientObject();
|
||||
return ((PlaceObject)object).occupants.contains(co.getOid());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean allowDispatch (DObject object, DEvent event)
|
||||
{
|
||||
return PresentsObjectAccess.DEFAULT.allowDispatch(object, event);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extends the access control in {@link #PLACE} to allow Bureau clients to subscribe.
|
||||
*/
|
||||
public static AccessController BUREAU_ACCESS_PLACE = new AccessController() {
|
||||
public boolean allowSubscribe (DObject object, Subscriber<?> sub) {
|
||||
if (sub instanceof ProxySubscriber) {
|
||||
ClientObject co = ((ProxySubscriber)sub).getClientObject();
|
||||
if (co instanceof BureauClientObject) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return PLACE.allowSubscribe(object, sub);
|
||||
}
|
||||
public boolean allowDispatch (DObject object, DEvent event) {
|
||||
return PLACE.allowDispatch(object, event);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.threerings.util.Name;
|
||||
|
||||
import com.threerings.presents.net.AuthRequest;
|
||||
import com.threerings.presents.server.SessionFactory;
|
||||
import com.threerings.presents.server.ClientResolver;
|
||||
import com.threerings.presents.server.PresentsSession;
|
||||
import com.threerings.presents.server.PresentsServer;
|
||||
|
||||
import com.threerings.crowd.chat.server.ChatProvider;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Extends the Presents server configuring extensions for Crowd services.
|
||||
*/
|
||||
@Singleton
|
||||
public class CrowdServer extends PresentsServer
|
||||
{
|
||||
/** Configures dependencies needed by the Crowd services. */
|
||||
public static class Module extends PresentsServer.Module
|
||||
{
|
||||
@Override protected void configure () {
|
||||
super.configure();
|
||||
// nada (yet)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all of the server services and prepares for operation.
|
||||
*/
|
||||
@Override
|
||||
public void init (Injector injector)
|
||||
throws Exception
|
||||
{
|
||||
super.init(injector);
|
||||
|
||||
// configure the client manager to use our bits
|
||||
_clmgr.setDefaultSessionFactory(new SessionFactory() {
|
||||
@Override
|
||||
public Class<? extends PresentsSession> getSessionClass (AuthRequest areq) {
|
||||
return CrowdSession.class;
|
||||
}
|
||||
@Override
|
||||
public Class<? extends ClientResolver> getClientResolverClass (Name username) {
|
||||
return CrowdClientResolver.class;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main (String[] args)
|
||||
{
|
||||
Injector injector = Guice.createInjector(new Module());
|
||||
CrowdServer server = injector.getInstance(CrowdServer.class);
|
||||
try {
|
||||
server.init(injector);
|
||||
server.run();
|
||||
} catch (Exception e) {
|
||||
log.warning("Unable to initialize server.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Handles the creation and tracking of place managers. */
|
||||
@Inject protected PlaceRegistry _plreg;
|
||||
|
||||
/** Handles body-related invocation services. */
|
||||
@Inject protected BodyManager _bodyman;
|
||||
|
||||
/** Handles location-related invocation services. */
|
||||
@Inject protected LocationManager _locman;
|
||||
|
||||
/** Provides chat-related invocation services. */
|
||||
@Inject protected ChatProvider _chatprov;
|
||||
|
||||
/** The config key for our list of invocation provider mappings. */
|
||||
protected final static String PROVIDERS_KEY = "providers";
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
|
||||
import com.threerings.presents.server.PresentsSession;
|
||||
|
||||
import com.threerings.crowd.chat.server.SpeakUtil;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
|
||||
/**
|
||||
* Extends the presents session with crowd-specific session handling.
|
||||
*/
|
||||
public class CrowdSession extends PresentsSession
|
||||
{
|
||||
@Override
|
||||
protected void sessionConnectionClosed ()
|
||||
{
|
||||
super.sessionConnectionClosed();
|
||||
|
||||
if (_clobj != null) {
|
||||
// note that the user is disconnected
|
||||
BodyObject bobj = (BodyObject)_clobj;
|
||||
_bodyman.updateOccupantStatus(bobj, OccupantInfo.DISCONNECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sessionWillResume ()
|
||||
{
|
||||
super.sessionWillResume();
|
||||
|
||||
// note that the user's active once more
|
||||
BodyObject bobj = (BodyObject)_clobj;
|
||||
_bodyman.updateOccupantStatus(bobj, OccupantInfo.ACTIVE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sessionDidEnd ()
|
||||
{
|
||||
super.sessionDidEnd();
|
||||
|
||||
BodyObject body = (BodyObject)_clobj;
|
||||
|
||||
// clear out our location so that anyone listening will know that we've left
|
||||
clearLocation(body);
|
||||
|
||||
// reset our status in case this object remains around until they start their next session
|
||||
// (which could happen very soon)
|
||||
_bodyman.updateOccupantStatus(body, OccupantInfo.ACTIVE);
|
||||
|
||||
// clear our chat history
|
||||
if (body != null) {
|
||||
SpeakUtil.clearHistory(body.getVisibleName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the user ends their session, this method is called to clear out any location they might
|
||||
* occupy. The default implementation takes care of standard crowd location occupancy, but
|
||||
* users of other services may which to override this method and clear the user out of a scene,
|
||||
* zone or other location-derived occupancy.
|
||||
*/
|
||||
protected void clearLocation (BodyObject bobj)
|
||||
{
|
||||
_locman.leaveOccupiedPlace(bobj);
|
||||
}
|
||||
|
||||
@Inject protected BodyManager _bodyman;
|
||||
@Inject protected LocationManager _locman;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.client.LocationService;
|
||||
import com.threerings.crowd.data.LocationMarshaller;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
|
||||
/**
|
||||
* Dispatches requests to the {@link LocationProvider}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from LocationService.java.")
|
||||
public class LocationDispatcher extends InvocationDispatcher<LocationMarshaller>
|
||||
{
|
||||
/**
|
||||
* Creates a dispatcher that may be registered to dispatch invocation
|
||||
* service requests for the specified provider.
|
||||
*/
|
||||
public LocationDispatcher (LocationProvider provider)
|
||||
{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocationMarshaller createMarshaller ()
|
||||
{
|
||||
return new LocationMarshaller();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchRequest (
|
||||
ClientObject source, int methodId, Object[] args)
|
||||
throws InvocationException
|
||||
{
|
||||
switch (methodId) {
|
||||
case LocationMarshaller.LEAVE_PLACE:
|
||||
((LocationProvider)provider).leavePlace(
|
||||
source
|
||||
);
|
||||
return;
|
||||
|
||||
case LocationMarshaller.MOVE_TO:
|
||||
((LocationProvider)provider).moveTo(
|
||||
source, ((Integer)args[0]).intValue(), (LocationService.MoveListener)args[1]
|
||||
);
|
||||
return;
|
||||
|
||||
default:
|
||||
super.dispatchRequest(source, methodId, args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.server.ClientManager;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
import com.threerings.presents.server.PresentsSession;
|
||||
|
||||
import com.threerings.crowd.client.LocationService;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.CrowdCodes;
|
||||
import com.threerings.crowd.data.LocationCodes;
|
||||
import com.threerings.crowd.data.Place;
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* Handles location-related services.
|
||||
*/
|
||||
@Singleton
|
||||
public class LocationManager
|
||||
implements LocationProvider, LocationCodes
|
||||
{
|
||||
@Inject public LocationManager (InvocationManager invmgr)
|
||||
{
|
||||
invmgr.registerDispatcher(new LocationDispatcher(this), CrowdCodes.CROWD_GROUP);
|
||||
}
|
||||
|
||||
// from interface LocationProvider
|
||||
public void moveTo (ClientObject caller, int placeOid, LocationService.MoveListener listener)
|
||||
throws InvocationException
|
||||
{
|
||||
// do the move and send the response
|
||||
listener.moveSucceeded(moveTo((BodyObject)caller, placeOid));
|
||||
}
|
||||
|
||||
// from interface LocationProvider
|
||||
public void leavePlace (ClientObject caller)
|
||||
{
|
||||
leaveOccupiedPlace((BodyObject)caller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the specified body from whatever location they currently occupy to the location
|
||||
* identified by the supplied place oid.
|
||||
*
|
||||
* @return the config object for the new location.
|
||||
*
|
||||
* @exception InvocationException thrown if the move was not successful for some reason
|
||||
* (which will be communicated as an error code in the exception's message data).
|
||||
*/
|
||||
public PlaceConfig moveTo (BodyObject source, int placeOid)
|
||||
throws InvocationException
|
||||
{
|
||||
// make sure the place in question actually exists
|
||||
PlaceManager pmgr = _plreg.getPlaceManager(placeOid);
|
||||
if (pmgr == null) {
|
||||
log.info("Requested to move to non-existent place", "who", source.who(),
|
||||
"placeOid", placeOid);
|
||||
throw new InvocationException(NO_SUCH_PLACE);
|
||||
}
|
||||
|
||||
// if they're already in the location they're asking to move to, just give them the config
|
||||
// because we don't need to update anything in distributed object world
|
||||
Place place = pmgr.getLocation();
|
||||
if (place.equals(source.location)) {
|
||||
log.debug("Going along with client request to move to where they already are",
|
||||
"source", source.who(), "place", place);
|
||||
return pmgr.getConfig();
|
||||
}
|
||||
|
||||
// make sure they have access to the specified place
|
||||
String errmsg;
|
||||
if ((errmsg = pmgr.ratifyBodyEntry(source)) != null) {
|
||||
throw new InvocationException(errmsg);
|
||||
}
|
||||
|
||||
// acquire a lock on the body object to avoid breakage by rapid fire moveTo requests
|
||||
if (!source.acquireLock("moveToLock")) {
|
||||
// if we're still locked, a previous moveTo request hasn't been fully processed
|
||||
throw new InvocationException(MOVE_IN_PROGRESS);
|
||||
}
|
||||
|
||||
// configure the client accordingly if the place uses a custom class loader
|
||||
PresentsSession client = _clmgr.getClient(source.username);
|
||||
if (client != null) {
|
||||
client.setClassLoader(pmgr.getClass().getClassLoader());
|
||||
}
|
||||
|
||||
try {
|
||||
source.startTransaction();
|
||||
try {
|
||||
// remove them from any previous location
|
||||
leaveOccupiedPlace(source);
|
||||
|
||||
// let the place manager know that we're coming in
|
||||
pmgr.bodyWillEnter(source);
|
||||
|
||||
// let the body object know that it's going in
|
||||
source.willEnterPlace(place, pmgr.getPlaceObject());
|
||||
|
||||
} finally {
|
||||
source.commitTransaction();
|
||||
}
|
||||
|
||||
} finally {
|
||||
// and finally queue up an event to release the lock once these events are processed
|
||||
source.releaseLock("moveToLock");
|
||||
}
|
||||
|
||||
return pmgr.getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified body from the place object they currently occupy. Does nothing if the
|
||||
* body is not currently in a place.
|
||||
*/
|
||||
public void leaveOccupiedPlace (BodyObject source)
|
||||
{
|
||||
Place oldloc = source.location;
|
||||
if (oldloc == null) {
|
||||
return; // nothing to do if they weren't previously in some location
|
||||
}
|
||||
|
||||
PlaceManager pmgr = _plreg.getPlaceManager(oldloc.placeOid);
|
||||
if (pmgr == null) {
|
||||
log.warning("Body requested to leave no longer existent place?",
|
||||
"boid", source.getOid(), "place", oldloc);
|
||||
return;
|
||||
}
|
||||
|
||||
// tell the place manager that they're on the way out
|
||||
pmgr.bodyWillLeave(source);
|
||||
|
||||
// clear out their location
|
||||
source.didLeavePlace(pmgr.getPlaceObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcibly moves the specified body object to the new place. This is accomplished by first
|
||||
* removing the client from their old location and then sending the client a notification,
|
||||
* instructing it to move to the new location (which it does using the normal moveTo service).
|
||||
* This has the benefit that the client is removed from their old place regardless of whether
|
||||
* or not they are cooperating. If they choose to ignore the forced move request, they will
|
||||
* remain in limbo, unable to do much of anything.
|
||||
*/
|
||||
public void moveBody (BodyObject source, Place place)
|
||||
{
|
||||
// first remove them from their old place
|
||||
leaveOccupiedPlace(source);
|
||||
|
||||
// then send a forced move notification
|
||||
LocationSender.forcedMove(source, place.placeOid);
|
||||
}
|
||||
|
||||
@Inject protected RootDObjectManager _omgr;
|
||||
@Inject protected PlaceRegistry _plreg;
|
||||
@Inject protected ClientManager _clmgr;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import javax.annotation.Generated;
|
||||
|
||||
import com.threerings.crowd.client.LocationService;
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationProvider;
|
||||
|
||||
/**
|
||||
* Defines the server-side of the {@link LocationService}.
|
||||
*/
|
||||
@Generated(value={"com.threerings.presents.tools.GenServiceTask"},
|
||||
comments="Derived from LocationService.java.")
|
||||
public interface LocationProvider extends InvocationProvider
|
||||
{
|
||||
/**
|
||||
* Handles a {@link LocationService#leavePlace} request.
|
||||
*/
|
||||
void leavePlace (ClientObject caller);
|
||||
|
||||
/**
|
||||
* Handles a {@link LocationService#moveTo} request.
|
||||
*/
|
||||
void moveTo (ClientObject caller, int arg1, LocationService.MoveListener arg2)
|
||||
throws InvocationException;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.server.InvocationSender;
|
||||
|
||||
import com.threerings.crowd.client.LocationDecoder;
|
||||
import com.threerings.crowd.client.LocationReceiver;
|
||||
|
||||
/**
|
||||
* Used to issue notifications to a {@link LocationReceiver} instance on a
|
||||
* client.
|
||||
*/
|
||||
public class LocationSender extends InvocationSender
|
||||
{
|
||||
/**
|
||||
* Issues a notification that will result in a call to {@link
|
||||
* LocationReceiver#forcedMove} on a client.
|
||||
*/
|
||||
public static void forcedMove (
|
||||
ClientObject target, int arg1)
|
||||
{
|
||||
sendNotification(
|
||||
target, LocationDecoder.RECEIVER_CODE, LocationDecoder.FORCED_MOVE,
|
||||
new Object[] { Integer.valueOf(arg1) });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
|
||||
/**
|
||||
* An operation to be applied to all occupants in a location that may
|
||||
* contain occupants, e.g., a {@link PlaceManager}.
|
||||
*/
|
||||
public interface OccupantOp
|
||||
{
|
||||
/**
|
||||
* Called with the occupant info for each occupant in the location.
|
||||
*/
|
||||
void apply (OccupantInfo info);
|
||||
}
|
||||
@@ -0,0 +1,815 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.Interval;
|
||||
import com.samskivert.util.MethodFinder;
|
||||
import com.samskivert.util.StringUtil;
|
||||
|
||||
import com.threerings.presents.data.ClientObject;
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.AccessController;
|
||||
import com.threerings.presents.dobj.DObject;
|
||||
import com.threerings.presents.dobj.DSet;
|
||||
import com.threerings.presents.dobj.DynamicListener;
|
||||
import com.threerings.presents.dobj.EntryUpdatedEvent;
|
||||
import com.threerings.presents.dobj.MessageEvent;
|
||||
import com.threerings.presents.dobj.MessageListener;
|
||||
import com.threerings.presents.dobj.NamedSetAdapter;
|
||||
import com.threerings.presents.dobj.ObjectAddedEvent;
|
||||
import com.threerings.presents.dobj.ObjectDeathListener;
|
||||
import com.threerings.presents.dobj.ObjectDestroyedEvent;
|
||||
import com.threerings.presents.dobj.ObjectRemovedEvent;
|
||||
import com.threerings.presents.dobj.OidListListener;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.dobj.SetAdapter;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
|
||||
import com.threerings.crowd.chat.data.ChatCodes;
|
||||
import com.threerings.crowd.chat.server.SpeakDispatcher;
|
||||
import com.threerings.crowd.chat.server.SpeakHandler;
|
||||
import com.threerings.crowd.data.BodyObject;
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
import com.threerings.crowd.data.Place;
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* The place manager is the server-side entity that handles all place-related interaction. It
|
||||
* subscribes to the place object and reacts to message and other events. Behavior specific to a
|
||||
* place (or class of places) should live in the place manager. An intelligently constructed
|
||||
* hierarchy of place manager classes working in concert with invocation services should provide
|
||||
* the majority of the server-side functionality of an application built on the Presents platform.
|
||||
*
|
||||
* <p> The base place manager class takes care of the necessary interactions with the place
|
||||
* registry to manage place registration. It handles the place-related component of chatting. It
|
||||
* also provides the basis for place-based access control.
|
||||
*
|
||||
* <p> A derived class is expected to handle initialization, cleanup and operational functionality
|
||||
* via the calldown functions {@link #didInit}, {@link #didStartup}, and {@link #didShutdown} as
|
||||
* well as through event listeners.
|
||||
*/
|
||||
public class PlaceManager
|
||||
implements MessageListener, SpeakHandler.SpeakerValidator
|
||||
{
|
||||
/**
|
||||
* An interface used to allow the registration of standard message handlers to be invoked by
|
||||
* the place manager when particular types of message events are received.
|
||||
*
|
||||
* @deprecated Use dynamically bound methods instead. See {@link DynamicListener}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static interface MessageHandler
|
||||
{
|
||||
/**
|
||||
* Invokes this message handler on the supplied event.
|
||||
*
|
||||
* @param event the message event received.
|
||||
* @param pmgr the place manager for which the message is being handled.
|
||||
*/
|
||||
void handleEvent (MessageEvent event, PlaceManager pmgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to call methods on this place manager's delegates.
|
||||
*/
|
||||
public static abstract class DelegateOp
|
||||
{
|
||||
public DelegateOp (Class<? extends PlaceManagerDelegate> delegateClass) {
|
||||
_delegateClass = delegateClass;
|
||||
}
|
||||
|
||||
/** Applies an operation to the supplied delegate. */
|
||||
public abstract void apply (PlaceManagerDelegate delegate);
|
||||
|
||||
public boolean shouldApply (PlaceManagerDelegate delegate) {
|
||||
return _delegateClass.isInstance(delegate);
|
||||
}
|
||||
|
||||
protected Class<? extends PlaceManagerDelegate> _delegateClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a reference to our place configuration object.
|
||||
*/
|
||||
public PlaceConfig getConfig ()
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Place} instance that identifies this place.
|
||||
*/
|
||||
public Place getLocation ()
|
||||
{
|
||||
return new Place(_plobj.getOid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the place object managed by this place manager.
|
||||
*/
|
||||
public PlaceObject getPlaceObject ()
|
||||
{
|
||||
return _plobj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the supplied occupant operation to each occupant currently present in this place.
|
||||
*/
|
||||
public void applyToOccupants (OccupantOp op)
|
||||
{
|
||||
if (_plobj != null) {
|
||||
for (OccupantInfo info : _plobj.occupantInfo) {
|
||||
op.apply(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the supplied updater on the canonical occupant info record for the specified body
|
||||
* (which must be an occupant of this place) and broadcasts the update to all other occupants.
|
||||
*
|
||||
* @return true if the updater was called and the update sent, false if the body could not be
|
||||
* located (was not an occupant of this place) or the updater made no modifications.
|
||||
*
|
||||
* @exception ClassCastException thrown if the type of the supplied updater does not match the
|
||||
* type of {@link OccupantInfo} record used for the occupant. Caveat utilitor.
|
||||
*/
|
||||
public <T extends OccupantInfo> boolean updateOccupantInfo (
|
||||
int bodyOid, OccupantInfo.Updater<T> updater)
|
||||
{
|
||||
@SuppressWarnings("unchecked") T info = (T)_occInfo.get(bodyOid);
|
||||
if (info == null || !updater.update(info)) {
|
||||
return false;
|
||||
}
|
||||
// update the canonical copy
|
||||
_occInfo.put(info.getBodyOid(), info);
|
||||
// clone the canonical copy and send an event updating the distributed set with that clone
|
||||
_plobj.updateOccupantInfo(info.clone());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the place registry after creating this place manager.
|
||||
*/
|
||||
public void init (PlaceRegistry registry, InvocationManager invmgr, RootDObjectManager omgr,
|
||||
BodyLocator locator, PlaceConfig config)
|
||||
{
|
||||
_registry = registry;
|
||||
_invmgr = invmgr;
|
||||
_omgr = omgr;
|
||||
_locator = locator;
|
||||
_config = config;
|
||||
|
||||
// initialize our delegates
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.init(PlaceManager.this, _omgr, _invmgr);
|
||||
}
|
||||
});
|
||||
|
||||
// let derived classes do initialization stuff
|
||||
try {
|
||||
didInit();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Manager choked in didInit()", "where", where(), t);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the supplied delegate to the list for this manager.
|
||||
*/
|
||||
public void addDelegate (PlaceManagerDelegate delegate)
|
||||
{
|
||||
if (_delegates == null) {
|
||||
_delegates = Lists.newArrayList();
|
||||
}
|
||||
if (_omgr != null) {
|
||||
delegate.init(this, _omgr, _invmgr);
|
||||
delegate.didInit(_config);
|
||||
}
|
||||
_delegates.add(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the supplied operation to this manager's registered delegates.
|
||||
*/
|
||||
public void applyToDelegates (DelegateOp op)
|
||||
{
|
||||
if (_delegates != null) {
|
||||
for (int ii = 0, ll = _delegates.size(); ii < ll; ii++) {
|
||||
PlaceManagerDelegate delegate = _delegates.get(ii);
|
||||
if (op.shouldApply(delegate)) {
|
||||
op.apply(delegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity for place managers to ratify the creation of a place based on
|
||||
* whatever criterion they may require (based on information available to the manager at this
|
||||
* post-init() but pre-startup() phase of initialization).
|
||||
*
|
||||
* @return If a permissions check is to fail, the manager should return a translatable string
|
||||
* explaining the failure. <code>null</code> should be returned if initialization is to be
|
||||
* allowed to proceed.
|
||||
*/
|
||||
public String checkPermissions ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the place manager after the place object has been successfully created.
|
||||
*/
|
||||
public void startup (PlaceObject plobj)
|
||||
{
|
||||
// keep track of this
|
||||
_plobj = plobj;
|
||||
|
||||
// we usually want to create and register a speaker service instance that clients can use
|
||||
// to speak in this place
|
||||
if (shouldCreateSpeakService()) {
|
||||
plobj.setSpeakService(addDispatcher(new SpeakDispatcher(createSpeakHandler(plobj))));
|
||||
}
|
||||
|
||||
// we'll need to hear about place object events
|
||||
plobj.addListener(this);
|
||||
plobj.addListener(_bodyUpdater);
|
||||
plobj.addListener(_occListener);
|
||||
plobj.addListener(_deathListener);
|
||||
|
||||
// configure this place's access controller
|
||||
plobj.setAccessController(getAccessController());
|
||||
|
||||
// let our derived classes do their thang
|
||||
try {
|
||||
didStartup();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Manager choked in didStartup()", "where", where(), t);
|
||||
}
|
||||
|
||||
// since we start empty, we need to immediately assume shutdown
|
||||
checkShutdownInterval();
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes the place object being managed by this place manager to be destroyed and the place
|
||||
* manager to shut down.
|
||||
*/
|
||||
public void shutdown ()
|
||||
{
|
||||
// destroy the object and everything will follow from that
|
||||
_omgr.destroyObject(_plobj.getOid());
|
||||
|
||||
// make sure we don't have any shutdowner in the queue
|
||||
cancelShutdowner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity for the place manager to prevent bodies from entering.
|
||||
*
|
||||
* @return <code>null</code> if the body can enter, otherwise a translatable message explaining
|
||||
* the reason the body is blocked from entering
|
||||
*/
|
||||
public String ratifyBodyEntry (BodyObject body)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is called to inform the manager that a body is on the way in. This is called at the
|
||||
* very beginning of the entry process before the client is informed that it is allowed to
|
||||
* enter. This will be followed by a call to {@link #bodyEntered} once all events relating to
|
||||
* body entry have been processed.
|
||||
*/
|
||||
public void bodyWillEnter (BodyObject body)
|
||||
{
|
||||
// create a new occupant info instance and insert it into our canonical table
|
||||
OccupantInfo info = body.createOccupantInfo(_plobj);
|
||||
_occInfo.put(info.getBodyOid(), info);
|
||||
|
||||
_plobj.startTransaction();
|
||||
try {
|
||||
addOccupantInfo(body, info.clone());
|
||||
} finally {
|
||||
_plobj.commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to inform a manager that a body is about to leave this place. This will be followed
|
||||
* by a call to {@link #bodyLeft} once all events relating to body entry have been processed.
|
||||
*/
|
||||
public void bodyWillLeave (BodyObject body)
|
||||
{
|
||||
_plobj.startTransaction();
|
||||
try {
|
||||
// remove their occupant info (which is keyed on oid)
|
||||
_plobj.removeFromOccupantInfo(body.getOid());
|
||||
// and remove them from the occupant list
|
||||
_plobj.removeFromOccupants(body.getOid());
|
||||
} finally {
|
||||
_plobj.commitTransaction();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a particular message handler instance to be used when processing message events
|
||||
* with the specified name.
|
||||
*
|
||||
* @param name the message name of the message events that should be handled by this handler.
|
||||
* @param handler the handler to be registered.
|
||||
*
|
||||
* @deprecated Use dynamically bound methods instead. See {@link DynamicListener}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void registerMessageHandler (String name, MessageHandler handler)
|
||||
{
|
||||
// create our handler map if necessary
|
||||
if (_msghandlers == null) {
|
||||
_msghandlers = Maps.newHashMap();
|
||||
}
|
||||
_msghandlers.put(name, handler);
|
||||
}
|
||||
|
||||
// from interface MessageListener
|
||||
public void messageReceived (MessageEvent event)
|
||||
{
|
||||
if (_msghandlers != null) {
|
||||
MessageHandler handler = _msghandlers.get(event.getName());
|
||||
if (handler != null) {
|
||||
handler.handleEvent(event, this);
|
||||
}
|
||||
}
|
||||
|
||||
// If the message is directed at us, see if it's a request for a method invocation
|
||||
if (event.isPrivate()) { // aka if (event instanceof ServerMessageEvent)
|
||||
// the first argument should be the client object of the caller or null if it is
|
||||
// a server-originated event
|
||||
int srcoid = event.getSourceOid();
|
||||
DObject source = (srcoid <= 0) ? null : _omgr.getObject(srcoid);
|
||||
Object[] args = event.getArgs(), nargs;
|
||||
if (args == null) {
|
||||
nargs = new Object[] { source };
|
||||
} else {
|
||||
nargs = new Object[args.length+1];
|
||||
nargs[0] = source;
|
||||
System.arraycopy(args, 0, nargs, 1, args.length);
|
||||
}
|
||||
|
||||
// Lazily create our dispatcher now that it's actually getting a message
|
||||
if (_dispatcher == null) {
|
||||
Class<?> clazz = getClass();
|
||||
MethodFinder finder = _dispatcherFinders.get(clazz);
|
||||
if (finder == null) {
|
||||
finder = new MethodFinder(clazz);
|
||||
_dispatcherFinders.put(clazz, finder);
|
||||
}
|
||||
_dispatcher = new DynamicListener<DSet.Entry>(this, finder);
|
||||
}
|
||||
_dispatcher.dispatchMethod(event.getName(), nargs);
|
||||
}
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
public boolean isValidSpeaker (DObject speakObj, ClientObject speaker, byte mode)
|
||||
{
|
||||
// have a whitelist for valid modes (no broadcasting, that's done elsewhere)
|
||||
switch (mode) {
|
||||
default:
|
||||
return false;
|
||||
case ChatCodes.DEFAULT_MODE:
|
||||
case ChatCodes.THINK_MODE:
|
||||
case ChatCodes.EMOTE_MODE:
|
||||
case ChatCodes.SHOUT_MODE:
|
||||
break;
|
||||
}
|
||||
// only allow people in the room to speak.
|
||||
return _plobj.occupants.contains(speaker.getOid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that can be used in log messages to identify the place as sensibly as
|
||||
* possible to the developer who has to puzzle over log output trying to figure out what's
|
||||
* going on. Derived place managers can override this and augment the default value (which is
|
||||
* simply the place object id) with useful identifying information.
|
||||
*/
|
||||
public String where ()
|
||||
{
|
||||
return (_plobj == null) ? StringUtil.shortClassName(this) + ":-1" : _plobj.which();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string representation of this manager. Does so in a way that makes it easier for
|
||||
* derived classes to add to the string representation.
|
||||
*
|
||||
* @see #toString(StringBuilder)
|
||||
*/
|
||||
@Override
|
||||
public String toString ()
|
||||
{
|
||||
StringBuilder buf = new StringBuilder();
|
||||
buf.append("[");
|
||||
toString(buf);
|
||||
buf.append("]");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes will generally override this method to create a custom {@link PlaceObject}
|
||||
* derivation that contains extra information.
|
||||
*/
|
||||
protected PlaceObject createPlaceObject ()
|
||||
{
|
||||
try {
|
||||
return getPlaceObjectClass().newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #createPlaceObject}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Class<? extends PlaceObject> getPlaceObjectClass ()
|
||||
{
|
||||
return PlaceObject.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after this place manager has been initialized with its configuration information but
|
||||
* before it has been started up with its place object reference. Derived classes can override
|
||||
* this function and perform any basic initialization that they desire. They should of course
|
||||
* be sure to call <code>super.didInit()</code>.
|
||||
*/
|
||||
protected void didInit ()
|
||||
{
|
||||
// initialize our delegates
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.didInit(_config);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called if the permissions check failed, to give place managers a chance to do any cleanup
|
||||
* that might be necessary due to their early initialization or permissions checking code.
|
||||
*/
|
||||
protected void permissionsFailed ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if we should create a speaker service for our place object so that clients can
|
||||
* use it to speak in this place.
|
||||
*/
|
||||
protected boolean shouldCreateSpeakService ()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an access controller for this place's distributed object, which by default is {@link
|
||||
* CrowdObjectAccess#PLACE}.
|
||||
*/
|
||||
protected AccessController getAccessController ()
|
||||
{
|
||||
return CrowdObjectAccess.PLACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Derived classes should override this (and be sure to call <code>super.didStartup()</code>)
|
||||
* to perform any startup time initialization. The place object will be available by the time
|
||||
* this method is executed.
|
||||
*/
|
||||
protected void didStartup ()
|
||||
{
|
||||
// let our delegates know that we've started up
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.didStartup(_plobj);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this place has been destroyed and the place manager has shut down (via a call to
|
||||
* {@link #shutdown}). Derived classes can override this method and perform any necessary
|
||||
* shutdown time processing.
|
||||
*/
|
||||
protected void didShutdown ()
|
||||
{
|
||||
// clear out our listenership
|
||||
_plobj.removeListener(this);
|
||||
_plobj.removeListener(_bodyUpdater);
|
||||
_plobj.removeListener(_occListener);
|
||||
_plobj.removeListener(_deathListener);
|
||||
|
||||
// clear out our invocation service registrations
|
||||
for (InvocationMarshaller marsh : _marshallers) {
|
||||
_invmgr.clearDispatcher(marsh);
|
||||
}
|
||||
|
||||
// let our delegates know that we've shut down
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.didShutdown();
|
||||
}
|
||||
});
|
||||
|
||||
// if shutting down emptied the place and scheduled the shutdowner, clear that out
|
||||
cancelShutdowner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an invocation dispatcher and notes the registration such that it will be
|
||||
* automatically cleared when this manager shuts down.
|
||||
*/
|
||||
protected <T extends InvocationMarshaller> T addDispatcher (InvocationDispatcher<T> disp)
|
||||
{
|
||||
T marsh = _invmgr.registerDispatcher(disp);
|
||||
_marshallers.add(marsh);
|
||||
return marsh;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body object enters this place.
|
||||
*/
|
||||
protected void bodyEntered (final int bodyOid)
|
||||
{
|
||||
log.debug("Body entered", "where", where(), "oid", bodyOid);
|
||||
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.bodyEntered(bodyOid);
|
||||
}
|
||||
});
|
||||
|
||||
// if we were on the road to shutting down, step off
|
||||
cancelShutdowner();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body object leaves this place.
|
||||
*/
|
||||
protected void bodyLeft (final int bodyOid)
|
||||
{
|
||||
log.debug("Body left", "where", where(), "oid", bodyOid);
|
||||
|
||||
// if their occupant info hasn't been removed (which may be the case if they logged off
|
||||
// rather than left via a MoveTo request), we need to get it on out of here
|
||||
Integer key = Integer.valueOf(bodyOid);
|
||||
if (_plobj.occupantInfo.containsKey(key)) {
|
||||
_plobj.removeFromOccupantInfo(key);
|
||||
}
|
||||
|
||||
// clear out their canonical (local) occupant info record
|
||||
OccupantInfo leaver = _occInfo.remove(bodyOid);
|
||||
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.bodyLeft(bodyOid);
|
||||
}
|
||||
});
|
||||
|
||||
// if that leaves us with zero occupants, maybe do something
|
||||
if (shouldDeclareEmpty(leaver)) {
|
||||
placeBecameEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this occupant's info to the {@link PlaceObject}. This is called in a transaction on the
|
||||
* place object so if a derived class needs to add additional information for an occupant it
|
||||
* should override this method. It may opt to add the information before calling super if it
|
||||
* wishes to rely on its information being configured when {@link #bodyAdded} is called.
|
||||
*/
|
||||
protected void addOccupantInfo (BodyObject body, OccupantInfo info)
|
||||
{
|
||||
// clone the canonical copy and insert it into the DSet
|
||||
_plobj.addToOccupantInfo(info);
|
||||
|
||||
// add the body oid to our place object's occupant list
|
||||
_plobj.addToOccupants(body.getOid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the location should be marked as empty and potentially shutdown.
|
||||
*/
|
||||
protected boolean shouldDeclareEmpty (OccupantInfo leaver)
|
||||
{
|
||||
return (_plobj.occupants.size() == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body's occupant info is updated.
|
||||
*/
|
||||
protected void bodyUpdated (final OccupantInfo info)
|
||||
{
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.bodyUpdated(info);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when we transition from having bodies in the place to not having any bodies in the
|
||||
* place. Some places may take this as a sign to pack it in, others may wish to stick
|
||||
* around. In any case, they can override this method to do their thing.
|
||||
*/
|
||||
protected void placeBecameEmpty ()
|
||||
{
|
||||
// let our delegates know what's up
|
||||
applyToDelegates(new DelegateOp(PlaceManagerDelegate.class) {
|
||||
@Override
|
||||
public void apply (PlaceManagerDelegate delegate) {
|
||||
delegate.placeBecameEmpty();
|
||||
}
|
||||
});
|
||||
|
||||
// Log.info("Place became empty " + where() + ".");
|
||||
|
||||
checkShutdownInterval();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on startup and when the place is empty.
|
||||
*/
|
||||
protected void checkShutdownInterval ()
|
||||
{
|
||||
// queue up a shutdown interval, unless we've already got one.
|
||||
long idlePeriod = idleUnloadPeriod();
|
||||
if (idlePeriod > 0L && _shutdownInterval == null) {
|
||||
(_shutdownInterval = _omgr.newInterval(new Runnable() {
|
||||
public void run () {
|
||||
log.debug("Unloading idle place '" + where() + "'.");
|
||||
shutdown();
|
||||
}
|
||||
})).schedule(idlePeriod);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels any registered shutdown interval.
|
||||
*/
|
||||
protected void cancelShutdowner ()
|
||||
{
|
||||
if (_shutdownInterval != null) {
|
||||
_shutdownInterval.cancel();
|
||||
_shutdownInterval = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the period (in milliseconds) of emptiness after which this place manager will unload
|
||||
* itself and shutdown. Returning <code>0</code> indicates that the place should never be
|
||||
* shutdown.
|
||||
*/
|
||||
protected long idleUnloadPeriod ()
|
||||
{
|
||||
return 5 * 60 * 1000L;
|
||||
}
|
||||
|
||||
/**
|
||||
* An extensible way to add to the string representation of this class. Override this (being
|
||||
* sure to call super) and append your info to the buffer.
|
||||
*/
|
||||
protected void toString (StringBuilder buf)
|
||||
{
|
||||
buf.append("place=").append(_plobj);
|
||||
buf.append(", config=").append(_config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the speak handler for this place. Derived classes can customize the speak handler if
|
||||
* they so desire.
|
||||
*/
|
||||
protected SpeakHandler createSpeakHandler (PlaceObject plobj)
|
||||
{
|
||||
return new SpeakHandler(plobj, this);
|
||||
}
|
||||
|
||||
/** Listens for occupant updates. */
|
||||
protected SetAdapter<OccupantInfo> _bodyUpdater =
|
||||
new NamedSetAdapter<OccupantInfo>(PlaceObject.OCCUPANT_INFO) {
|
||||
@Override
|
||||
public void namedEntryUpdated (EntryUpdatedEvent<OccupantInfo> event) {
|
||||
bodyUpdated(event.getEntry());
|
||||
}
|
||||
};
|
||||
|
||||
/** Listens for body entry and departure. */
|
||||
protected OidListListener _occListener = new OidListListener() {
|
||||
public void objectAdded (ObjectAddedEvent event) {
|
||||
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
||||
bodyEntered(event.getOid());
|
||||
}
|
||||
}
|
||||
public void objectRemoved (ObjectRemovedEvent event) {
|
||||
if (event.getName().equals(PlaceObject.OCCUPANTS)) {
|
||||
bodyLeft(event.getOid());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Listens for death of our place object. */
|
||||
protected ObjectDeathListener _deathListener = new ObjectDeathListener() {
|
||||
public void objectDestroyed (ObjectDestroyedEvent event) {
|
||||
// unregister ourselves
|
||||
_registry.unmapPlaceManager(PlaceManager.this);
|
||||
|
||||
// let our derived classes and delegates shut themselves down
|
||||
try {
|
||||
didShutdown();
|
||||
} catch (Throwable t) {
|
||||
log.warning("Manager choked in didShutdown()", "where", where(), t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** A reference to the place registry with which we're registered. */
|
||||
protected PlaceRegistry _registry;
|
||||
|
||||
/** The invocation manager with whom we register our game invocation services. */
|
||||
protected InvocationManager _invmgr;
|
||||
|
||||
/** A distributed object manager for doing dobj stuff. */
|
||||
protected RootDObjectManager _omgr;
|
||||
|
||||
/** Used to look up body objects by name. */
|
||||
protected BodyLocator _locator;
|
||||
|
||||
/** A reference to the place object that we manage. */
|
||||
protected PlaceObject _plobj;
|
||||
|
||||
/** A reference to the configuration for our place. */
|
||||
protected PlaceConfig _config;
|
||||
|
||||
/** Message handlers are used to process message events. */
|
||||
protected Map<String, MessageHandler> _msghandlers;
|
||||
|
||||
/** A list of the delegates in use by this manager. */
|
||||
protected List<PlaceManagerDelegate> _delegates;
|
||||
|
||||
/** A list of services registered with {@link #addDispatcher} which will be automatically
|
||||
* cleared when this manager shuts down. */
|
||||
protected List<InvocationMarshaller> _marshallers = Lists.newArrayList();
|
||||
|
||||
/** Used to keep a canonical copy of the occupant info records. */
|
||||
protected HashIntMap<OccupantInfo> _occInfo = new HashIntMap<OccupantInfo>();
|
||||
|
||||
/** The interval currently registered to shut this place down after a certain period of
|
||||
* idility, or null if no interval is currently registered. */
|
||||
protected Interval _shutdownInterval;
|
||||
|
||||
/** Used to do method lookup magic when we receive message events. */
|
||||
protected DynamicListener<?> _dispatcher;
|
||||
|
||||
/** Maps from a PlaceManager subclass to a MethodFinder for it. When there are many many
|
||||
* instances of a PlaceManager in existence, having a MethodFinder instance for each gets quite
|
||||
* expensive. */
|
||||
protected static Map<Class<?>, MethodFinder> _dispatcherFinders = Maps.newHashMap();
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import com.threerings.presents.data.InvocationMarshaller;
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.server.InvocationDispatcher;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
|
||||
import com.threerings.crowd.data.OccupantInfo;
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
/**
|
||||
* Provides an extensible mechanism for encapsulating delegated functionality that works with the
|
||||
* place services.
|
||||
*
|
||||
* <p> Thanks to Java's lack of multiple inheritance, it will likely become necessary to factor
|
||||
* certain services that might be used by a variety of {@link PlaceManager} derived classes into
|
||||
* delegate classes because they do not fit into the single inheritance hierarchy that makes sense
|
||||
* for a particular application. To facilitate this process, this delegate class is provided which
|
||||
* the standard place manager can be made to call out to for all of the standard methods.
|
||||
*/
|
||||
public class PlaceManagerDelegate
|
||||
{
|
||||
/**
|
||||
* Called by the place manager when this delegate is registered with it. This will happen
|
||||
* before any calls to {@link #didInit}, etc.
|
||||
*/
|
||||
public void init (PlaceManager plmgr, RootDObjectManager omgr, InvocationManager invmgr)
|
||||
{
|
||||
_plmgr = plmgr;
|
||||
_omgr = omgr;
|
||||
_invmgr = invmgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the place manager is initialized.
|
||||
*/
|
||||
public void didInit (PlaceConfig config)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the place manager is started up.
|
||||
*/
|
||||
public void didStartup (PlaceObject plobj)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the place manager is shut down.
|
||||
*/
|
||||
public void didShutdown ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body enters the place.
|
||||
*/
|
||||
public void bodyEntered (int bodyOid)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body leaves the place.
|
||||
*/
|
||||
public void bodyLeft (int bodyOid)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a body occupant info is updated.
|
||||
*/
|
||||
public void bodyUpdated (OccupantInfo info)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the last body leaves the place.
|
||||
*/
|
||||
public void placeBecameEmpty ()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes {@link PlaceManager#where}.
|
||||
*/
|
||||
public String where ()
|
||||
{
|
||||
return _plmgr.where();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an invocation dispatcher and notes the registration such that it will be
|
||||
* automatically cleared when our parent manager shuts down.
|
||||
*/
|
||||
protected <T extends InvocationMarshaller> T addDispatcher (InvocationDispatcher<T> disp)
|
||||
{
|
||||
return _plmgr.addDispatcher(disp);
|
||||
}
|
||||
|
||||
/** A reference to the manager for which we are delegating. */
|
||||
protected PlaceManager _plmgr;
|
||||
|
||||
/** A reference to our distributed object manager. */
|
||||
protected RootDObjectManager _omgr;
|
||||
|
||||
/** A reference to our invocation manager. */
|
||||
protected InvocationManager _invmgr;
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.server;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Injector;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import com.samskivert.util.IntMap;
|
||||
import com.samskivert.util.IntMaps;
|
||||
import com.samskivert.util.Lifecycle;
|
||||
|
||||
import com.threerings.presents.dobj.RootDObjectManager;
|
||||
import com.threerings.presents.server.InvocationException;
|
||||
import com.threerings.presents.server.InvocationManager;
|
||||
|
||||
import com.threerings.crowd.data.PlaceConfig;
|
||||
import com.threerings.crowd.data.PlaceObject;
|
||||
|
||||
import static com.threerings.crowd.Log.log;
|
||||
|
||||
/**
|
||||
* The place registry keeps track of all of the active places in the server. It should be used to
|
||||
* create new places and it will take care of instantiating and initializing a place manager to
|
||||
* manage newly created places.
|
||||
*/
|
||||
@Singleton
|
||||
public class PlaceRegistry
|
||||
implements Lifecycle.ShutdownComponent
|
||||
{
|
||||
/** Used in conjunction with {@link PlaceRegistry#createPlace(PlaceConfig,PreStartupHook)}. */
|
||||
public static interface PreStartupHook
|
||||
{
|
||||
void invoke (PlaceManager plmgr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes the place registry. This is called by the server during its
|
||||
* initialization phase.
|
||||
*/
|
||||
@Inject public PlaceRegistry (Lifecycle cycle)
|
||||
{
|
||||
cycle.addComponent(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the place manager associated with the specified place object id or null if no such
|
||||
* place exists.
|
||||
*/
|
||||
public PlaceManager getPlaceManager (int placeOid)
|
||||
{
|
||||
return _pmgrs.get(placeOid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and registers a new place manager with no delegates.
|
||||
*
|
||||
* @see #createPlace(PlaceConfig,List)
|
||||
*/
|
||||
public PlaceManager createPlace (PlaceConfig config)
|
||||
throws InstantiationException, InvocationException
|
||||
{
|
||||
return createPlace(config, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and registers a new place manager along with the place object to be managed. The
|
||||
* registry takes care of tracking the creation of the object and informing the manager when it
|
||||
* is created.
|
||||
*
|
||||
* @param config the configuration object for the place to be created. The {@link PlaceManager}
|
||||
* derived class that should be instantiated to manage the place will be determined from the
|
||||
* config object.
|
||||
* @param delegates a list of {@link PlaceManagerDelegate} instances to be registered with the
|
||||
* manager prior to it being initialized and started up. <em>Note:</em> these delegates will
|
||||
* have dependencies injected into them prior to registering them with the manager.
|
||||
*
|
||||
* @return a reference to the place manager, which will have been configured with its place
|
||||
* object and started up (via a call to {@link PlaceManager#startup}.
|
||||
*
|
||||
* @exception InstantiationException thrown if an error occurs trying to instantiate and
|
||||
* initialize the place manager.
|
||||
* @exception InvocationException thrown if the place manager returns failure from the call to
|
||||
* {@link PlaceManager#checkPermissions}. The error string returned by that call will be
|
||||
* provided as in the exception.
|
||||
*/
|
||||
public PlaceManager createPlace (PlaceConfig config, List<PlaceManagerDelegate> delegates)
|
||||
throws InstantiationException, InvocationException
|
||||
{
|
||||
return createPlace(config, delegates, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't use this method, see {@link #createPlace(PlaceConfig)}.
|
||||
*
|
||||
* @param hook an optional pre-startup hook that allows a place manager to be configured prior
|
||||
* to having {@link PlaceManager#startup} called. This mainly exists because it used to be
|
||||
* possible to do such things. Try not to use this in new code.
|
||||
*/
|
||||
public PlaceManager createPlace (PlaceConfig config, PreStartupHook hook)
|
||||
throws InstantiationException, InvocationException
|
||||
{
|
||||
return createPlace(config, null, hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of all of the registered place objects. This should only be accessed
|
||||
* on the dobjmgr thread and shouldn't be kept around across event dispatches.
|
||||
*/
|
||||
public Iterator<PlaceObject> enumeratePlaces ()
|
||||
{
|
||||
final Iterator<PlaceManager> itr = _pmgrs.values().iterator();
|
||||
return new Iterator<PlaceObject>() {
|
||||
public boolean hasNext () {
|
||||
return itr.hasNext();
|
||||
}
|
||||
public PlaceObject next () {
|
||||
PlaceManager plmgr = itr.next();
|
||||
return (plmgr == null) ? null : plmgr.getPlaceObject();
|
||||
}
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an enumeration of all of the registered place managers. This should only be
|
||||
* accessed on the dobjmgr thread and shouldn't be kept around across event dispatches.
|
||||
*/
|
||||
public Iterator<PlaceManager> enumeratePlaceManagers ()
|
||||
{
|
||||
return _pmgrs.values().iterator();
|
||||
}
|
||||
|
||||
// from interface Lifecycle.ShutdownComponent
|
||||
public void shutdown ()
|
||||
{
|
||||
// shut down all active places
|
||||
for (Iterator<PlaceManager> iter = enumeratePlaceManagers(); iter.hasNext(); ) {
|
||||
PlaceManager pmgr = iter.next();
|
||||
try {
|
||||
pmgr.shutdown();
|
||||
} catch (Exception e) {
|
||||
log.warning("Place manager failed shutting down", "where", pmgr.where(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a place manager using the supplied config, injects dependencies into and registers
|
||||
* the supplied list of delegates, runs the supplied pre-startup hook and finally returns it.
|
||||
*/
|
||||
protected PlaceManager createPlace (PlaceConfig config, List<PlaceManagerDelegate> delegates,
|
||||
PreStartupHook hook)
|
||||
throws InstantiationException, InvocationException
|
||||
{
|
||||
PlaceManager pmgr = null;
|
||||
|
||||
try {
|
||||
// create a place manager using the class supplied in the place config
|
||||
pmgr = createPlaceManager(config);
|
||||
|
||||
// if we have delegates, inject their dependencies and add them
|
||||
if (delegates != null) {
|
||||
for (PlaceManagerDelegate delegate : delegates) {
|
||||
_injector.injectMembers(delegate);
|
||||
pmgr.addDelegate(delegate);
|
||||
}
|
||||
}
|
||||
|
||||
// let the pmgr know about us and its configuration
|
||||
pmgr.init(this, _invmgr, _omgr, selectLocator(config), config);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning(e);
|
||||
throw new InstantiationException("Error creating PlaceManager for " + config);
|
||||
}
|
||||
|
||||
// let the manager abort the whole process if it fails any permissions checks
|
||||
String errmsg = pmgr.checkPermissions();
|
||||
if (errmsg != null) {
|
||||
// give the place manager a chance to clean up after its early initialization process
|
||||
pmgr.permissionsFailed();
|
||||
throw new InvocationException(errmsg);
|
||||
}
|
||||
|
||||
// and create and register the place object
|
||||
PlaceObject plobj = pmgr.createPlaceObject();
|
||||
_omgr.registerObject(plobj);
|
||||
|
||||
// stick the manager into our table
|
||||
_pmgrs.put(plobj.getOid(), pmgr);
|
||||
|
||||
// start the place manager up with the newly created place object
|
||||
try {
|
||||
if (hook != null) {
|
||||
hook.invoke(pmgr);
|
||||
}
|
||||
pmgr.startup(plobj);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.warning("Error starting place manager", "obj", plobj, "pmgr", pmgr, e);
|
||||
}
|
||||
|
||||
return pmgr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of a {@link PlaceManager} using the information in the supplied place
|
||||
* config. Derived classes may wish to specialize this process for certain places for example
|
||||
* loading user supplied place management code from a special class loader that sandboxes their
|
||||
* code.
|
||||
*/
|
||||
protected PlaceManager createPlaceManager (PlaceConfig config)
|
||||
throws Exception
|
||||
{
|
||||
@SuppressWarnings("unchecked") Class<? extends PlaceManager> clazz =
|
||||
(Class<? extends PlaceManager>)Class.forName(config.getManagerClassName());
|
||||
return _injector.getInstance(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the body locator to be used by the PlaceManager created for the supplied config.
|
||||
*/
|
||||
protected BodyLocator selectLocator (PlaceConfig config)
|
||||
{
|
||||
return _locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the place manager when it has been shut down.
|
||||
*/
|
||||
protected void unmapPlaceManager (PlaceManager pmgr)
|
||||
{
|
||||
int ploid = pmgr.getPlaceObject().getOid();
|
||||
// remove it from the table
|
||||
if (_pmgrs.remove(ploid) == null) {
|
||||
log.warning("Requested to unmap unmapped place manager", "pmgr", pmgr);
|
||||
|
||||
// } else {
|
||||
// Log.info("Unmapped place manager [class=" + pmgr.getClass().getName() +
|
||||
// ", ploid=" + ploid + "].");
|
||||
}
|
||||
}
|
||||
|
||||
/** We use this to inject dependencies into place managers that we create. */
|
||||
@Inject protected Injector _injector;
|
||||
|
||||
/** The invocation manager with which we operate. */
|
||||
@Inject protected InvocationManager _invmgr;
|
||||
|
||||
/** The distributed object manager with which we operate. */
|
||||
@Inject protected RootDObjectManager _omgr;
|
||||
|
||||
/** Used to look body objects up by name. */
|
||||
@Inject protected BodyLocator _locator;
|
||||
|
||||
/** A mapping from place object id to place manager. */
|
||||
protected IntMap<PlaceManager> _pmgrs = IntMaps.newHashIntMap();
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// $Id$
|
||||
//
|
||||
// Narya library - tools for developing networked games
|
||||
// Copyright (C) 2002-2010 Three Rings Design, Inc., All Rights Reserved
|
||||
// http://code.google.com/p/narya/
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU Lesser General Public License as published
|
||||
// by the Free Software Foundation; either version 2.1 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This library 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
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
package com.threerings.crowd.util;
|
||||
|
||||
import com.threerings.util.MessageManager;
|
||||
|
||||
import com.threerings.presents.util.PresentsContext;
|
||||
|
||||
import com.threerings.crowd.chat.client.ChatDirector;
|
||||
import com.threerings.crowd.client.LocationDirector;
|
||||
import com.threerings.crowd.client.OccupantDirector;
|
||||
import com.threerings.crowd.client.PlaceView;
|
||||
|
||||
/**
|
||||
* The crowd context provides access to the various managers, etc. that
|
||||
* are needed by the crowd client code.
|
||||
*/
|
||||
public interface CrowdContext extends PresentsContext
|
||||
{
|
||||
/**
|
||||
* Returns a reference to the location director.
|
||||
*/
|
||||
LocationDirector getLocationDirector ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the occupant director.
|
||||
*/
|
||||
OccupantDirector getOccupantDirector ();
|
||||
|
||||
/**
|
||||
* Provides access to the chat director.
|
||||
*/
|
||||
ChatDirector getChatDirector ();
|
||||
|
||||
/**
|
||||
* Returns a reference to the message manager used by the client to generate localized
|
||||
* messages.
|
||||
*/
|
||||
MessageManager getMessageManager ();
|
||||
|
||||
/**
|
||||
* When the client enters a new place, the location director creates a
|
||||
* place controller which then creates a place view to visualize the
|
||||
* place for the user. The place view created by the place controller
|
||||
* will be passed to this function to actually display it in whatever
|
||||
* user interface is provided for the user. We don't require any
|
||||
* particular user interface toolkit, so it is expected that the place
|
||||
* view implementation will coordinate with the client implementation
|
||||
* so that the client can display the view provided by the place
|
||||
* controller.
|
||||
*
|
||||
* <p> Though the place view is created before we enter the place, it
|
||||
* won't be displayed (via a call to this function) until we have
|
||||
* fully entered the place and are ready for user interaction.
|
||||
*/
|
||||
void setPlaceView (PlaceView view);
|
||||
|
||||
/**
|
||||
* When the client leaves a place, the place controller will remove
|
||||
* any place view it set previously via {@link #setPlaceView} with a
|
||||
* call to this method.
|
||||
*/
|
||||
void clearPlaceView (PlaceView view);
|
||||
}
|
||||
Reference in New Issue
Block a user