The great invocation services rethink of 2002! Rearchitected the remote

method invocation services and converted everything to the new style.
Could this be my biggest checkin ever?


git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@1642 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
Michael Bayne
2002-08-14 19:08:01 +00:00
parent 4481c5f835
commit e54a4d41f4
161 changed files with 6083 additions and 2805 deletions
@@ -1,45 +0,0 @@
//
// $Id: ChatMessageHandler.java,v 1.6 2002/04/30 17:27:30 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.crowd.server.PlaceManager;
/**
* The chat message handler handles chat messages that are issued on a
* place object with the intention of speaking to everyone in that place.
*/
public class ChatMessageHandler implements PlaceManager.MessageHandler
{
/**
* Handles {@link ChatCodes#SPEAK_REQUEST} messages.
*/
public void handleEvent (MessageEvent event, PlaceManager pmgr)
{
// presently we do no ratification of chat messages, so we just
// generate a chat notification with the message and name of the
// speaker
int soid = event.getSourceOid();
BodyObject source = (BodyObject)CrowdServer.omgr.getObject(soid);
if (source == null) {
Log.info("Chatter went away. Dropping chat request " +
"[req=" + event + "].");
return;
}
// parse our incoming arguments
Object[] inargs = event.getArgs();
int reqid = ((Integer)inargs[0]).intValue();
String message = (String)inargs[1];
// and generate a chat notification
ChatProvider.sendChatMessage(
event.getTargetOid(), source.username, null, message);
}
}
@@ -0,0 +1,52 @@
//
// $Id: ChatDecoder.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.ChatReceiver;
import com.threerings.presents.client.InvocationDecoder;
/**
* Dispatches calls to a {@link ChatReceiver} instance.
*/
public class ChatDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "50d26fa68e407846184dc06b78db3e1d";
/** The method id used to dispatch {@link ChatReceiver#receivedTell}
* notifications. */
public static final int RECEIVED_TELL = 1;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public ChatDecoder (ChatReceiver receiver)
{
this.receiver = receiver;
}
// documentation inherited
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
// documentation inherited
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case RECEIVED_TELL:
((ChatReceiver)receiver).receivedTell(
(String)args[0], (String)args[1], (String)args[2]
);
return;
default:
super.dispatchNotification(methodId, args);
}
}
// Generated on 11:25:46 08/12/02.
}
@@ -1,5 +1,5 @@
//
// $Id: ChatDirector.java,v 1.32 2002/08/14 00:48:57 shaper Exp $
// $Id: ChatDirector.java,v 1.33 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
@@ -8,7 +8,6 @@ import java.util.Iterator;
import java.util.LinkedList;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.Tuple;
import com.threerings.presents.client.*;
import com.threerings.presents.dobj.*;
@@ -26,9 +25,8 @@ import com.threerings.crowd.util.CrowdContext;
* services. It handles both place constrained chat as well as direct
* messaging.
*/
public class ChatDirector
implements LocationObserver, MessageListener, InvocationReceiver,
ChatCodes
public class ChatDirector extends BasicDirector
implements ChatCodes, LocationObserver, MessageListener, ChatReceiver
{
/**
* An interface that can receive information about the {@link
@@ -50,6 +48,8 @@ public class ChatDirector
*/
public ChatDirector (CrowdContext ctx, MessageManager msgmgr, String bundle)
{
super(ctx);
// keep the context around
_ctx = ctx;
_msgmgr = msgmgr;
@@ -57,7 +57,7 @@ public class ChatDirector
// register for chat notifications
_ctx.getClient().getInvocationDirector().registerReceiver(
MODULE_NAME, this);
new ChatDecoder(this));
// register ourselves as a location observer
_ctx.getLocationDirector().addLocationObserver(this);
@@ -218,41 +218,55 @@ public class ChatDirector
}
/**
* Requests that a speak message be generated and delivered to all
* users that occupy the place object that we currently occupy.
*
* @param message the contents of the speak message.
*
* @return an id which can be used to coordinate this speak request
* with the response that will be delivered to all active chat
* displays when it arrives, or -1 if we were unable to make the
* request because we are not currently in a place.
* Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the
* place object that we currently occupy.
*/
public int requestSpeak (String message)
public void requestSpeak (String message)
{
requestSpeak(message, DEFAULT_MODE);
}
/**
* Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the
* place object that we currently occupy.
*/
public void requestSpeak (String message, byte mode)
{
// make sure we're currently in a place
if (_place == null) {
return -1;
}
// make sure they can say what they want to say
for (Iterator iter = _validators.iterator(); iter.hasNext(); ) {
if (!((ChatValidator) iter.next()).validateSpeak(message)) {
return -1;
}
return;
}
// dispatch a speak request on the active place object
int reqid =
_ctx.getClient().getInvocationDirector().nextInvocationId();
Object[] args = new Object[] { new Integer(reqid), message };
MessageEvent mevt = new MessageEvent(
_place.getOid(), SPEAK_REQUEST, args);
_ctx.getDObjectManager().postEvent(mevt);
// TODO: when this gets changed such that we actually validate
// this on the server, we have to make sure that the
// user is not on a portal before we allow the 'shout' to go through
return reqid;
requestSpeak(_place.speakService, message, mode);
}
/**
* Requests that a speak message with the specified mode be generated
* and delivered via the supplied speak service instance (which will
* be associated with a particular "speak object"). The message will
* first be validated by all registered {@link ChatValidator}s (and
* possibly vetoed) before being dispatched.
*
* @param speakService the speak service to use when generating the
* speak request.
* @param message the contents of the speak message.
* @param mode a speech mode that will be interpreted by the {@link
* ChatDisplay} implementations that eventually display this speak
* message.
*/
public void requestSpeak (
SpeakService speakService, String message, byte mode)
{
// make sure they can say what they want to say
for (Iterator iter = _validators.iterator(); iter.hasNext(); ) {
if (!((ChatValidator) iter.next()).validateSpeak(message)) {
return;
}
}
// dispatch a speak request using the supplied speak service
speakService.speak(_ctx.getClient(), message, mode);
}
/**
@@ -262,26 +276,34 @@ public class ChatDirector
* @param target the username of the user to which the tell message
* should be delivered.
* @param message the contents of the tell message.
*
* @return an id which can be used to coordinate this request with the
* tell response that will be delivered to all active chat displays
* when it arrives.
*/
public int requestTell (String target, String message)
public void requestTell (final String target, final String message)
{
// make sure they can say what they want to say
for (Iterator iter = _validators.iterator(); iter.hasNext(); ) {
if (!((ChatValidator) iter.next()).validateTell(
target, message)) {
return -1;
return;
}
}
int invid = ChatService.tell(_ctx.getClient(), target, message, this);
// cache the tell info for use when reporting success or failure
// to our various chat displays
_tells.put(invid, new Tuple(target, message));
return invid;
// create a listener that will report success or failure
ChatService.TellListener listener = new ChatService.TellListener() {
public void tellSucceeded () {
String msg = MessageBundle.tcompose(
"m.told_format", target, message);
displayFeedbackMessage(_bundle, msg);
addChatter(target);
}
public void requestFailed (String reason) {
String msg =
MessageBundle.compose("m.tell_failed", target, reason);
displayFeedbackMessage(_bundle, msg);
}
};
_cservice.tell(_ctx.getClient(), target, message, listener);
}
/**
@@ -308,6 +330,13 @@ public class ChatDirector
_auxes.remove(source.getOid());
}
// documentation inherited from interface
protected void fetchServices (Client client)
{
// get a handle on our chat service
_cservice = (ChatService)client.requireService(ChatService.class);
}
// documentation inherited
public boolean locationMayChange (int placeId)
{
@@ -340,88 +369,33 @@ public class ChatDirector
public void messageReceived (MessageEvent event)
{
String name = event.getName();
if (name.equals(ChatService.SPEAK_NOTIFICATION)) {
if (name.equals(SPEAK_NOTIFICATION)) {
handleSpeakMessage(
getLocalType(event.getTargetOid()), event.getArgs());
} else if (name.equals(ChatService.SYSTEM_NOTIFICATION)) {
} else if (name.equals(SYSTEM_NOTIFICATION)) {
handleSystemMessage(
getLocalType(event.getTargetOid()), event.getArgs());
}
}
/**
* Called by the invocation director when another client has requested
* a tell message be delivered to this client.
*/
public void handleTellNotification (String source, String message)
// documentation inherited from interface
public void receivedTell (String speaker, String bundle, String message)
{
// bail if the speaker is blocked
if (isBlocked(source)) {
// ignore messages from blocked users
if (isBlocked(speaker)) {
return;
}
// if the message need be translated, do so
if (bundle != null) {
message = xlate(bundle, message);
}
UserMessage um = new UserMessage(
message, ChatCodes.TELL_CHAT_TYPE, source, ChatCodes.DEFAULT_MODE);
message, ChatCodes.TELL_CHAT_TYPE, speaker, ChatCodes.DEFAULT_MODE);
dispatchMessage(um);
addChatter(source);
}
/**
* Called by the invocation director when an entity on the server has
* requested that we deliver a tell notification to this
* client. Because the server generated the notification, displays
* will want to translate the message itself using the supplied bundle
* identifier.
*/
public void handleTellNotification (
String source, String bundle, String message)
{
handleTellNotification(source, xlate(bundle, message));
}
/**
* Called in response to a tell request that succeeded.
*
* @param invid the invocation id of the tell request.
*/
public void handleTellSucceeded (int invid)
{
// remove the tell info for the successful request
Tuple tup = (Tuple)_tells.remove(invid);
if (tup == null) {
Log.warning("Notified of successful tell request but no " +
"tell info available [invid=" + invid + "].");
return;
}
// pass this on to our chat displays
String target = (String)tup.left, message = (String)tup.right;
displayFeedbackMessage(
_bundle, MessageBundle.tcompose("m.told_format", target, message));
addChatter(target);
}
/**
* Called in response to a tell request that failed.
*
* @param invid the invocation id of the tell request.
* @param reason the code that describes the reason for failure.
*/
public void handleTellFailed (int invid, String reason)
{
// remove the tell info for the failed request
Tuple tup = (Tuple)_tells.remove(invid);
if (tup == null) {
Log.warning("Notified of failed tell request but no " +
"tell info available [invid=" + invid + "].");
return;
}
// pass this on to our chat displays
String target = (String)tup.left;
displayFeedbackMessage(
_bundle, MessageBundle.compose("m.tell_failed", target, reason));
addChatter(speaker);
}
/**
@@ -519,9 +493,20 @@ public class ChatDirector
return (_muter != null) && _muter.isMuted(username);
}
/**
* Used to assign unique ids to all speak requests.
*/
protected synchronized int nextRequestId ()
{
return _requestId++;
}
/** Our active chat context. */
protected CrowdContext _ctx;
/** Provides access to chat-related server-side services. */
protected ChatService _cservice;
/** The message manager. */
protected MessageManager _msgmgr;
@@ -544,16 +529,15 @@ public class ChatDirector
/** An optionally present mutelist director. */
protected MuteDirector _muter;
/** A cache of the target and message text associated with outstanding
* tell chat requests. */
protected HashIntMap _tells = new HashIntMap();
/** Usernames of users we've recently chatted with. */
protected LinkedList _chatters = new LinkedList();
/** Observers that are watching our chatters list. */
protected ArrayList _chatterObservers = new ArrayList();
/** Used by {@link #nextRequestId}. */
protected int _requestId;
/** The maximum number of chatter usernames to track. */
protected static final int MAX_CHATTERS = 6;
}
@@ -0,0 +1,30 @@
//
// $Id: ChatReceiver.java,v 1.3 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.presents.client.InvocationReceiver;
/**
* Defines, for the chat services, a set of notifications delivered
* asynchronously by the server to the client.
*/
public interface ChatReceiver extends InvocationReceiver
{
/**
* Called when a tell message is received from another player on the
* server.
*
* @param speaker the username of the user from which this message
* originated.
* @param bundle if non-null, a bundle that should be used to
* translate the text of the tell message. This is generally only used
* when some server entity originates the tell message rather than
* another user. The server entity might then wish for its tell
* message to be translated into a language appropriate for the
* receiver. Such luxuries are not available for human to human
* conversation, alas.
* @param message the text of the tell message.
*/
public void receivedTell (String speaker, String bundle, String message);
}
@@ -1,22 +1,31 @@
//
// $Id: ChatService.java,v 1.7 2002/05/15 23:54:34 mdb Exp $
// $Id: ChatService.java,v 1.8 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.crowd.Log;
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 chat director.
*
* @see ChatDirector
* used directly, but instead should be accessed via the {@link
* ChatDirector}.
*/
public class ChatService implements ChatCodes
public interface ChatService extends InvocationService
{
/**
* Used to communicate the response to a {@link #tell} request.
*/
public static interface TellListener extends InvocationListener
{
/**
* Communicates the response to a {@link #tell} request.
*/
public void tellSucceeded ();
}
/**
* Requests that a tell message be delivered to the user with username
* equal to <code>target</code>.
@@ -25,18 +34,8 @@ public class ChatService implements ChatCodes
* @param target the username of the user to which the tell message
* should be delivered.
* @param message the contents of the message.
* @param rsptarget the chat director reference that will receive the
* tell response.
*
* @return the invocation request id of the generated tell request.
* @param listener the reference that will receive the tell response.
*/
public static int tell (Client client, String target, String message,
ChatDirector rsptarget)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { target, message };
Log.debug("Sending tell request [tgt=" + target +
", msg=" + message + "].");
return invdir.invoke(MODULE_NAME, TELL_REQUEST, args, rsptarget);
}
public void tell (Client client, String target, String message,
TellListener listener);
}
@@ -0,0 +1,27 @@
//
// $Id: SpeakService.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
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.
*/
public void speak (Client client, String message, byte mode);
}
@@ -1,5 +1,5 @@
//
// $Id: ChatCodes.java,v 1.10 2002/07/26 20:35:01 ray Exp $
// $Id: ChatCodes.java,v 1.11 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
@@ -10,19 +10,13 @@ import com.threerings.presents.data.InvocationCodes;
*/
public interface ChatCodes extends InvocationCodes
{
/** The module name for the chat services. */
public static final String MODULE_NAME = "chat";
/** 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
/** 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 message identifier for a speak request message. */
public static final String SPEAK_REQUEST = "spkreq";
/** The message identifier for a speak notification message. */
public static final String SPEAK_NOTIFICATION = "spknot";
@@ -32,31 +26,16 @@ public interface ChatCodes extends InvocationCodes
/** The chat localtype for tells. */
public static final String TELL_CHAT_TYPE = "tellChat";
/** The message identifier for a tell request. */
public static final String TELL_REQUEST = "Tell";
/** The response identifier for a successful tell request. This is
* mapped by the invocation services to a call to {@link
* ChatDirector#handleTellSucceeded}. */
public static final String TELL_SUCCEEDED_RESPONSE = "TellSucceeded";
/** The response identifier for a failed tell request. This is mapped
* by the invocation services to a call to {@link
* ChatDirector#handleTellFailed}. */
public static final String TELL_FAILED_RESPONSE = "TellFailed";
/** The message identifier for a tell notification. */
public static final String TELL_NOTIFICATION = "Tell";
/** The default mode used by speak requests. */
/** The default mode used by {@link SpeakService#speak} requests. */
public static final byte DEFAULT_MODE = 0;
/** The think mode to indicate that the user is thinking
* what they're saying, or is it that they're saying what they're
* thinking? */
/** 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;
/** The mode to indicate that a speak is actually an emote. */
/** A {@link SpeakService#speak} mode to indicate that a speak is
* actually an emote. */
public static final byte EMOTE_MODE = 2;
/** An error code delivered when the user targeted for a tell
@@ -0,0 +1,67 @@
//
// $Id: ChatMarshaller.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.ChatService;
import com.threerings.crowd.chat.ChatService.TellListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* 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.
*/
public class ChatMarshaller extends InvocationMarshaller
implements ChatService
{
// documentation inherited
public static class TellMarshaller extends ListenerMarshaller
implements TellListener
{
/** The method id used to dispatch {@link #tellSucceeded}
* responses. */
public static final int TELL_SUCCEEDED = 0;
// documentation inherited from interface
public void tellSucceeded ()
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, TELL_SUCCEEDED,
new Object[] { }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case TELL_SUCCEEDED:
((TellListener)listener).tellSucceeded(
);
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
/** The method id used to dispatch {@link #tell} requests. */
public static final int TELL = 1;
// documentation inherited from interface
public void tell (Client arg1, String arg2, String arg3, TellListener arg4)
{
TellMarshaller listener4 = new TellMarshaller();
listener4.listener = arg4;
sendRequest(arg1, TELL, new Object[] {
arg2, arg3, listener4
});
}
// Class file generated on 00:25:59 08/11/02.
}
@@ -0,0 +1,33 @@
//
// $Id: SpeakMarshaller.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.SpeakService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* 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.
*/
public class SpeakMarshaller extends InvocationMarshaller
implements SpeakService
{
/** The method id used to dispatch {@link #speak} requests. */
public static final int SPEAK = 1;
// documentation inherited from interface
public void speak (Client arg1, String arg2, byte arg3)
{
sendRequest(arg1, SPEAK, new Object[] {
arg2, new Byte(arg3)
});
}
// Class file generated on 19:01:34 08/12/02.
}
@@ -0,0 +1,52 @@
//
// $Id: ChatDispatcher.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.ChatMarshaller;
import com.threerings.crowd.chat.ChatService;
import com.threerings.crowd.chat.ChatService.TellListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
/**
* Dispatches requests to the {@link ChatProvider}.
*/
public class ChatDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public ChatDispatcher (ChatProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new ChatMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case ChatMarshaller.TELL:
((ChatProvider)provider).tell(
source,
(String)args[0], (String)args[1], (TellListener)args[2]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,50 +1,70 @@
//
// $Id: ChatProvider.java,v 1.12 2002/07/22 22:54:03 ray Exp $
// $Id: ChatProvider.java,v 1.13 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.crowd.Log;
import com.threerings.crowd.chat.ChatService.TellListener;
/**
* The chat provider handles the server side of the chat-related
* invocation services.
*/
public class ChatProvider
extends InvocationProvider implements ChatCodes
implements ChatCodes, InvocationProvider
{
/**
* Initializes the chat services and registers a chat provider with
* the invocation manager.
*/
public static void init (InvocationManager invmgr, DObjectManager omgr)
{
_omgr = omgr;
// register a chat provider with the invocation manager
invmgr.registerDispatcher(new ChatDispatcher(
new ChatProvider()), true);
}
/**
* Processes a request from a client to deliver a tell message to
* another client.
*/
public void handleTellRequest (
BodyObject source, int invid, String target, String message)
public void tell (ClientObject caller, String target, String message,
TellListener listener)
throws InvocationException
{
// look up the target body object
// make sure the target user is online
BodyObject tobj = CrowdServer.lookupBody(target);
if (tobj == null) {
sendResponse(source, invid, TELL_FAILED_RESPONSE,
USER_NOT_ONLINE);
} else {
// deliver a tell notification to the target player
sendTellMessage(tobj.getOid(), source.username, null, message);
// let the teller know it went ok
sendResponse(source, invid, TELL_SUCCEEDED_RESPONSE);
throw new InvocationException(USER_NOT_ONLINE);
}
// deliver a tell notification to the target player
BodyObject source = (BodyObject)caller;
sendTellMessage(tobj, source.username, null, message);
// let the teller know it went ok
listener.tellSucceeded();
}
/**
* Delivers a tell notification to the specified target player,
* originating with the specified speaker.
*
* @param targetOid the body object id of the user that will receive
* the tell message.
* @param target the body object of the user that will receive the
* tell message.
* @param speaker the username of the user that generated the message
* (or some special speaker name for server messages).
* @param bundle the bundle identifier that will be used by the client
@@ -54,89 +74,11 @@ public class ChatProvider
* @param message the text of the chat message.
*/
public static void sendTellMessage (
int targetOid, String speaker, String bundle, String message)
BodyObject target, String speaker, String bundle, String message)
{
Object[] args = null;
if (bundle == null) {
args = new Object[] { speaker, message };
} else {
args = new Object[] { speaker, bundle, message };
}
CrowdServer.invmgr.sendNotification(
targetOid, MODULE_NAME, TELL_NOTIFICATION, args);
ChatSender.sendTell(target, speaker, bundle, message);
}
/**
* Sends a chat 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 placeOid the place to which to deliver the chat 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 chat message.
*/
public static void sendChatMessage (
int placeOid, String speaker, String bundle, String message)
{
sendChatMessage(
placeOid, speaker, bundle, message, ChatCodes.DEFAULT_MODE);
}
/**
* Sends a chat 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 placeOid the place to which to deliver the chat 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 chat message.
* @param mode the mode of the message, @see ChatCodes.DEFAULT_MODE
*/
public static void sendChatMessage (
int placeOid, String speaker, String bundle, String message,
byte mode)
{
Object[] outargs = null;
if (bundle == null) {
outargs = new Object[] { speaker, message, new Byte(mode) };
} else {
outargs = new Object[] { speaker, bundle, message, new Byte(mode) };
}
MessageEvent nevt = new MessageEvent(
placeOid, ChatService.SPEAK_NOTIFICATION, outargs);
CrowdServer.omgr.postEvent(nevt);
}
/**
* Sends a system message notification to the specified place object
* with the supplied message content. A system message is one that
* will be rendered where the chat messages are rendered, but in a way
* that makes it clear that it is a message from the server.
*
* @param placeOid the place to 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 sendSystemMessage (
int placeOid, String bundle, String message)
{
Object[] outargs = new Object[] { bundle, message };
MessageEvent nevt = new MessageEvent(
placeOid, ChatService.SYSTEM_NOTIFICATION, outargs);
CrowdServer.omgr.postEvent(nevt);
}
/** The distributed object manager used by the chat services. */
protected static DObjectManager _omgr;
}
@@ -0,0 +1,30 @@
//
// $Id: ChatSender.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.ChatDecoder;
import com.threerings.crowd.chat.ChatReceiver;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
/**
* Used to issue notifications to a {@link ChatReceiver} instance on a
* client.
*/
public class ChatSender extends InvocationSender
{
/**
* Issues a notification that will result in a call to {@link
* ChatReceiver#receivedTell} on a client.
*/
public static void sendTell (
ClientObject target, String arg1, String arg2, String arg3)
{
sendNotification(
target, ChatDecoder.RECEIVER_CODE, ChatDecoder.RECEIVED_TELL,
new Object[] { arg1, arg2, arg3 });
}
// Generated on 11:25:46 08/12/02.
}
@@ -0,0 +1,51 @@
//
// $Id: SpeakDispatcher.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.crowd.chat.SpeakMarshaller;
import com.threerings.crowd.chat.SpeakService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.server.InvocationDispatcher;
import com.threerings.presents.server.InvocationException;
/**
* Dispatches requests to the {@link SpeakProvider}.
*/
public class SpeakDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public SpeakDispatcher (SpeakProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new SpeakMarshaller();
}
// documentation inherited
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);
}
}
}
@@ -0,0 +1,149 @@
//
// $Id: SpeakProvider.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.chat;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject;
/**
* Provides the back-end of the chat speaking facilities. A server entity
* can make "speech" available among the subscribers of a particular
* distributed object by constructing a speak provider and registering it
* with the invocation manager, 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 SpeakProvider
implements InvocationProvider, ChatCodes
{
/**
* 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.
*/
public boolean isValidSpeaker (DObject speakObj, ClientObject speaker);
}
/**
* Creates a speak provider 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 SpeakProvider (DObject speakObj, SpeakerValidator validator)
{
_speakObj = speakObj;
_validator = validator;
}
/**
* Handles a {@link SpeakService#speak} request.
*/
public void speak (ClientObject caller, String message, byte mode)
{
// ensure that the speaker is valid
if (!_validator.isValidSpeaker(_speakObj, caller)) {
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
sendSpeak(_speakObj, ((BodyObject)caller).username,
null, message, 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.
*/
public static void sendSpeak (DObject speakObj, String 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, String speaker,
String bundle, String message, byte mode)
{
Object[] outargs = null;
if (bundle == null) {
outargs = new Object[] { speaker, message, new Byte(mode) };
} else {
outargs = new Object[] { speaker, bundle, message, new Byte(mode) };
}
speakObj.postEvent(
new MessageEvent(speakObj.getOid(), SPEAK_NOTIFICATION, outargs));
}
/**
* Sends a system 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.
*
* @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 sendSystemSpeak (
DObject speakObj, String bundle, String message)
{
speakObj.postEvent(
new MessageEvent(speakObj.getOid(), SYSTEM_NOTIFICATION,
new Object[] { bundle, message }));
}
/** Our speech object. */
protected DObject _speakObj;
/** The entity that will validate our speakers. */
protected SpeakerValidator _validator;
}