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
+12 -6
View File
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
# $Id: genreceiver,v 1.1 2002/08/11 05:53:13 mdb Exp $
# $Id: genreceiver,v 1.2 2002/08/14 19:07:48 mdb Exp $
#
# This script is used to generate invocation receiver marshaller and
# unmarshaller classes based on receiver interface definitions.
@@ -10,6 +10,7 @@ use Getopt::Long;
use Template;
use File::Basename;
use POSIX qw(strftime);
use MD5;
# use Dumpvalue;
@@ -84,14 +85,14 @@ while ($srcfile = shift) {
open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n";
while (<JPO>) {
if (/^public interface (\S+) extends (\S+)/) {
if (/^public interface (\S+) extends ([^, ]+)/) {
$ifcname = $1;
$imports{$ifcname} = 1;
my $sname = $2;
# if this interface does not extend InvocationReceiver, we
# don't want to do nothin'
die "$script: Receiver interface does not extend " .
die "$script: $ifcname does not extend " .
"InvocationReceiver\n (extends $sname).\n"
if ($sname ne $ISVC_CNAME);
@@ -156,7 +157,8 @@ while ($srcfile = shift) {
# create a template processor
my $ttconfig = {
RELATIVE => 1
RELATIVE => 1,
ABSOLUTE => 1,
};
my $tt = Template->new($ttconfig);
my $genstamp = strftime("%T %D", localtime);
@@ -207,7 +209,7 @@ while ($srcfile = shift) {
my @dimportsl = sort keys %dimports;
my %ddata = ( "imports" => \@dimportsl,
"name" => $svcname,
"receiver_id" => 0, # TODO: do something here
"receiver_code" => MD5->hexhash($ifcname),
"package" => $dcdrpkg,
"methods" => \@methods,
"genstamp" => $genstamp,
@@ -232,8 +234,12 @@ sub grab_imports {
# skip primitive types
next unless ($class =~ m/\./);
# trim array delimiters
my $tclass = $class;
$tclass =~ s:[\[\]]::g;
# stuff the class into our import table
$imports{$class} = 1;
$imports{$tclass} = 1;
}
}
+14 -9
View File
@@ -1,6 +1,6 @@
#!/usr/bin/perl -w
#
# $Id: genservice,v 1.2 2002/08/11 05:53:29 mdb Exp $
# $Id: genservice,v 1.3 2002/08/14 19:07:48 mdb Exp $
#
# This script is used to generate invocation service marshaller and
# unmarshaller classes based on invocation service interface definitions.
@@ -87,14 +87,14 @@ while ($srcfile = shift) {
open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n";
while (<JPO>) {
if (/^public interface (\S+) extends (\S+)/) {
if (/^public interface (\S+) extends ([^, ]+)/) {
$ifcname = $1;
$imports{$ifcname} = 1;
my $sname = $2;
# if this interface does not extend InvocationService, we
# don't want to do nothin'
die "$script: Service interface does not extend " .
die "$script: $ifcname does not extend " .
"InvocationService\n (extends $sname).\n"
if ($sname ne $ISVC_CNAME);
@@ -203,7 +203,8 @@ while ($srcfile = shift) {
# create a template processor
my $ttconfig = {
RELATIVE => 1
RELATIVE => 1,
ABSOLUTE => 1,
};
my $tt = Template->new($ttconfig);
my $genstamp = strftime("%T %D", localtime);
@@ -284,16 +285,20 @@ sub grab_imports {
# skip primitive types
next unless ($class =~ m/\./);
# trim array delimiters
my $tclass = $class;
$tclass =~ s:[\[\]]::g;
# stuff the class into our import table
$imports{$class} = 1;
$imports{$tclass} = 1;
# if this class is a listener and not in the same package as the
# service class we're currently processing, we need to import its
# marshaller as well
if ($class =~ m/Listener$/ &&
$class !~ m/^$spackage/ &&
$class !~ $ISVC_CNAME) {
my $mclass = $class;
if ($tclass =~ m/Listener$/ &&
$tclass !~ m/^$spackage/ &&
$tclass !~ $ISVC_CNAME) {
my $mclass = $tclass;
$mclass =~ s:\.client\.:.data.:g;
$mclass =~ s:Listener:Marshaller:g;
$mclass =~ s:Service:Marshaller:g;
+6 -2
View File
@@ -306,7 +306,7 @@ Presents Notes -*- outline -*-
public ChatDirector (PresentsContext ctx)
{
InvocationDirector invdir = ctx.getClient().getInvocationDirector();
invdir.registerReceiver(new ChatDispatcher(this));
invdir.registerReceiver(new ChatDecoder(this));
}
// documentation inherited from interface
@@ -316,7 +316,7 @@ Presents Notes -*- outline -*-
}
}
The dispatcher class referenced above is generated from the interface
The decoder class referenced above is generated from the interface
specification and is responsible for converting notification events into
the appropriate method calls on the receiver implementation with which
it is constructed.
@@ -354,3 +354,7 @@ Presents Notes -*- outline -*-
"received" are converted to start with "send". If the interface method
does not start with "received" its name will not be changed in the
sender class definition.
* 8/13/2002
** True Names
Invocation -> Remote? That'd be a hairy pain...
+5 -5
View File
@@ -1,5 +1,5 @@
//
// $Id: decoder.tmpl,v 1.1 2002/08/11 05:53:13 mdb Exp $
// $Id: decoder.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $
package [% package %];
@@ -12,8 +12,8 @@ import [% import %];
*/
public class [% name %]Decoder extends InvocationDecoder
{
/** The id used to identify this notification receiver. */
public static final short RECEIVER_ID = [% receiver_id %];
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "[% receiver_code %]";
[% FOREACH method = methods -%]
/** The method id used to dispatch {@link [% name %]Receiver#[% method.mname %]}
@@ -31,9 +31,9 @@ public class [% name %]Decoder extends InvocationDecoder
}
// documentation inherited
public int getReceiverId ()
public String getReceiverCode ()
{
return RECEIVER_ID;
return RECEIVER_CODE;
}
// documentation inherited
+2 -2
View File
@@ -1,5 +1,5 @@
//
// $Id: dispatcher.tmpl,v 1.1 2002/08/09 03:36:09 mdb Exp $
// $Id: dispatcher.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $
package [% package %];
@@ -36,7 +36,7 @@ public class [% name %]Dispatcher extends InvocationDispatcher
[% FOREACH method = methods -%]
case [% name %]Marshaller.[% method.mcode %]:
(([% name %]Provider)provider).[% method.mname %](
source,
source[% IF method.unwrapped_args %],[% END %]
[% method.unwrapped_args %]
);
return;
+2 -2
View File
@@ -1,5 +1,5 @@
//
// $Id: marshaller.tmpl,v 1.1 2002/08/09 03:36:09 mdb Exp $
// $Id: marshaller.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $
package [% package %];
@@ -34,8 +34,8 @@ public class [% name %]Marshaller extends InvocationMarshaller
callerOid, requestId, [% lm.mcode %],
new Object[] { [% lm.wrapped_args %] }));
}
[% END -%]
[% END -%]
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
+3 -3
View File
@@ -1,5 +1,5 @@
//
// $Id: sender.tmpl,v 1.1 2002/08/11 05:53:13 mdb Exp $
// $Id: sender.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $
package [% package %];
@@ -19,10 +19,10 @@ public class [% name %]Sender extends InvocationSender
* [% name %]Receiver#[% m.mname %]} on a client.
*/
public static void [% m.sname %] (
ClientObject target, [% m.arglist %])
ClientObject target[% IF m.arglist %], [% END %][% m.arglist %])
{
sendNotification(
target, [% name %]Decoder.RECEIVER_ID, [% name %]Decoder.[% m.mcode %],
target, [% name %]Decoder.RECEIVER_CODE, [% name %]Decoder.[% m.mcode %],
new Object[] { [% m.wrapped_args %] });
}
@@ -1,34 +1,32 @@
//
// $Id: AdminService.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
// $Id: AdminService.java,v 1.2 2002/08/14 19:07:48 mdb Exp $
package com.threerings.admin.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.presents.client.InvocationService;
import com.threerings.admin.Log;
import com.threerings.admin.data.AdminCodes;
/**
* Handles the client side of the admin invocation services.
* Defines the client side of the admin invocation services.
*/
public class AdminService
implements AdminCodes
public interface AdminService extends InvocationService
{
/**
* Requests the list of config objects. This will result in a call to
*
* <pre>
* public void handleConfigInfoResponse (int invid, String[] keys,
* int[] oids)
* </pre>
*
* on the response target.
* Used to communicate a response to a {@link #getConfigInfo} request.
*/
public static void getConfigInfo (Client client, Object rsptarget)
public static interface ConfigInfoListener extends InvocationListener
{
InvocationDirector invdir = client.getInvocationDirector();
invdir.invoke(MODULE_NAME, GET_CONFIG_INFO_REQUEST, null, rsptarget);
Log.debug("Sent getConfigInfo request.");
/**
* Delivers a successful response to a {@link #getConfigInfo}
* request.
*/
public void gotConfigInfo (String[] keys, int[] oids);
}
/**
* Requests the list of config objects.
*/
public void getConfigInfo (Client client, ConfigInfoListener listener);
}
@@ -1,5 +1,5 @@
//
// $Id: ConfigEditorPanel.java,v 1.3 2002/07/09 21:13:20 ray Exp $
// $Id: ConfigEditorPanel.java,v 1.4 2002/08/14 19:07:48 mdb Exp $
package com.threerings.admin.client;
@@ -20,6 +20,7 @@ import com.threerings.admin.Log;
* displays their fields in a tree widget to be viewed and edited.
*/
public class ConfigEditorPanel extends JTabbedPane
implements AdminService.ConfigInfoListener
{
/**
* Constructs an editor panel which will use the supplied context to
@@ -39,8 +40,11 @@ public class ConfigEditorPanel extends JTabbedPane
// if we have no children, ship off a getConfigInfo request to
// find out what config objects are available for editing
if (getComponentCount() == 0) {
// locate our admin service and use it to make a request
AdminService service = (AdminService)
_ctx.getClient().requireService(AdminService.class);
Log.info("Sending get config info.");
AdminService.getConfigInfo(_ctx.getClient(), this);
service.getConfigInfo(_ctx.getClient(), this);
}
}
@@ -48,7 +52,7 @@ public class ConfigEditorPanel extends JTabbedPane
* Called in response to our getConfigInfo server-side service
* request.
*/
public void handleConfigInfo (int invid, String[] keys, int[] oids)
public void gotConfigInfo (String[] keys, int[] oids)
{
Log.info("Got config info: " + StringUtil.toString(keys));
// create object editor panels for each of the categories
@@ -60,6 +64,12 @@ public class ConfigEditorPanel extends JTabbedPane
}
}
// documentation inherited from interface
public void requestFailed (String reason)
{
Log.warning("Failed to get config info [reason=" + reason + "].");
}
/**
* Called when the panel is hidden; this instructs all of our object
* editors to clear out their subscriptions.
@@ -1,21 +0,0 @@
//
// $Id: AdminCodes.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
package com.threerings.admin.data;
import com.threerings.presents.data.InvocationCodes;
/**
* Contains codes used by the admin invocation services.
*/
public interface AdminCodes extends InvocationCodes
{
/** The module name for the admin services. */
public static final String MODULE_NAME = "admin";
/** The message identifier for a getConfigInfo request. */
public static final String GET_CONFIG_INFO_REQUEST = "GetConfigInfo";
/** The response identifier for a successful getConfigInfo request. */
public static final String CONFIG_INFO_RESPONSE = "ConfigInfo";
}
@@ -0,0 +1,67 @@
//
// $Id: AdminMarshaller.java,v 1.1 2002/08/14 19:07:48 mdb Exp $
package com.threerings.admin.data;
import com.threerings.admin.client.AdminService;
import com.threerings.admin.client.AdminService.ConfigInfoListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* Provides the implementation of the {@link AdminService} 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 AdminMarshaller extends InvocationMarshaller
implements AdminService
{
// documentation inherited
public static class ConfigInfoMarshaller extends ListenerMarshaller
implements ConfigInfoListener
{
/** The method id used to dispatch {@link #gotConfigInfo}
* responses. */
public static final int GOT_CONFIG_INFO = 0;
// documentation inherited from interface
public void gotConfigInfo (String[] arg1, int[] arg2)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, GOT_CONFIG_INFO,
new Object[] { arg1, arg2 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case GOT_CONFIG_INFO:
((ConfigInfoListener)listener).gotConfigInfo(
(String[])args[0], (int[])args[1]);
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
/** The method id used to dispatch {@link #getConfigInfo} requests. */
public static final int GET_CONFIG_INFO = 1;
// documentation inherited from interface
public void getConfigInfo (Client arg1, ConfigInfoListener arg2)
{
ConfigInfoMarshaller listener2 = new ConfigInfoMarshaller();
listener2.listener = arg2;
sendRequest(arg1, GET_CONFIG_INFO, new Object[] {
listener2
});
}
// Class file generated on 00:25:58 08/11/02.
}
@@ -0,0 +1,52 @@
//
// $Id: AdminDispatcher.java,v 1.1 2002/08/14 19:07:48 mdb Exp $
package com.threerings.admin.server;
import com.threerings.admin.client.AdminService;
import com.threerings.admin.client.AdminService.ConfigInfoListener;
import com.threerings.admin.data.AdminMarshaller;
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 AdminProvider}.
*/
public class AdminDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public AdminDispatcher (AdminProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new AdminMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case AdminMarshaller.GET_CONFIG_INFO:
((AdminProvider)provider).getConfigInfo(
source,
(ConfigInfoListener)args[0]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,22 +1,20 @@
//
// $Id: AdminProvider.java,v 1.1 2002/06/07 06:22:24 mdb Exp $
// $Id: AdminProvider.java,v 1.2 2002/08/14 19:07:48 mdb Exp $
package com.threerings.admin.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.data.BodyObject;
import com.threerings.admin.Log;
import com.threerings.admin.data.AdminCodes;
import com.threerings.admin.client.AdminService;
/**
* Provides the server-side interface to various administrator services.
* Provides the server-side implementation of various administrator
* services.
*/
public class AdminProvider extends InvocationProvider
implements AdminCodes
public class AdminProvider implements InvocationProvider
{
/**
* Constructs an admin provider and registers it with the invocation
@@ -25,15 +23,15 @@ public class AdminProvider extends InvocationProvider
*/
public static void init (InvocationManager invmgr)
{
invmgr.registerProvider(MODULE_NAME, new AdminProvider());
invmgr.registerDispatcher(
new AdminDispatcher(new AdminProvider()), true);
}
/**
* Processes a request from a client to obtain the configuration keys
* and oids for all registered configuration objects.
* Handles a request for the list of config objects.
*/
public void handleGetConfigInfoRequest (BodyObject source, int invid)
throws ServiceFailedException
public void getConfigInfo (
ClientObject caller, AdminService.ConfigInfoListener listener)
{
// we don't have to validate the request because the user can't do
// anything with the keys or oids unless they're an admin (we put
@@ -46,6 +44,6 @@ public class AdminProvider extends InvocationProvider
for (int ii = 0; ii < keys.length; ii++) {
oids[ii] = ConfObjRegistry.getObject(keys[ii]).getOid();
}
sendResponse(source, invid, CONFIG_INFO_RESPONSE, keys, oids);
listener.gotConfigInfo(keys, oids);
}
}
@@ -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;
}
@@ -0,0 +1,52 @@
//
// $Id: LocationDecoder.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.client;
import com.threerings.crowd.client.LocationReceiver;
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;
}
// documentation inherited
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
// documentation inherited
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);
}
}
// Generated on 11:25:46 08/12/02.
}
@@ -1,5 +1,5 @@
//
// $Id: LocationDirector.java,v 1.24 2002/06/14 01:40:16 ray Exp $
// $Id: LocationDirector.java,v 1.25 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.client;
@@ -10,9 +10,9 @@ import com.samskivert.util.ObserverList;
import com.samskivert.util.ObserverList.ObserverOp;
import com.samskivert.util.ResultListener;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationReceiver;
import com.threerings.presents.client.SessionObserver;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.ObjectAccessException;
@@ -32,8 +32,9 @@ import com.threerings.crowd.util.CrowdContext;
* provides a mechanism for ratifying a request to move to a new place
* before actually issuing the request.
*/
public class LocationDirector
implements LocationCodes, SessionObserver, Subscriber, InvocationReceiver
public class LocationDirector extends BasicDirector
implements LocationCodes, Subscriber, LocationReceiver,
LocationService.MoveListener
{
/**
* Used to recover from a moveTo request that was accepted but
@@ -55,15 +56,14 @@ public class LocationDirector
*/
public LocationDirector (CrowdContext ctx)
{
super(ctx);
// keep this around for later
_ctx = ctx;
// register ourselves as a client observer
ctx.getClient().addClientObserver(this);
// register for location notifications
_ctx.getClient().getInvocationDirector().registerReceiver(
MODULE_NAME, this);
new LocationDecoder(this));
}
/**
@@ -137,7 +137,7 @@ public class LocationDirector
_pendingPlaceId = placeId;
// issue a moveTo request
LocationService.moveTo(_ctx.getClient(), placeId, this);
_lservice.moveTo(_ctx.getClient(), placeId, this);
return true;
}
@@ -314,7 +314,9 @@ public class LocationDirector
// documentation inherited from interface
public void clientDidLogon (Client client)
{
// get a copy of our body object
super.clientDidLogon(client);
// subscribe to our body object
Subscriber sub = new Subscriber() {
public void objectAvailable (DObject object)
{
@@ -332,6 +334,14 @@ public class LocationDirector
client.getDObjectManager().subscribeToObject(cloid, sub);
}
// documentation inherited
protected void fetchServices (Client client)
{
// obtain our service handle
_lservice = (LocationService)
client.requireService(LocationService.class);
}
protected void gotBodyObject (BodyObject clobj)
{
// check to see if we are already in a location, in which case
@@ -341,17 +351,19 @@ public class LocationDirector
// documentation inherited from interface
public void clientDidLogoff (Client client)
{
super.clientDidLogoff(client);
// clear ourselves out and inform observers of our departure
didLeavePlace();
// let our observers know that we're no longer in a location
_observers.apply(_didChangeOp);
_lservice = null;
}
/**
* Called in response to a successful <code>moveTo</code> request.
*/
public void handleMoveSucceeded (int invid, PlaceConfig config)
// documentation inherited from interface
public void moveSucceeded (PlaceConfig config)
{
// handle the successful move
didMoveTo(_pendingPlaceId, config);
@@ -360,10 +372,8 @@ public class LocationDirector
_pendingPlaceId = -1;
}
/**
* Called in response to a failed <code>moveTo</code> request.
*/
public void handleMoveFailed (int invid, String reason)
// documentation inherited from interface
public void requestFailed (String reason)
{
// clear out our pending request oid
int placeId = _pendingPlaceId;
@@ -376,13 +386,8 @@ public class LocationDirector
notifyFailure(placeId, reason);
}
/**
* Called when the server has decided to forcibly move us to another
* room. The server first ejects us from our previous room and then
* sends us a notification with our new location. We then turn around
* and issue a standard moveTo request.
*/
public void handleMoveNotification (int placeId)
// documentation inherited from interface
public void forcedMove (int placeId)
{
Log.info("Moving at request of server [placeId=" + placeId + "].");
@@ -493,6 +498,9 @@ public class LocationDirector
/** 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 _observers =
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY);
@@ -0,0 +1,21 @@
//
// $Id: LocationReceiver.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
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.
*/
public void forcedMove (int placeId);
}
@@ -1,33 +1,43 @@
//
// $Id: LocationService.java,v 1.6 2002/05/15 23:54:34 mdb Exp $
// $Id: LocationService.java,v 1.7 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.presents.client.InvocationService;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.LocationCodes;
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
* location director.
*
* @see LocationDirector
* {@link LocationDirector}.
*/
public class LocationService implements LocationCodes
public interface LocationService extends InvocationService
{
/**
* Requests that that this client's body be moved to the specified
* location.
* Used to communicate responses to {@link #moveTo} requests.
*/
public static void moveTo (Client client, int placeId,
LocationDirector rsptarget)
public static interface MoveListener extends InvocationListener
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(placeId) };
invdir.invoke(MODULE_NAME, MOVE_TO_REQUEST, args, rsptarget);
Log.debug("Sent moveTo request [place=" + placeId + "].");
/**
* Called in response to a successful {@link #moveTo} request.
*/
public 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.
*/
public void moveTo (Client client, int placeId, MoveListener listener);
}
@@ -1,5 +1,5 @@
//
// $Id: BodyObject.dobj,v 1.9 2002/06/20 22:37:32 mdb Exp $
// $Id: BodyObject.dobj,v 1.10 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
@@ -18,9 +18,7 @@ public class BodyObject extends ClientObject
*/
public int location = -1;
/**
* Returns a short string identifying this body.
*/
// documentation inherited
public String who ()
{
return username + " (" + getOid() + ")";
@@ -1,5 +1,5 @@
//
// $Id: BodyObject.java,v 1.3 2002/06/20 22:37:32 mdb Exp $
// $Id: BodyObject.java,v 1.4 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
@@ -24,9 +24,7 @@ public class BodyObject extends ClientObject
*/
public int location = -1;
/**
* Returns a short string identifying this body.
*/
// documentation inherited
public String who ()
{
return username + " (" + getOid() + ")";
@@ -1,35 +1,15 @@
//
// $Id: LocationCodes.java,v 1.3 2002/05/26 02:24:46 mdb Exp $
// $Id: LocationCodes.java,v 1.4 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.crowd.client.LocationDirector;
/**
* Contains codes used by the location invocation services.
*/
public interface LocationCodes extends InvocationCodes
{
/** The module name for the location services. */
public static final String MODULE_NAME = "location";
/** The message identifier for a moveTo request. */
public static final String MOVE_TO_REQUEST = "MoveTo";
/** The response identifier for a successful moveTo request. This is
* mapped by the invocation services to a call to {@link
* LocationDirector#handleMoveSucceeded}. */
public static final String MOVE_SUCCEEDED_RESPONSE = "MoveSucceeded";
/** The response identifier for a failed moveTo request. This is
* mapped by the invocation services to a call to {@link
* LocationDirector#handleMoveFailed}. */
public static final String MOVE_FAILED_RESPONSE = "MoveFailed";
/** The message identifier for a move notification. */
public static final String MOVE_NOTIFICATION = "Move";
/** An error code indicating that a place identified by a particular
* place id does not exist. Usually generated by a failed moveTo
* request. */
@@ -0,0 +1,68 @@
//
// $Id: LocationMarshaller.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.client.LocationService.MoveListener;
import com.threerings.crowd.data.PlaceConfig;
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.
*/
public class LocationMarshaller extends InvocationMarshaller
implements LocationService
{
// documentation inherited
public static class MoveMarshaller extends ListenerMarshaller
implements MoveListener
{
/** The method id used to dispatch {@link #moveSucceeded}
* responses. */
public static final int MOVE_SUCCEEDED = 0;
// documentation inherited from interface
public void moveSucceeded (PlaceConfig arg1)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, MOVE_SUCCEEDED,
new Object[] { arg1 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case MOVE_SUCCEEDED:
((MoveListener)listener).moveSucceeded(
(PlaceConfig)args[0]);
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
/** The method id used to dispatch {@link #moveTo} requests. */
public static final int MOVE_TO = 1;
// documentation inherited from interface
public void moveTo (Client arg1, int arg2, MoveListener arg3)
{
MoveMarshaller listener3 = new MoveMarshaller();
listener3.listener = arg3;
sendRequest(arg1, MOVE_TO, new Object[] {
new Integer(arg2), listener3
});
}
// Class file generated on 00:25:59 08/11/02.
}
@@ -1,5 +1,5 @@
//
// $Id: OccupantInfo.java,v 1.8 2002/07/23 05:54:52 mdb Exp $
// $Id: OccupantInfo.java,v 1.9 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
@@ -40,7 +40,7 @@ public class OccupantInfo extends SimpleStreamableObject
}
// documentation inherited
public Object getKey ()
public Comparable getKey ()
{
return bodyOid;
}
@@ -1,9 +1,13 @@
//
// $Id: PlaceObject.dobj,v 1.9 2002/04/15 14:30:49 shaper Exp $
// $Id: PlaceObject.dobj,v 1.10 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
import com.threerings.presents.dobj.*;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.OidList;
import com.threerings.crowd.chat.SpeakMarshaller;
public class PlaceObject extends DObject
{
@@ -19,4 +23,7 @@ public class PlaceObject extends DObject
* to be known by everyone in the place.
*/
public DSet occupantInfo = new DSet();
/** Used to generate speak requests on this place object. */
public SpeakMarshaller speakService;
}
@@ -1,9 +1,13 @@
//
// $Id: PlaceObject.java,v 1.6 2002/04/15 14:35:31 shaper Exp $
// $Id: PlaceObject.java,v 1.7 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.data;
import com.threerings.presents.dobj.*;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
import com.threerings.presents.dobj.OidList;
import com.threerings.crowd.chat.SpeakMarshaller;
public class PlaceObject extends DObject
{
@@ -13,6 +17,9 @@ public class PlaceObject extends DObject
/** The field name of the <code>occupantInfo</code> field. */
public static final String OCCUPANT_INFO = "occupantInfo";
/** The field name of the <code>speakService</code> field. */
public static final String SPEAK_SERVICE = "speakService";
/**
* Tracks the oid of the body objects of all of the occupants of this
* place.
@@ -26,6 +33,9 @@ public class PlaceObject extends DObject
*/
public DSet occupantInfo = new DSet();
/** Used to generate speak requests on this place object. */
public SpeakMarshaller speakService;
/**
* Requests that the specified oid be added to the
* <code>occupants</code> oid list. The list will not change until the
@@ -91,4 +101,18 @@ public class PlaceObject extends DObject
this.occupantInfo = occupantInfo;
requestAttributeChange(OCCUPANT_INFO, occupantInfo);
}
/**
* 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.
*/
public void setSpeakService (SpeakMarshaller speakService)
{
this.speakService = speakService;
requestAttributeChange(SPEAK_SERVICE, speakService);
}
}
@@ -1,5 +1,5 @@
//
// $Id: CrowdServer.java,v 1.12 2002/04/18 00:44:50 shaper Exp $
// $Id: CrowdServer.java,v 1.13 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
@@ -8,6 +8,7 @@ import java.util.Iterator;
import com.threerings.presents.server.PresentsServer;
import com.threerings.crowd.Log;
import com.threerings.crowd.chat.ChatProvider;
import com.threerings.crowd.data.BodyObject;
/**
@@ -40,10 +41,13 @@ public class CrowdServer extends PresentsServer
// create our place registry
plreg = new PlaceRegistry(invmgr, omgr);
// register our invocation service providers
String[] providers = null;
providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers);
registerProviders(providers);
// initialize the chat services
ChatProvider.init(invmgr, omgr);
// // register our invocation service providers
// String[] providers = null;
// providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers);
// registerProviders(providers);
Log.info("Crowd server initialized.");
}
@@ -0,0 +1,53 @@
//
// $Id: LocationDispatcher.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.client.LocationService.MoveListener;
import com.threerings.crowd.data.LocationMarshaller;
import com.threerings.crowd.data.PlaceConfig;
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 LocationProvider}.
*/
public class LocationDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public LocationDispatcher (LocationProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new LocationMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case LocationMarshaller.MOVE_TO:
((LocationProvider)provider).moveTo(
source,
((Integer)args[0]).intValue(), (MoveListener)args[1]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,16 +1,19 @@
//
// $Id: LocationProvider.java,v 1.15 2002/06/20 22:38:58 mdb Exp $
// $Id: LocationProvider.java,v 1.16 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.RootDObjectManager;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.Log;
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.LocationCodes;
import com.threerings.crowd.data.OccupantInfo;
@@ -20,42 +23,38 @@ import com.threerings.crowd.data.PlaceObject;
/**
* This class provides the server end of the location services.
*/
public class LocationProvider extends InvocationProvider
implements LocationCodes
public class LocationProvider
implements LocationCodes, InvocationProvider
{
/**
* Constructs a location provider and registers it with the invocation
* manager to handle location services. This is done automatically by
* the {@link PlaceRegistry}.
* Creates a location provider and prepares it for operation.
*/
public static void init (
InvocationManager invmgr, RootDObjectManager omgr, PlaceRegistry plreg)
public LocationProvider (InvocationManager invmgr, RootDObjectManager omgr,
PlaceRegistry plreg)
{
// we'll need these later
_invmgr = invmgr;
_omgr = omgr;
_plreg = plreg;
// register a location provider instance
invmgr.registerProvider(MODULE_NAME, new LocationProvider());
}
/**
* Processes a request from a client to move to a new place.
* Requests that this client's body be moved to the specified
* location.
*
* @param caller the client object of the client that invoked this
* remotely callable method.
* @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.
*/
public void handleMoveToRequest (
BodyObject source, int invid, int placeId)
public void moveTo (ClientObject caller, int placeId,
LocationService.MoveListener listener)
throws InvocationException
{
try {
// do the move
PlaceConfig config = moveTo(source, placeId);
// and send the response
sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, config);
} catch (ServiceFailedException sfe) {
sendResponse(source, invid, MOVE_FAILED_RESPONSE,
sfe.getMessage());
}
// do the move and send the response
listener.moveSucceeded(moveTo((BodyObject)caller, placeId));
}
/**
@@ -68,8 +67,8 @@ public class LocationProvider extends InvocationProvider
* successful for some reason (which will be communicated as an error
* code in the exception's message data).
*/
public static PlaceConfig moveTo (BodyObject source, int placeId)
throws ServiceFailedException
public PlaceConfig moveTo (BodyObject source, int placeId)
throws InvocationException
{
int bodoid = source.getOid();
@@ -78,7 +77,7 @@ public class LocationProvider extends InvocationProvider
if (pmgr == null) {
Log.info("Requested to move to non-existent place " +
"[source=" + source + ", place=" + placeId + "].");
throw new ServiceFailedException(NO_SUCH_PLACE);
throw new InvocationException(NO_SUCH_PLACE);
}
// if they're already in the location they're asking to move to,
@@ -97,26 +96,17 @@ public class LocationProvider extends InvocationProvider
if (!source.acquireLock("moveToLock")) {
// if we're still locked, a previous moveTo request hasn't
// been fully processed
throw new ServiceFailedException(MOVE_IN_PROGRESS);
throw new InvocationException(MOVE_IN_PROGRESS);
}
PlaceObject place = pmgr.getPlaceObject();
try {
place.startTransaction();
source.startTransaction();
// remove them from any previous location
leaveOccupiedPlace(source);
// set the body's new location
source.setLocation(place.getOid());
} finally {
source.commitTransaction();
}
try {
place.startTransaction();
// generate a new occupant info record and add it to the
// target location
OccupantInfo info = pmgr.buildOccupantInfo(source);
@@ -124,11 +114,15 @@ public class LocationProvider extends InvocationProvider
place.addToOccupantInfo(info);
}
// set the body's new location
source.setLocation(place.getOid());
// add the body oid to the place object's occupant list
place.addToOccupants(bodoid);
} finally {
place.commitTransaction();
source.commitTransaction();
}
} finally {
@@ -144,7 +138,7 @@ public class LocationProvider extends InvocationProvider
* Removes the specified body from the place object they currently
* occupy. Does nothing if the body is not currently in a place.
*/
public static void leaveOccupiedPlace (BodyObject source)
public void leaveOccupiedPlace (BodyObject source)
{
int oldloc = source.location;
int bodoid = source.getOid();
@@ -200,18 +194,16 @@ public class LocationProvider extends InvocationProvider
// first remove them from their old place
leaveOccupiedPlace(source);
// then send a move notification
_invmgr.sendNotification(
source.getOid(), MODULE_NAME, MOVE_NOTIFICATION,
new Object[] { new Integer(placeId) });
// then send a forced move notification
LocationSender.forcedMove(source, placeId);
}
/** The invocation manager with which we interoperate. */
protected static InvocationManager _invmgr;
protected InvocationManager _invmgr;
/** The distributed object manager with which we interoperate. */
protected static RootDObjectManager _omgr;
protected RootDObjectManager _omgr;
/** The place registry with which we interoperate. */
protected static PlaceRegistry _plreg;
protected PlaceRegistry _plreg;
}
@@ -0,0 +1,30 @@
//
// $Id: LocationSender.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
import com.threerings.crowd.client.LocationDecoder;
import com.threerings.crowd.client.LocationReceiver;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
/**
* 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[] { new Integer(arg1) });
}
// Generated on 11:25:46 08/12/02.
}
@@ -1,5 +1,5 @@
//
// $Id: PlaceManager.java,v 1.31 2002/06/20 22:12:22 mdb Exp $
// $Id: PlaceManager.java,v 1.32 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
@@ -7,6 +7,10 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.dobj.MessageListener;
@@ -22,6 +26,10 @@ import com.threerings.crowd.data.OccupantInfo;
import com.threerings.crowd.data.PlaceConfig;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.chat.SpeakDispatcher;
import com.threerings.crowd.chat.SpeakMarshaller;
import com.threerings.crowd.chat.SpeakProvider;
/**
* The place manager is the server-side entity that handles all
* place-related interaction. It subscribes to the place object and reacts
@@ -42,7 +50,8 @@ import com.threerings.crowd.data.PlaceObject;
* listeners.
*/
public class PlaceManager
implements MessageListener, OidListListener, ObjectDeathListener
implements MessageListener, OidListListener, ObjectDeathListener,
SpeakProvider.SpeakerValidator
{
/**
* An interface used to allow the registration of standard message
@@ -99,11 +108,13 @@ public class PlaceManager
* Called by the place registry after creating this place manager.
*/
public void init (
PlaceRegistry registry, PlaceConfig config, DObjectManager omgr)
PlaceRegistry registry, InvocationManager invmgr,
DObjectManager omgr, PlaceConfig config)
{
_registry = registry;
_config = config;
_invmgr = invmgr;
_omgr = omgr;
_config = config;
// let derived classes do initialization stuff
didInit();
@@ -135,6 +146,13 @@ public class PlaceManager
// keep track of this
_plobj = plobj;
// create and register a speaker service instance that clients can
// use to speak in this place
SpeakMarshaller speakService =
(SpeakMarshaller)_invmgr.registerDispatcher(
new SpeakDispatcher(new SpeakProvider(_plobj, this)), false);
plobj.setSpeakService(speakService);
// we'll need to hear about place object events
plobj.addListener(this);
@@ -379,6 +397,13 @@ public class PlaceManager
didShutdown();
}
// documentation inherited from interface
public boolean isValidSpeaker (DObject speakObj, ClientObject speaker)
{
// only allow people in the room to speak
return _plobj.occupants.contains(speaker.getOid());
}
/**
* Generates a string representation of this manager. Does so in a way
* that makes it easier for derived classes to add to the string
@@ -438,6 +463,13 @@ public class PlaceManager
}
}
/** 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 DObjectManager _omgr;
@@ -447,9 +479,6 @@ public class PlaceManager
/** A reference to the configuration for our place. */
protected PlaceConfig _config;
/** A reference to the place registry with which we're registered. */
protected PlaceRegistry _registry;
/** Message handlers are used to process message events. */
protected HashMap _msghandlers;
@@ -1,5 +1,5 @@
//
// $Id: PlaceRegistry.java,v 1.20 2002/04/15 16:28:01 shaper Exp $
// $Id: PlaceRegistry.java,v 1.21 2002/08/14 19:07:49 mdb Exp $
package com.threerings.crowd.server;
@@ -43,17 +43,23 @@ public class PlaceRegistry
public void placeCreated (PlaceObject place, PlaceManager pmgr);
}
/** The location provider used by the place registry to provide
* location-related invocation services. */
public LocationProvider locprov;
/**
* Creates and initializes the place registry; called by the server
* during its initialization phase.
*/
public PlaceRegistry (InvocationManager invmgr, RootDObjectManager omgr)
{
// we'll need this later
_omgr = omgr;
// create and register our location provider
locprov = new LocationProvider(invmgr, omgr, this);
invmgr.registerDispatcher(new LocationDispatcher(locprov), true);
// register the location provider
LocationProvider.init(invmgr, omgr, this);
// we'll need these later
_omgr = omgr;
_invmgr = invmgr;
}
/**
@@ -86,7 +92,7 @@ public class PlaceRegistry
// create a place manager for this place
PlaceManager pmgr = (PlaceManager)pmgrClass.newInstance();
// let the pmgr know about us and its configuration
pmgr.init(this, config, _omgr);
pmgr.init(this, _invmgr, _omgr, config);
// stick the manager on the creation queue because we know
// we'll get our calls to objectAvailable()/requestFailed() in
@@ -221,6 +227,9 @@ public class PlaceRegistry
}
}
/** The invocation manager with which we operate. */
protected InvocationManager _invmgr;
/** The distributed object manager with which we operate. */
protected RootDObjectManager _omgr;
@@ -1,5 +1,5 @@
//
// $Id: MiCasaApplet.java,v 1.6 2002/02/09 20:47:11 mdb Exp $
// $Id: MiCasaApplet.java,v 1.7 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.client;
@@ -93,7 +93,7 @@ public class MiCasaApplet extends Applet
// hide the frame and log off
_frame.setVisible(false);
Client client = _client.getContext().getClient();
if (client.loggedOn()) {
if (client.isLoggedOn()) {
client.logoff(false);
}
}
@@ -1,5 +1,5 @@
//
// $Id: MiCasaClient.java,v 1.14 2002/07/12 17:01:28 mdb Exp $
// $Id: MiCasaClient.java,v 1.15 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.client;
@@ -55,7 +55,7 @@ public class MiCasaClient
_frame.addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent evt) {
// if we're logged on, log off
if (_client.loggedOn()) {
if (_client.isLoggedOn()) {
_client.logoff(true);
} else {
// otherwise get the heck out
@@ -1,36 +0,0 @@
//
// $Id: LobbyCodes.java,v 1.4 2002/04/15 16:28:02 shaper Exp $
package com.threerings.micasa.lobby;
import com.threerings.presents.data.InvocationCodes;
/**
* Contains codes used by the lobby invocation services.
*/
public interface LobbyCodes extends InvocationCodes
{
/** The module name for the lobby services. */
public static final String MODULE_NAME = "lobby";
/** The message identifier for a get categories request. */
public static final String GET_CATEGORIES_REQUEST = "GetCategories";
/** The message identifier for a got categories response. This is
* mapped by the invocation services to a call to
* <code>handleGotCategories</code>. */
public static final String GOT_CATEGORIES_RESPONSE = "GotCategories";
/** The message identifier for a get lobbies request. */
public static final String GET_LOBBIES_REQUEST = "GetLobbies";
/** The message identifier for a got lobbies response. This is mapped
* by the invocation services to a call to
* <code>handleGotLobbies</code>. */
public static final String GOT_LOBBIES_RESPONSE = "GotLobbies";
/** The message identifier for a failed request. This is
* mapped by the invocation services to a call to
* <code>handleRequestFailed</code>. */
public static final String REQUEST_FAILED_RESPONSE = "RequestFailed";
}
@@ -1,5 +1,5 @@
//
// $Id: LobbyController.java,v 1.8 2001/10/25 23:42:33 mdb Exp $
// $Id: LobbyController.java,v 1.9 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
@@ -15,8 +15,7 @@ import com.threerings.parlor.game.GameConfig;
import com.threerings.micasa.Log;
import com.threerings.micasa.util.MiCasaContext;
public class LobbyController
extends PlaceController
public class LobbyController extends PlaceController
implements InvitationHandler, InvitationResponseObserver
{
public void init (CrowdContext ctx, PlaceConfig config)
@@ -61,35 +60,38 @@ public class LobbyController
}
}
public void invitationReceived (int inviteId, String inviter,
GameConfig config)
// documentation inherited from interface
public void invitationReceived (Invitation invite)
{
Log.info("Invitation received [inviteId=" + inviteId +
", inviter=" + inviter + ", config=" + config + "].");
Log.info("Invitation received [invite=" + invite + "].");
// accept the invitation. we're game...
_ctx.getParlorDirector().accept(inviteId);
invite.accept();
}
public void invitationCancelled (int inviteId)
// documentation inherited from interface
public void invitationCancelled (Invitation invite)
{
Log.info("Invitation cancelled [inviteId=" + inviteId + "].");
Log.info("Invitation cancelled " + invite + ".");
}
public void invitationAccepted (int inviteId)
// documentation inherited from interface
public void invitationAccepted (Invitation invite)
{
Log.info("Invitation accepted [inviteId=" + inviteId + "].");
Log.info("Invitation accepted " + invite + ".");
}
public void invitationRefused (int inviteId, String message)
// documentation inherited from interface
public void invitationRefused (Invitation invite, String message)
{
Log.info("Invitation refused [inviteId=" + inviteId +
Log.info("Invitation refused [invite=" + invite +
", message=" + message + "].");
}
public void invitationCountered (int inviteId, GameConfig config)
// documentation inherited from interface
public void invitationCountered (Invitation invite, GameConfig config)
{
Log.info("Invitation countered [inviteId=" + inviteId +
Log.info("Invitation countered [invite=" + invite +
", config=" + config + "].");
}
@@ -0,0 +1,61 @@
//
// $Id: LobbyDispatcher.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import com.threerings.micasa.lobby.LobbyMarshaller;
import com.threerings.micasa.lobby.LobbyService;
import com.threerings.micasa.lobby.LobbyService.CategoriesListener;
import com.threerings.micasa.lobby.LobbyService.LobbiesListener;
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;
import java.util.List;
/**
* Dispatches requests to the {@link LobbyProvider}.
*/
public class LobbyDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public LobbyDispatcher (LobbyProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new LobbyMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case LobbyMarshaller.GET_CATEGORIES:
((LobbyProvider)provider).getCategories(
source,
(CategoriesListener)args[0]
);
return;
case LobbyMarshaller.GET_LOBBIES:
((LobbyProvider)provider).getLobbies(
source,
(String)args[0], (LobbiesListener)args[1]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,13 +1,12 @@
//
// $Id: LobbyManager.java,v 1.4 2001/10/11 04:13:33 mdb Exp $
// $Id: LobbyManager.java,v 1.5 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import java.util.Properties;
import com.samskivert.util.StringUtil;
import com.threerings.crowd.chat.ChatService;
import com.threerings.crowd.chat.ChatMessageHandler;
import com.threerings.crowd.chat.ChatCodes;
import com.threerings.crowd.server.PlaceManager;
import com.threerings.micasa.Log;
@@ -61,11 +60,6 @@ public class LobbyManager extends PlaceManager
// let the lobby registry know that we're up and running
_lobreg.lobbyReady(_plobj.getOid(), _gameIdent, _name);
// register a chat message handler because we want to support
// chatting
MessageHandler handler = new ChatMessageHandler();
registerMessageHandler(ChatService.SPEAK_REQUEST, handler);
}
/** The universal game identifier for the game matchmade by this
@@ -0,0 +1,113 @@
//
// $Id: LobbyMarshaller.java,v 1.1 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import com.threerings.micasa.lobby.LobbyService;
import com.threerings.micasa.lobby.LobbyService.CategoriesListener;
import com.threerings.micasa.lobby.LobbyService.LobbiesListener;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
import java.util.List;
/**
* Provides the implementation of the {@link LobbyService} 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 LobbyMarshaller extends InvocationMarshaller
implements LobbyService
{
// documentation inherited
public static class CategoriesMarshaller extends ListenerMarshaller
implements CategoriesListener
{
/** The method id used to dispatch {@link #gotCategories}
* responses. */
public static final int GOT_CATEGORIES = 0;
// documentation inherited from interface
public void gotCategories (String[] arg1)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, GOT_CATEGORIES,
new Object[] { arg1 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case GOT_CATEGORIES:
((CategoriesListener)listener).gotCategories(
(String[])args[0]);
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
// documentation inherited
public static class LobbiesMarshaller extends ListenerMarshaller
implements LobbiesListener
{
/** The method id used to dispatch {@link #gotLobbies}
* responses. */
public static final int GOT_LOBBIES = 0;
// documentation inherited from interface
public void gotLobbies (List arg1)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, GOT_LOBBIES,
new Object[] { arg1 }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case GOT_LOBBIES:
((LobbiesListener)listener).gotLobbies(
(List)args[0]);
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
/** The method id used to dispatch {@link #getCategories} requests. */
public static final int GET_CATEGORIES = 1;
// documentation inherited from interface
public void getCategories (Client arg1, CategoriesListener arg2)
{
CategoriesMarshaller listener2 = new CategoriesMarshaller();
listener2.listener = arg2;
sendRequest(arg1, GET_CATEGORIES, new Object[] {
listener2
});
}
/** The method id used to dispatch {@link #getLobbies} requests. */
public static final int GET_LOBBIES = 2;
// documentation inherited from interface
public void getLobbies (Client arg1, String arg2, LobbiesListener arg3)
{
LobbiesMarshaller listener3 = new LobbiesMarshaller();
listener3.listener = arg3;
sendRequest(arg1, GET_LOBBIES, new Object[] {
arg2, listener3
});
}
// Class file generated on 00:26:00 08/11/02.
}
@@ -1,57 +1,31 @@
//
// $Id: LobbyProvider.java,v 1.4 2002/07/23 05:54:52 mdb Exp $
// $Id: LobbyProvider.java,v 1.5 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import com.threerings.crowd.data.BodyObject;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.util.StreamableArrayList;
import com.threerings.micasa.lobby.LobbyService.CategoriesListener;
import com.threerings.micasa.lobby.LobbyService.LobbiesListener;
/**
* Handles server side of lobby-related invocation services.
* Provides access to the server-side implementation of the lobby
* services.
*/
public class LobbyProvider
extends InvocationProvider implements LobbyCodes
public interface LobbyProvider extends InvocationProvider
{
/**
* Constructs a lobby provider instance which will be used to handle
* all lobby-related invocation service requests. This is
* automatically taken care of by the lobby registry, so no other
* entity need instantiate and register a lobby provider.
*
* @param lobreq a reference to the lobby registry active in this
* server.
*/
public LobbyProvider (LobbyRegistry lobreg)
{
_lobreg = lobreg;
}
/**
* Processes a request by the client to obtain a list of the lobby
* categories available on this server.
*/
public void handleGetCategoriesRequest (
BodyObject source, int invid)
{
String[] cats = _lobreg.getCategories(source);
// we have to cast the array to an object to avoid having the
// compiler pick the version of sendResponse that takes Object[]
sendResponse(source, invid, GOT_CATEGORIES_RESPONSE, (Object)cats);
}
public void getCategories (ClientObject caller,
CategoriesListener listener);
/**
* Processes a request by the client to obtain a list of lobbies
* matching the supplied category string.
*/
public void handleGetLobbiesRequest (
BodyObject source, int invid, String category)
{
StreamableArrayList list = new StreamableArrayList();
_lobreg.getLobbies(source, category, list);
sendResponse(source, invid, GOT_LOBBIES_RESPONSE, list);
}
/** A reference to the lobby registry. */
protected LobbyRegistry _lobreg;
public void getLobbies (ClientObject caller, String category,
LobbiesListener listener);
}
@@ -1,15 +1,20 @@
//
// $Id: LobbyRegistry.java,v 1.9 2002/05/21 04:46:44 mdb Exp $
// $Id: LobbyRegistry.java,v 1.10 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import java.util.*;
import com.samskivert.util.*;
import com.threerings.util.StreamableArrayList;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationManager;
import com.threerings.crowd.data.BodyObject;
import com.threerings.micasa.Log;
import com.threerings.micasa.lobby.LobbyService.CategoriesListener;
import com.threerings.micasa.lobby.LobbyService.LobbiesListener;
import com.threerings.micasa.server.MiCasaConfig;
import com.threerings.micasa.server.MiCasaServer;
@@ -64,7 +69,8 @@ import com.threerings.micasa.server.MiCasaServer;
* sense, the client will be able to use them to search for lobbies
* containing games of similar types using the provided facilities.
*/
public class LobbyRegistry implements LobbyCodes
public class LobbyRegistry
implements LobbyProvider
{
/**
* Initializes the registry. It will use the supplied configuration
@@ -75,9 +81,8 @@ public class LobbyRegistry implements LobbyCodes
*/
public void init (InvocationManager invmgr)
{
// register our invocation service handler
LobbyProvider provider = new LobbyProvider(this);
invmgr.registerProvider(MODULE_NAME, provider);
// register ourselves as an invocation service handler
invmgr.registerDispatcher(new LobbyDispatcher(this), true);
// create our lobby managers
String[] lmgrs = null;
@@ -178,6 +183,30 @@ public class LobbyRegistry implements LobbyCodes
return cats;
}
/**
* Processes a request by the client to obtain a list of the lobby
* categories available on this server.
*/
public void getCategories (ClientObject caller, CategoriesListener listener)
{
listener.gotCategories(getCategories((BodyObject)caller));
}
/**
* Processes a request by the client to obtain a list of lobbies
* matching the supplied category string.
*/
public void getLobbies (ClientObject caller, String category,
LobbiesListener listener)
{
StreamableArrayList target = new StreamableArrayList();
ArrayList list = (ArrayList)_lobbies.get(category);
if (list != null) {
target.addAll(list);
}
listener.gotLobbies(target);
}
/**
* Called by our lobby managers once they have started up and are
* ready to do their lobby duties.
@@ -1,5 +1,5 @@
//
// $Id: LobbySelector.java,v 1.4 2001/12/14 16:07:30 shaper Exp $
// $Id: LobbySelector.java,v 1.5 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
@@ -18,8 +18,6 @@ import com.samskivert.util.StringUtil;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.micasa.Log;
import com.threerings.micasa.lobby.Lobby;
import com.threerings.micasa.lobby.LobbyService;
import com.threerings.micasa.util.MiCasaContext;
/**
@@ -28,8 +26,9 @@ import com.threerings.micasa.util.MiCasaContext;
* displays a list of the lobbies that are available in that category. If
* a lobby is double-clicked, it moves the client into that lobby room.
*/
public class LobbySelector
extends JPanel implements ActionListener
public class LobbySelector extends JPanel
implements ActionListener, LobbyService.CategoriesListener,
LobbyService.LobbiesListener
{
/**
* Constructs a new lobby selector component. It will wait until it is
@@ -64,16 +63,18 @@ public class LobbySelector
};
_loblist.addMouseListener(ml);
add(_loblist, BorderLayout.CENTER);
}
// we'll need to know when we are made visible because we'll want
// to issue a request for our categories when that happens
addAncestorListener(new AncestorAdapter () {
public void ancestorAdded (AncestorEvent evt) {
// issue a get categories request to get the category list
LobbyService.getCategories(
_ctx.getClient(), LobbySelector.this);
}
});
// documentation inherited
public void addNotify ()
{
super.addNotify();
// get a handle on our lobby service instance
_lservice = (LobbyService)
_ctx.getClient().requireService(LobbyService.class);
// and use them to look up the lobby categories
_lservice.getCategories(_ctx.getClient(), this);
}
/**
@@ -90,11 +91,8 @@ public class LobbySelector
}
}
/**
* Called in response to a {@link LobbyService#getCategories}
* invocation service request.
*/
public void handleGotCategories (int invid, String[] categories)
// documentation inherited from interface
public void gotCategories (String[] categories)
{
// append these to our "unselected" item
for (int i = 0; i < categories.length; i++) {
@@ -102,11 +100,8 @@ public class LobbySelector
}
}
/**
* Called in response to a {@link LobbyService#getLobbies} invocation
* service request.
*/
public void handleGotLobbies (int invid, List lobbies)
// documentation inherited from interface
public void gotLobbies (List lobbies)
{
// create a list model for this category
DefaultListModel model = new DefaultListModel();
@@ -130,16 +125,13 @@ public class LobbySelector
_pendingCategory = null;
}
/**
* Called in response to a lobby invocation request when the request
* failed.
*/
public void handleRequestFailed (int invid, String reason)
// documentation inherited from interface
public void requestFailed (String reason)
{
Log.info("Request failed [invid=" + invid +
", reason=" + reason + "].");
Log.info("Request failed [reason=" + reason + "].");
// clear out our pending category indicator
// clear out our pending category indicator in case this was a
// failed getLobbies() request
_pendingCategory = null;
}
@@ -158,7 +150,7 @@ public class LobbySelector
// make a note that we're loading up this category
_pendingCategory = category;
// issue a request to load up the lobbies in this category
LobbyService.getLobbies(_ctx.getClient(), category, this);
_lservice.getLobbies(_ctx.getClient(), category, this);
} else {
Log.info("Ignoring category select request because " +
@@ -201,6 +193,8 @@ public class LobbySelector
}
protected MiCasaContext _ctx;
protected LobbyService _lservice;
protected JComboBox _combo;
protected JList _loblist;
@@ -1,40 +1,54 @@
//
// $Id: LobbyService.java,v 1.3 2002/05/15 23:54:34 mdb Exp $
// $Id: LobbyService.java,v 1.4 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.lobby;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import java.util.List;
import com.threerings.micasa.Log;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* This class provides an interface to the various parlor services that
* are directly invokable by the client (by means of the invocation
* services).
* Provides an interface to the various parlor services that are directly
* invokable by the client (by means of the invocation services).
*/
public class LobbyService
implements LobbyCodes
public interface LobbyService extends InvocationService
{
/**
* Used to communicate the results of a {@link #getCategories}
* request.
*/
public static interface CategoriesListener extends InvocationListener
{
/**
* Supplies the listener with the results of a {@link
* #getCategories} request.
*/
public void gotCategories (String[] categories);
}
/**
* Used to communicate the results of a {@link #getLobbies}
* request.
*/
public static interface LobbiesListener extends InvocationListener
{
/**
* Supplies the listener with the results of a {@link
* #getLobbies} request.
*/
public void gotLobbies (List lobbies);
}
/**
* Requests the list of lobby cateogories that are available on this
* server.
*
* @param client a connected, operational client instance.
* @param rsptarget the object reference that will receive and process
* the response. The response will come in the form of a method call
* to <code>handleGotCategories</code> or
* <code>handleRequestFailed</code>.
*
* @return the invocation request id of the generated request.
* @param listener the listener that will receive and process the
* response.
*/
public static int getCategories (Client client, Object rsptarget)
{
InvocationDirector invdir = client.getInvocationDirector();
Log.debug("Sending get categories.");
return invdir.invoke(
MODULE_NAME, GET_CATEGORIES_REQUEST, null, rsptarget);
}
public void getCategories (Client client, CategoriesListener listener);
/**
* Requests information on all active lobbies that match the specified
@@ -43,20 +57,9 @@ public class LobbyService
* @param client a connected, operational client instance.
* @param category the category of game for which a list of lobbies is
* desired.
* @param rsptarget the object reference that will receive and process
* the response. The response will come in the form of a method call
* to <code>handleLobbyList</code> or
* <code>handleRequestFailed</code>.
*
* @return the invocation request id of the generated request.
* @param listener the listener that will receive and process the
* response.
*/
public static int getLobbies (
Client client, String category, Object rsptarget)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { category };
Log.debug("Sending get lobbies [category=" + category + "].");
return invdir.invoke(
MODULE_NAME, GET_LOBBIES_REQUEST, args, rsptarget);
}
public void getLobbies (Client client, String category,
LobbiesListener listener);
}
@@ -1,5 +1,5 @@
//
// $Id: MiCasaServer.java,v 1.5 2002/03/28 22:32:32 mdb Exp $
// $Id: MiCasaServer.java,v 1.6 2002/08/14 19:07:49 mdb Exp $
package com.threerings.micasa.server;
@@ -40,7 +40,7 @@ public class MiCasaServer extends CrowdServer
clmgr.setClientClass(MiCasaClient.class);
// initialize our parlor manager
parmgr.init(invmgr);
parmgr.init(invmgr, plreg);
// initialize the lobby registry
lobreg.init(invmgr);
@@ -1,5 +1,5 @@
//
// $Id: ClientController.java,v 1.6 2002/05/17 21:22:46 shaper Exp $
// $Id: ClientController.java,v 1.7 2002/08/14 19:07:50 mdb Exp $
package com.threerings.micasa.simulator.client;
@@ -73,8 +73,11 @@ public class ClientController extends Controller
config = (GameConfig)
Class.forName(_info.gameConfigClass).newInstance();
// send the game creation request
SimulatorDirector.createGame(
// get the simulator service and use it to request that our
// game be created
SimulatorService sservice = (SimulatorService)
client.requireService(SimulatorService.class);
sservice.createGame(
client, config, _info.simClass, _info.playerCount);
// our work here is done, as the location manager will move us
@@ -1,5 +1,5 @@
//
// $Id: SimpleClient.java,v 1.6 2002/07/15 03:09:29 mdb Exp $
// $Id: SimpleClient.java,v 1.7 2002/08/14 19:07:50 mdb Exp $
package com.threerings.micasa.simulator.client;
@@ -53,7 +53,7 @@ public class SimpleClient
_frame.getFrame().addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent evt) {
// if we're logged on, log off
if (_client.loggedOn()) {
if (_client.isLoggedOn()) {
_client.logoff(true);
}
}
@@ -1,33 +0,0 @@
//
// $Id: SimulatorDirector.java,v 1.2 2002/04/15 16:28:02 shaper Exp $
package com.threerings.micasa.simulator.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.parlor.game.GameConfig;
import com.threerings.micasa.simulator.data.SimulatorCodes;
public class SimulatorDirector implements SimulatorCodes
{
/**
* Requests that a new game be created.
*
* @param client a connected, operational client instance.
* @param config the game config for the game to be created.
* @param simClass the class name of the simulant to create.
* @param playerCount the number of players in the game.
*
* @return the invocation request id of the generated request.
*/
public static int createGame (
Client client, GameConfig config, String simClass, int playerCount)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] {
config, simClass, new Integer(playerCount) };
return invdir.invoke(MODULE_NAME, CREATE_GAME_REQUEST, args, null);
}
}
@@ -0,0 +1,26 @@
//
// $Id: SimulatorService.java,v 1.1 2002/08/14 19:07:50 mdb Exp $
package com.threerings.micasa.simulator.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.game.GameConfig;
/**
* Provides access to simulator invocation services.
*/
public interface SimulatorService extends InvocationService
{
/**
* Requests that a new game be created.
*
* @param client a connected, operational client instance.
* @param config the game config for the game to be created.
* @param simClass the class name of the simulant to create.
* @param playerCount the number of players in the game.
*/
public void createGame (Client client, GameConfig config,
String simClass, int playerCount);
}
@@ -1,14 +0,0 @@
//
// $Id: SimulatorCodes.java,v 1.1 2002/04/15 16:28:02 shaper Exp $
package com.threerings.micasa.simulator.data;
public interface SimulatorCodes
{
/** The module name for the simulator services. */
public static final String MODULE_NAME = "simulator";
/** The message identifier for a create table request. */
public static final String CREATE_GAME_REQUEST = "CreateGame";
}
@@ -0,0 +1,34 @@
//
// $Id: SimulatorMarshaller.java,v 1.1 2002/08/14 19:07:51 mdb Exp $
package com.threerings.micasa.simulator.data;
import com.threerings.micasa.simulator.client.SimulatorService;
import com.threerings.parlor.game.GameConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* Provides the implementation of the {@link SimulatorService} 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 SimulatorMarshaller extends InvocationMarshaller
implements SimulatorService
{
/** The method id used to dispatch {@link #createGame} requests. */
public static final int CREATE_GAME = 1;
// documentation inherited from interface
public void createGame (Client arg1, GameConfig arg2, String arg3, int arg4)
{
sendRequest(arg1, CREATE_GAME, new Object[] {
arg2, arg3, new Integer(arg4)
});
}
// Class file generated on 00:26:00 08/11/02.
}
@@ -1,7 +1,7 @@
//
// $Id: Simulant.java,v 1.3 2002/02/05 22:11:51 mdb Exp $
// $Id: Simulant.java,v 1.4 2002/08/14 19:07:51 mdb Exp $
package com.threerings.micasa.simulator.client;
package com.threerings.micasa.simulator.server;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.MessageEvent;
@@ -9,19 +9,21 @@ import com.threerings.presents.dobj.MessageEvent;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.game.GameCodes;
import com.threerings.parlor.game.GameConfig;
import com.threerings.parlor.game.GameManager;
public abstract class Simulant implements GameCodes
public abstract class Simulant
{
/**
* Initializes the simulant with a body object and the game config for
* the game they'll be engaged in.
*/
public void init (BodyObject self, GameConfig config, DObjectManager omgr)
public void init (BodyObject self, GameConfig config,
GameManager gmgr, DObjectManager omgr)
{
_self = self;
_config = config;
_gmgr = gmgr;
_omgr = omgr;
}
@@ -37,9 +39,7 @@ public abstract class Simulant implements GameCodes
public void willEnterPlace (PlaceObject plobj)
{
// let the game manager know that the simulant's ready
MessageEvent mevt = new MessageEvent(
plobj.getOid(), PLAYER_READY_NOTIFICATION, null);
postEvent(mevt);
_gmgr.playerReady(_self);
}
/**
@@ -59,6 +59,9 @@ public abstract class Simulant implements GameCodes
/** The game config object. */
protected GameConfig _config;
/** The game manager for the game we're playing. */
protected GameManager _gmgr;
/** Our body object. */
protected BodyObject _self;
@@ -0,0 +1,52 @@
//
// $Id: SimulatorDispatcher.java,v 1.1 2002/08/14 19:07:51 mdb Exp $
package com.threerings.micasa.simulator.server;
import com.threerings.micasa.simulator.client.SimulatorService;
import com.threerings.micasa.simulator.data.SimulatorMarshaller;
import com.threerings.parlor.game.GameConfig;
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 SimulatorProvider}.
*/
public class SimulatorDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public SimulatorDispatcher (SimulatorProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new SimulatorMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case SimulatorMarshaller.CREATE_GAME:
((SimulatorProvider)provider).createGame(
source,
(GameConfig)args[0], (String)args[1], ((Integer)args[2]).intValue()
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: SimulatorManager.java,v 1.11 2002/07/02 21:14:23 shaper Exp $
// $Id: SimulatorManager.java,v 1.12 2002/08/14 19:07:51 mdb Exp $
package com.threerings.micasa.simulator.server;
@@ -14,25 +14,21 @@ import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.server.LocationProvider;
import com.threerings.crowd.server.PlaceManager;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.crowd.server.PlaceRegistry.CreationObserver;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.parlor.game.GameConfig;
import com.threerings.parlor.game.GameManager;
import com.threerings.parlor.game.GameObject;
import com.threerings.micasa.Log;
import com.threerings.micasa.simulator.client.Simulant;
import com.threerings.micasa.simulator.data.SimulatorCodes;
/**
* The simulator manager is responsible for handling the simulator
* services on the server side.
*/
public class SimulatorManager
implements SimulatorCodes
{
/**
* Initializes the simulator manager manager. This should be called by
@@ -48,7 +44,7 @@ public class SimulatorManager
{
// register our simulator provider
SimulatorProvider sprov = new SimulatorProvider(this);
invmgr.registerProvider(MODULE_NAME, sprov);
invmgr.registerDispatcher(new SimulatorDispatcher(sprov), true);
// keep these for later
_plreg = plreg;
@@ -172,7 +168,7 @@ public class SimulatorManager
// give the simulant its body
BodyObject bobj = (BodyObject)_sims.get(ii - 1);
sim.init(bobj, _config, _omgr);
sim.init(bobj, _config, _gmgr, _omgr);
// give the simulant a chance to engage in place antics
sim.willEnterPlace(_gobj);
@@ -180,7 +176,7 @@ public class SimulatorManager
// move the simulant into the game room since they have no
// location director to move them automagically
try {
LocationProvider.moveTo(bobj, _gobj.getOid());
_plreg.locprov.moveTo(bobj, _gobj.getOid());
} catch (Exception e) {
Log.warning("Failed to move simulant into room " +
"[e=" + e + "].");
@@ -1,9 +1,11 @@
//
// $Id: SimulatorProvider.java,v 1.1 2001/12/19 09:32:02 shaper Exp $
// $Id: SimulatorProvider.java,v 1.2 2002/08/14 19:07:51 mdb Exp $
package com.threerings.micasa.simulator.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.game.GameConfig;
@@ -14,7 +16,7 @@ import com.threerings.micasa.Log;
* side, passing them off to the {@link SimulatorManager}.
*/
public class SimulatorProvider
extends InvocationProvider
implements InvocationProvider
{
/**
* Constructs a simulator provider.
@@ -27,15 +29,14 @@ public class SimulatorProvider
/**
* Processes a request from the client to create a new game.
*/
public void handleCreateGameRequest (
BodyObject source, int invid, GameConfig config,
String simClass, int playerCount)
public void createGame (ClientObject caller, GameConfig config,
String simClass, int playerCount)
{
Log.info("handleCreateGameRequest [source=" + source +
Log.info("handleCreateGameRequest [caller=" + caller +
", config=" + config + ", simClass=" + simClass +
", playerCount=" + playerCount + "].");
_simmgr.createGame(source, config, simClass, playerCount);
_simmgr.createGame((BodyObject)caller, config, simClass, playerCount);
}
/** The simulator manager. */
@@ -0,0 +1,191 @@
//
// $Id: Invitation.java,v 1.1 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
import com.threerings.parlor.Log;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.GameConfig;
import com.threerings.parlor.util.ParlorContext;
/**
* The invitation class is used to track information related to
* outstanding invitations generated by or targeted to this client.
*/
public class Invitation
implements ParlorCodes, ParlorService.InviteListener
{
/** The unique id for this invitation (as assigned by the
* server). This is -1 until we receive an acknowledgement from
* the server that our invitation was delivered. */
public int inviteId = -1;
/** The name of the other user involved in this invitation. */
public String opponent;
/** The configuration of the game to be created. */
public GameConfig config;
/** Constructs a new invitation record. */
public Invitation (ParlorContext ctx, ParlorService pservice,
String opponent, GameConfig config,
InvitationResponseObserver observer)
{
_ctx = ctx;
_pservice = pservice;
_observer = observer;
this.opponent = opponent;
this.config = config;
}
/**
* Accepts this invitation.
*/
public void accept ()
{
// generate the invocation service request
_pservice.respond(_ctx.getClient(), inviteId,
INVITATION_ACCEPTED, null, this);
}
/**
* Refuses this invitation.
*
* @param message the message to deliver to the inviting user
* explaining the reason for the refusal or null if no message is to
* be provided.
*/
public void refuse (String message)
{
// generate the invocation service request
_pservice.respond(_ctx.getClient(), inviteId,
INVITATION_REFUSED, message, this);
}
/**
* Cancels this invitation.
*/
public void cancel ()
{
// if the invitation has not yet been acknowleged by the
// server, we make a note that it should be cancelled when we
// do receive the acknowlegement
if (inviteId == -1) {
_cancelled = true;
} else {
// otherwise, generate the invocation service request
_pservice.cancel(_ctx.getClient(), inviteId, this);
// and remove it from the pending table
_ctx.getParlorDirector().clearInvitation(this);
}
}
/**
* Counters this invitation with an invitation with different game
* configuration parameters.
*
* @param config the updated game configuration.
* @param observer the entity that will be notified if this
* counter-invitation is accepted, refused or countered.
*/
public void counter (GameConfig config, InvitationResponseObserver observer)
{
// update our observer (who will eventually be hearing back from
// the other client about their counter-invitation)
_observer = observer;
// generate the invocation service request
_pservice.respond(_ctx.getClient(), inviteId,
INVITATION_COUNTERED, config, this);
}
// documentation inherited from interface
public void inviteReceived (int inviteId)
{
// fill in our invitation id
inviteId = inviteId;
// if the invitation was cancelled before we heard back about
// it, we need to send off a cancellation request now
if (_cancelled) {
_pservice.cancel(_ctx.getClient(), inviteId, this);
} else {
// otherwise, put it in the pending invites table
_ctx.getParlorDirector().registerInvitation(this);
}
}
// documentation inherited from interface
public void requestFailed (String reason)
{
// let the observer know what's up
_observer.invitationRefused(this, reason);
}
/**
* Called by the parlor director when we receive a response to an
* invitation initiated by this client.
*/
protected void receivedResponse (int code, Object arg)
{
// make sure we have an observer to notify
if (_observer == null) {
Log.warning("No observer registered for invitation " +
this + ".");
return;
}
// notify the observer
try {
switch (code) {
case INVITATION_ACCEPTED:
_observer.invitationAccepted(this);
break;
case INVITATION_REFUSED:
_observer.invitationRefused(this, (String)arg);
break;
case INVITATION_COUNTERED:
_observer.invitationCountered(this, (GameConfig)arg);
break;
}
} catch (Exception e) {
Log.warning("Invitation response observer choked on response " +
"[code=" + code + ", arg=" + arg +
", invite=" + this + "].");
Log.logStackTrace(e);
}
// unless the invitation was countered, we can remove it from the
// pending table because it's resolved
if (code != INVITATION_COUNTERED) {
_ctx.getParlorDirector().clearInvitation(this);
}
}
/** Returns a string representation of this invitation record. */
public String toString ()
{
return "[inviteId=" + inviteId + ", opponent=" + opponent +
", config=" + config + ", observer=" + _observer +
", cancelled=" + _cancelled + "]";
}
/** Provides access to client services. */
protected ParlorContext _ctx;
/** Provides access to parlor services. */
protected ParlorService _pservice;
/** The entity to notify when we receive a response for this
* invitation. */
protected InvitationResponseObserver _observer;
/** A flag indicating that we were requested to cancel this
* invitation before we even heard back with an acknowledgement
* that it was received by the server. */
protected boolean _cancelled = false;
}
@@ -1,5 +1,5 @@
//
// $Id: InvitationHandler.java,v 1.2 2001/10/11 21:08:21 mdb Exp $
// $Id: InvitationHandler.java,v 1.3 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
@@ -16,18 +16,14 @@ public interface InvitationHandler
/**
* Called when an invitation is received from another player.
*
* @param inviteId this invitation's unique id.
* @param inviter the username of the user that sent the invitation.
* @param config the configuration of the game to which we are being
* invited.
* @param invite the received invitation.
*/
public void invitationReceived (int inviteId, String inviter,
GameConfig config);
public void invitationReceived (Invitation invite);
/**
* Called when an invitation is cancelled by the inviting player.
*
* @param inviteId this invitation's unique id.
* @param invite the cancelled invitation.
*/
public void invitationCancelled (int inviteId);
public void invitationCancelled (Invitation invite);
}
@@ -1,5 +1,5 @@
//
// $Id: InvitationResponseObserver.java,v 1.5 2001/10/11 21:08:21 mdb Exp $
// $Id: InvitationResponseObserver.java,v 1.6 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
@@ -17,30 +17,27 @@ public interface InvitationResponseObserver
/**
* Called if the invitation was accepted.
*
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param invite the invitation for which we received a response.
*/
public void invitationAccepted (int inviteId);
public void invitationAccepted (Invitation invite);
/**
* Called if the invitation was refused.
*
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param invite the invitation for which we received a response.
* @param message a message provided by the invited user explaining
* the reason for their refusal, or the empty string if no message was
* provided.
*/
public void invitationRefused (int inviteId, String message);
public void invitationRefused (Invitation invite, String message);
/**
* Called if the invitation was countered with an alternate game
* configuration.
*
* @param inviteId the unique id of the invitation for which we
* received a response.
* @param invite the invitation for which we received a response.
* @param config the game configuration proposed by the invited
* player.
*/
public void invitationCountered (int inviteId, GameConfig config);
public void invitationCountered (Invitation invite, GameConfig config);
}
@@ -0,0 +1,83 @@
//
// $Id: ParlorDecoder.java,v 1.1 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
import com.threerings.parlor.client.ParlorReceiver;
import com.threerings.parlor.game.GameConfig;
import com.threerings.presents.client.InvocationDecoder;
/**
* Dispatches calls to a {@link ParlorReceiver} instance.
*/
public class ParlorDecoder extends InvocationDecoder
{
/** The generated hash code used to identify this receiver class. */
public static final String RECEIVER_CODE = "5ef9ee0d359c42a9024498ee9aad119a";
/** The method id used to dispatch {@link ParlorReceiver#gameIsReady}
* notifications. */
public static final int GAME_IS_READY = 1;
/** The method id used to dispatch {@link ParlorReceiver#receivedInvite}
* notifications. */
public static final int RECEIVED_INVITE = 2;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteResponse}
* notifications. */
public static final int RECEIVED_INVITE_RESPONSE = 3;
/** The method id used to dispatch {@link ParlorReceiver#receivedInviteCancellation}
* notifications. */
public static final int RECEIVED_INVITE_CANCELLATION = 4;
/**
* Creates a decoder that may be registered to dispatch invocation
* service notifications to the specified receiver.
*/
public ParlorDecoder (ParlorReceiver receiver)
{
this.receiver = receiver;
}
// documentation inherited
public String getReceiverCode ()
{
return RECEIVER_CODE;
}
// documentation inherited
public void dispatchNotification (int methodId, Object[] args)
{
switch (methodId) {
case GAME_IS_READY:
((ParlorReceiver)receiver).gameIsReady(
((Integer)args[0]).intValue()
);
return;
case RECEIVED_INVITE:
((ParlorReceiver)receiver).receivedInvite(
((Integer)args[0]).intValue(), (String)args[1], (GameConfig)args[2]
);
return;
case RECEIVED_INVITE_RESPONSE:
((ParlorReceiver)receiver).receivedInviteResponse(
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (Object)args[2]
);
return;
case RECEIVED_INVITE_CANCELLATION:
((ParlorReceiver)receiver).receivedInviteCancellation(
((Integer)args[0]).intValue()
);
return;
default:
super.dispatchNotification(methodId, args);
}
}
// Generated on 11:25:47 08/12/02.
}
@@ -1,5 +1,5 @@
//
// $Id: ParlorDirector.java,v 1.15 2002/04/15 16:28:02 shaper Exp $
// $Id: ParlorDirector.java,v 1.16 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
@@ -7,7 +7,8 @@ import java.util.ArrayList;
import java.util.Iterator;
import com.samskivert.util.HashIntMap;
import com.threerings.presents.client.InvocationReceiver;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.parlor.Log;
import com.threerings.parlor.data.ParlorCodes;
@@ -21,8 +22,8 @@ import com.threerings.parlor.util.ParlorContext;
* that will actually create and display the user interface for the game
* that started.
*/
public class ParlorDirector
implements ParlorCodes, InvocationReceiver
public class ParlorDirector extends BasicDirector
implements ParlorCodes, ParlorReceiver
{
/**
* Constructs a parlor director and provides it with the parlor
@@ -35,12 +36,16 @@ public class ParlorDirector
*/
public ParlorDirector (ParlorContext ctx)
{
super(ctx);
_ctx = ctx;
// register ourselves with the invocation director as handling
// parlor notifications
_ctx.getClient().getInvocationDirector().
registerReceiver(MODULE_NAME, this);
// register as a session observer
_ctx.getClient().addClientObserver(this);
// register ourselves with the invocation director as a parlor
// notification receiver
_ctx.getClient().getInvocationDirector().registerReceiver(
new ParlorDecoder(this));
}
/**
@@ -83,135 +88,30 @@ public class ParlorDirector
* @param observer the entity that will be notified if this invitation
* is accepted, refused or countered.
*
* @return a unique id associated with this invitation that can be
* used to discern between various outstanding invitations by the
* invitation observer.
* @return an invitation object that can be used to manage the
* outstanding invitation.
*/
public int invite (String invitee, GameConfig config,
InvitationResponseObserver observer)
public Invitation invite (String invitee, GameConfig config,
InvitationResponseObserver observer)
{
// generate the invocation service request
int invid = ParlorService.invite(
_ctx.getClient(), invitee, config, this);
// create an invitation record and put it in the submitted table
Invitation invite = new Invitation(invitee, config, observer);
_submittedInvites.put(invid, invite);
return invite.inviteId;
// create the invitation record
Invitation invite = new Invitation(
_ctx, _pservice, invitee, config, observer);
// submit the invitation request to the server
_pservice.invite(_ctx.getClient(), invitee, config, invite);
// and return the invitation to the caller
return invite;
}
/**
* Accept an invitation.
*
* @param inviteId the id of the invitation to accept.
*/
public void accept (int inviteId)
// documentation inherited
protected void fetchServices (Client client)
{
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to accept non-existent " +
"invitation [inviteId=" + inviteId + "].");
return;
}
// generate the invocation service request
ParlorService.respond(_ctx.getClient(), invite.remoteId,
INVITATION_ACCEPTED, null, this);
// get a handle on our parlor services
_pservice = (ParlorService)client.requireService(ParlorService.class);
}
/**
* Refuse an invitation.
*
* @param inviteId the id of the invitation to accept.
* @param message the message to deliver to the inviting user
* explaining the reason for the refusal or null if no message is to
* be provided.
*/
public void refuse (int inviteId, String message)
{
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to accept non-existent " +
"invitation [inviteId=" + inviteId + "].");
return;
}
// generate the invocation service request
ParlorService.respond(_ctx.getClient(), invite.remoteId,
INVITATION_REFUSED, message, this);
}
/**
* Counters a received invitation with an invitation with different
* game configuration parameters.
*
* @param inviteId the id of the received invitation.
* @param config the updated game configuration.
* @param observer the entity that will be notified if this
* counter-invitation is accepted, refused or countered.
*/
public void counter (int inviteId, GameConfig config,
InvitationResponseObserver observer)
{
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to counter non-existent " +
"invitation [inviteId=" + inviteId +
", config=" + config + "].");
return;
}
// update the invitation record with the observer (who will
// eventually be hearing back from the other client about their
// counter-invitation)
invite.observer = observer;
// generate the invocation service request
ParlorService.respond(_ctx.getClient(), invite.remoteId,
INVITATION_COUNTERED, config, this);
}
/**
* Issues a request to cancel an outstanding invitation.
*
* @param inviteId the id of the invitation to cancel.
*/
public void cancel (int inviteId)
{
// look up the invitation record (oh the two separate key spaces
// humanity)
Invitation invite = getInviteByLocalId(inviteId);
if (invite == null) {
// complain if we didn't find a matching invitation
Log.warning("Received request to cancel non-existent " +
"invitation [inviteId=" + inviteId + "].");
return;
}
// if the invitation has not yet been acknowleged by the server,
// we make a note that it should be cancelled when we do receive
// the acknowlegement
if (invite.remoteId == -1) {
invite.cancelled = true;
} else {
// otherwise, generate the invocation service request
ParlorService.cancel(_ctx.getClient(), invite.remoteId, this);
// and remove it from the pending table
_pendingInvites.remove(invite.remoteId);
}
}
/**
* Called by the invocation services when a game in which we are a
* player is ready to begin.
*
* @param gameOid the object id of the game object.
*/
public void handleGameReadyNotification (int gameOid)
// documentation inherited from interface
public void gameIsReady (int gameOid)
{
Log.info("Handling game ready [goid=" + gameOid + "].");
@@ -229,55 +129,31 @@ public class ParlorDirector
}
}
/**
* Called by the invocation services when another user has invited us
* to play a game.
*
* @param remoteId the unique indentifier for this invitation (used
* when countering or responding).
* @param inviter the username of the inviting user.
* @param config the configuration information for the game to which
* we've been invited.
*/
public void handleInviteNotification (
// documentation inherited from interface
public void receivedInvite (
int remoteId, String inviter, GameConfig config)
{
// create an invitation record for this invitation
Invitation invite = new Invitation(inviter, config, null);
invite.remoteId = remoteId;
Invitation invite = new Invitation(
_ctx, _pservice, inviter, config, null);
invite.inviteId = remoteId;
// put it in the pending invitations table
_pendingInvites.put(remoteId, invite);
try {
// notify the invitation handler of the incoming invitation
_handler.invitationReceived(invite.inviteId, inviter, config);
_handler.invitationReceived(invite);
} catch (Exception e) {
Log.warning("Invitation handler choked on invite " +
"notification [inviteId=" + invite.inviteId +
", inviter=" + inviter +
", config=" + config + "].");
"notification " + invite + ".");
Log.logStackTrace(e);
}
}
/**
* Called by the invocation services when another user has responded
* to our invitation by either accepting, refusing or countering it.
*
* @param remoteId the unique indentifier for the invitation.
* @param code the response code, either {@link
* ParlorCodes#INVITATION_ACCEPTED} or {@link
* ParlorCodes#INVITATION_REFUSED} or {@link
* ParlorCodes#INVITATION_COUNTERED}.
* @param arg in the case of a refused invitation, a string
* containing a message provided by the invited user explaining the
* reason for refusal (the empty string if no explanation was
* provided). In the case of a countered invitation, a new game config
* object with the modified game configuration.
*/
public void handleRespondInviteNotification (
// documentation inherited from interface
public void receivedInviteResponse (
int remoteId, int code, Object arg)
{
// look up the invitation record for this invitation
@@ -286,226 +162,50 @@ public class ParlorDirector
Log.warning("Have no record of invitation for which we " +
"received a response?! [remoteId=" + remoteId +
", code=" + code + ", arg=" + arg + "].");
return;
}
// make sure we have an observer to notify
if (invite.observer == null) {
Log.warning("No observer registered for invitation " +
invite + ".");
return;
}
// notify the observer
try {
switch (code) {
case INVITATION_ACCEPTED:
invite.observer.invitationAccepted(invite.inviteId);
break;
case INVITATION_REFUSED:
invite.observer.invitationRefused(
invite.inviteId, (String)arg);
break;
case INVITATION_COUNTERED:
invite.observer.invitationCountered(
invite.inviteId, (GameConfig)arg);
break;
}
} catch (Exception e) {
Log.warning("Invitation response observer choked on response " +
"[code=" + code + ", arg=" + arg +
", invite=" + invite + "].");
Log.logStackTrace(e);
}
// unless the invitation was countered, we can remove it from the
// pending table because it's resolved
if (code != INVITATION_COUNTERED) {
_pendingInvites.remove(remoteId);
} else {
invite.receivedResponse(code, arg);
}
}
/**
* Called by the invocation services when an outstanding invitation
* has been cancelled by the inviting user.
*
* @param remoteId the unique indentifier for the invitation.
*/
public void handleCancelInviteNotification (int remoteId)
// documentation inherited from interface
public void receivedInviteCancellation (int remoteId)
{
// TBD
}
/**
* Called by the invocation services when an invitation request was
* received by the server and delivered to the intended invitee.
*
* @param invid the invocation id of the invitation request.
* Register a new invitation in our pending invitations table. The
* invitation will call this when it knows its invitation id.
*/
public void handleInviteReceived (int invid, int remoteId)
protected void registerInvitation (Invitation invite)
{
// remove the invitation record from the submitted table and put
// it in the pending table
Invitation invite = (Invitation)_submittedInvites.remove(invid);
if (invite == null) {
Log.warning("Received accepted notification for non-existent " +
"invitation request!? [invid=" + invid +
", remoteId=" + remoteId + "].");
return;
}
// now that we know the invitation's unique id, keep track of it
invite.remoteId = remoteId;
// if the invitation was cancelled before we heard back about it,
// we need to send off a cancellation request now
if (invite.cancelled) {
// generate the invocation service request to cancel it
ParlorService.cancel(_ctx.getClient(), invite.remoteId, this);
} else {
// otherwise, put it in the new table
_pendingInvites.put(remoteId, invite);
}
_pendingInvites.put(invite.inviteId, invite);
}
/**
* Called by the invocation services when an invitation request failed
* or was rejected for some reason (this is different than an
* invitation being refused by the invitee which is handled by {@link
* #handleRespondInviteNotification}).
*
* @param invid the invocation id of the invitation request.
* @param reason a reason code explaining the failure.
* Called by an invitation when it knows it is no longer and can be
* cleared from the pending invitations table.
*/
public void handleInviteFailed (int invid, String reason)
protected void clearInvitation (Invitation invite)
{
Log.info("Handling invite failed [invid=" + invid +
", reason=" + reason + "].");
// remove the invitation record from the submitted table and let
// the observer know that we're hosed
Invitation invite = (Invitation)_submittedInvites.remove(invid);
if (invite == null) {
Log.warning("Received failed notification for non-existent " +
"invitation request!? [invid=" + invid +
", reason=" + reason + "].");
return;
}
// let the observer know what's up
try {
invite.observer.invitationRefused(invite.inviteId, reason);
} catch (Exception e) {
Log.warning("Invite observer choked on refusal notification " +
"[invite=" + invite + ", reason=" + reason + "].");
Log.logStackTrace(e);
}
}
/**
* The invitation class is used to track information related to
* outstanding invitations generated by or targeted to this client.
*/
protected static class Invitation
{
/** A unique id for this invitation assigned on the client which
* is only used to allow the invitation observers to discriminate
* between multiple outstanding invitations. We'd use the remote
* id here except that that is not known until we receive the
* acknowlegedment from the server and we need to provide the
* caller with some sort of unique id at the time the request is
* generated. */
public int inviteId = _localInvitationId++;
/** The unique id for this invitation (as assigned by the
* server). This is -1 until we receive an acknowledgement from
* the server that our invitation was delivered. */
public int remoteId = -1;
/** The name of the other user involved in this invitation. */
public String opponent;
/** The configuration of the game to be created. */
public GameConfig config;
/** The entity to notify when we receive a response for this
* invitation. */
public InvitationResponseObserver observer;
/** A flag indicating that we were requested to cancel this
* invitation before we even heard back with an acknowledgement
* that it was received by the server. */
public boolean cancelled = false;
/** Constructs a new invitation record. */
public Invitation (String opponent, GameConfig config,
InvitationResponseObserver observer)
{
this.opponent = opponent;
this.config = config;
this.observer = observer;
}
/** Returns a string representation of this invitation record. */
public String toString ()
{
return "[inviteId=" + inviteId + ", remoteId=" + remoteId +
", opponent=" + opponent + ", config=" + config +
", observer=" + observer + ", cancelled=" + cancelled + "]";
}
}
/**
* Looks up an invitation by its local unique identifier. Oh the dual
* uid space humanity!
*/
protected Invitation getInviteByLocalId (int inviteId)
{
// first search the pending invites which is where we're most
// likely to find it
Iterator iter = _pendingInvites.values().iterator();
while (iter.hasNext()) {
Invitation match = (Invitation)iter.next();
if (match.inviteId == inviteId) {
return match;
}
}
// if that didn't work, look in the submitted invites
iter = _submittedInvites.values().iterator();
while (iter.hasNext()) {
Invitation match = (Invitation)iter.next();
if (match.inviteId == inviteId) {
return match;
}
}
// il n'existe pas
return null;
_pendingInvites.remove(invite.inviteId);
}
/** An active parlor context. */
protected ParlorContext _ctx;
/** Provides access to parlor server side services. */
protected ParlorService _pservice;
/** The entity that has registered itself to handle incoming
* invitation notifications. */
protected InvitationHandler _handler;
/** A table of submitted (but not acknowledged) invitation requests,
* keyed on invocation request id. */
protected HashIntMap _submittedInvites = new HashIntMap();
/** A table of acknowledged (but not yet accepted or refused)
* invitation requests, keyed on invitation id. */
protected HashIntMap _pendingInvites = new HashIntMap();
/** A counter used to assign a unique id to every invitation. */
protected static int _localInvitationId = 0;
/** We notify the entities on this list when we get a game ready
* notification. */
protected ArrayList _grobs = new ArrayList();
@@ -0,0 +1,66 @@
//
// $Id: ParlorReceiver.java,v 1.1 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
import com.threerings.presents.client.InvocationReceiver;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.GameConfig;
/**
* Defines, for the parlor services, a set of notifications delivered
* asynchronously by the server to the client. These are handled by the
* {@link ParlorDirector}.
*/
public interface ParlorReceiver extends InvocationReceiver
{
/**
* Dispatched to the client when a game in which they are a
* participant is ready for play. The client will then enter the game
* room which will trigger the loading of the appropriate game UI code
* and generally get things started.
*
* @param gameOid the object id of the game object.
*/
public void gameIsReady (int gameOid);
/**
* Called by the invocation services when another user has invited us
* to play a game.
*
* @param remoteId the unique indentifier for this invitation (used
* when countering or responding).
* @param inviter the username of the inviting user.
* @param config the configuration information for the game to which
* we've been invited.
*/
public void receivedInvite (
int remoteId, String inviter, GameConfig config);
/**
* Called by the invocation services when another user has responded
* to our invitation by either accepting, refusing or countering it.
*
* @param remoteId the indentifier for the invitation on question.
* @param code the response code, either {@link
* ParlorCodes#INVITATION_ACCEPTED} or {@link
* ParlorCodes#INVITATION_REFUSED} or {@link
* ParlorCodes#INVITATION_COUNTERED}.
* @param arg in the case of a refused invitation, a string
* containing a message provided by the invited user explaining the
* reason for refusal (the empty string if no explanation was
* provided). In the case of a countered invitation, a new game config
* object with the modified game configuration.
*/
public void receivedInviteResponse (
int remoteId, int code, Object arg);
/**
* Called by the invocation services when an outstanding invitation
* has been cancelled by the inviting user.
*
* @param remoteId the indentifier of the cancelled invitation.
*/
public void receivedInviteCancellation (int remoteId);
}
@@ -1,27 +1,33 @@
//
// $Id: ParlorService.java,v 1.12 2002/04/15 16:28:02 shaper Exp $
// $Id: ParlorService.java,v 1.13 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.presents.client.InvocationService;
import com.threerings.parlor.Log;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.GameConfig;
/**
* This class provides an interface to the various parlor services that
* are directly invokable by the client (by means of the invocation
* services). Presently these services are limited to the various
* matchmaking mechanisms. It is unlikely that client code will want to
* make direct use of this class, instead they would make use of the
* programmatic interface provided by the {@link ParlorDirector}.
*
* @see ParlorDirector
* Provides an interface to the various parlor invocation services.
* Presently these services are limited to the various matchmaking
* mechanisms. It is unlikely that client code will want to make direct
* use of this class, instead they would make use of the programmatic
* interface provided by the {@link ParlorDirector}.
*/
public class ParlorService implements ParlorCodes
public interface ParlorService extends InvocationService
{
/**
* Used to communicate responses to {@link #invite} requests.
*/
public static interface InviteListener extends InvocationListener
{
/**
* Called in response to a successful {@link #invite} request.
*/
public void inviteReceived (int inviteId);
}
/**
* You probably don't want to call this directly, but want to generate
* your invitation request via {@link ParlorDirector#invite}. Requests
@@ -33,18 +39,10 @@ public class ParlorService implements ParlorCodes
* @param invitee the username of the user to be invited.
* @param config a game config object detailing the type and
* configuration of the game to be created.
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int invite (Client client, String invitee,
GameConfig config, Object rsptgt)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { invitee, config };
return invdir.invoke(MODULE_NAME, INVITE_ID, args, rsptgt);
}
public void invite (Client client, String invitee, GameConfig config,
InviteListener listener);
/**
* You probably don't want to call this directly, but want to call one
@@ -62,21 +60,10 @@ public class ParlorService implements ParlorCodes
* the case of an invitation refusal or an updated game configuration
* object in the case of a counter-invitation, or null in the case of
* an accepted invitation).
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int respond (Client client, int inviteId, int code,
Object arg, Object rsptgt)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] {
new Integer(inviteId), new Integer(code), null };
// we can't have a null argument so we use the empty string
args[2] = (arg == null) ? "" : arg;
return invdir.invoke(MODULE_NAME, RESPOND_INVITE_ID, args, rsptgt);
}
public void respond (Client client, int inviteId, int code, Object arg,
InvocationListener listener);
/**
* You probably don't want to call this directly, but want to call
@@ -86,16 +73,18 @@ public class ParlorService implements ParlorCodes
* @param client a connected, operational client instance.
* @param inviteId the unique id previously assigned by the server to
* this invitation.
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int cancel (Client client, int inviteId, Object rsptgt)
public void cancel (Client client, int inviteId,
InvocationListener listener);
/**
* Used to communicate responses to {@link #createTable}, {@link
* #joinTable}, and {@link #leaveTable} requests.
*/
public static interface TableListener extends InvocationListener
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(inviteId) };
return invdir.invoke(MODULE_NAME, CANCEL_INVITE_ID, args, rsptgt);
public void tableCreated (int tableId);
}
/**
@@ -108,18 +97,10 @@ public class ParlorService implements ParlorCodes
* created table.
* @param config the game config for the game to be matchmade by the
* table.
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int createTable (
Client client, int lobbyOid, GameConfig config, Object rsptgt)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(lobbyOid), config };
return invdir.invoke(MODULE_NAME, CREATE_TABLE_REQUEST, args, rsptgt);
}
public void createTable (Client client, int lobbyOid, GameConfig config,
TableListener listener);
/**
* You probably don't want to call this directly, but want to call
@@ -132,41 +113,22 @@ public class ParlorService implements ParlorCodes
* to be added.
* @param position the position at the table to which this user desires
* to be added.
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int joinTable (Client client, int lobbyOid, int tableId,
int position, Object rsptgt)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(lobbyOid),
new Integer(tableId),
new Integer(position) };
return invdir.invoke(MODULE_NAME, JOIN_TABLE_REQUEST, args, rsptgt);
}
public void joinTable (Client client, int lobbyOid, int tableId,
int position, InvocationListener listener);
/**
* You probably don't want to call this directly, but want to call
* {@link TableDirector#leaveTable}. Requests that the current user
* be removed from the specified table.
* {@link TableDirector#leaveTable}. Requests that the current user be
* removed from the specified table.
*
* @param client a connected, operational client instance.
* @param lobbyOid the oid of the lobby that contains the table.
* @param tableId the unique id of the table from which this user
* wishes to be removed.
* @param rsptgt the object reference that will receive and process
* the response.
*
* @return the invocation request id of the generated request.
* @param listener will receive and process the response.
*/
public static int leaveTable (
Client client, int lobbyOid, int tableId, Object rsptgt)
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(lobbyOid),
new Integer(tableId) };
return invdir.invoke(MODULE_NAME, LEAVE_TABLE_REQUEST, args, rsptgt);
}
public void leaveTable (Client client, int lobbyOid, int tableId,
InvocationListener listener);
}
@@ -1,13 +1,16 @@
//
// $Id: TableDirector.java,v 1.7 2002/04/17 18:26:29 mdb Exp $
// $Id: TableDirector.java,v 1.8 2002/08/14 19:07:52 mdb Exp $
package com.threerings.parlor.client;
import java.util.ArrayList;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client;
import com.threerings.presents.dobj.EntryAddedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.SetListener;
import com.threerings.crowd.data.BodyObject;
@@ -37,8 +40,8 @@ import com.threerings.parlor.util.ParlorContext;
* lobby in which the table matchmaking takes place implements the {@link
* TableLobbyObject} interface.
*/
public class TableDirector
implements SetListener
public class TableDirector extends BasicDirector
implements SetListener, ParlorService.TableListener
{
/**
* Creates a new table director to manage tables with the specified
@@ -54,6 +57,8 @@ public class TableDirector
public TableDirector (
ParlorContext ctx, String tableField, TableObserver observer)
{
super(ctx);
// keep track of this stuff
_ctx = ctx;
_tableField = tableField;
@@ -140,8 +145,7 @@ public class TableDirector
}
// go ahead and issue the create request
ParlorService.createTable(
_ctx.getClient(), _lobby.getOid(), config, this);
_pservice.createTable(_ctx.getClient(), _lobby.getOid(), config, this);
}
/**
@@ -166,8 +170,8 @@ public class TableDirector
}
// issue the join request
ParlorService.joinTable(
_ctx.getClient(), _lobby.getOid(), tableId, position, this);
_pservice.joinTable(_ctx.getClient(), _lobby.getOid(), tableId,
position, this);
}
/**
@@ -185,8 +189,14 @@ public class TableDirector
}
// issue the leave request
ParlorService.leaveTable(
_ctx.getClient(), _lobby.getOid(), tableId, this);
_pservice.leaveTable(_ctx.getClient(), _lobby.getOid(), tableId, this);
}
// documentation inherited
protected void fetchServices (Client client)
{
// get a handle on our parlor services
_pservice = (ParlorService)client.requireService(ParlorService.class);
}
// documentation inherited
@@ -234,54 +244,19 @@ public class TableDirector
}
}
/**
* Called by the invocation services when a table creation request was
* received by the server and the table was successfully created.
*
* @param invid the invocation id of the invitation request.
*/
public void handleTableCreated (int invid, int tableId)
// documentation inherited from interface
public void tableCreated (int tableId)
{
// nothing much to do here
Log.info("Table creation succeeded [tableId=" + tableId + "].");
}
/**
* Called by the invocation services when a table creation request
* failed or was rejected for some reason.
*
* @param invid the invocation id of the creation request.
* @param reason a reason code explaining the failure.
*/
public void handleCreateTableFailed (int invid, String reason)
// documentation inherited from interface
public void requestFailed (String reason)
{
Log.warning("Table creation failed [reason=" + reason + "].");
}
/**
* Called by the invocation services when a join table request failed
* or was rejected for some reason.
*
* @param invid the invocation id of the join request.
* @param reason a reason code explaining the failure.
*/
public void handleJoinTableFailed (int invid, String reason)
{
Log.warning("Join table failed [reason=" + reason + "].");
}
/**
* Called by the invocation services when a leave table request failed
* or was rejected for some reason.
*
* @param invid the invocation id of the leave request.
* @param reason a reason code explaining the failure.
*/
public void handleLeaveTableFailed (int invid, String reason)
{
Log.warning("Leave table failed [reason=" + reason + "].");
}
/**
* Checks to see if we're a member of this table and notes it as our
* table, if so.
@@ -338,6 +313,9 @@ public class TableDirector
/** A context by which we can access necessary client services. */
protected ParlorContext _ctx;
/** Parlor server-side services. */
protected ParlorService _pservice;
/** The place object in which we're currently managing tables. */
protected PlaceObject _lobby;
@@ -1,48 +1,15 @@
//
// $Id: ParlorCodes.java,v 1.3 2002/04/17 18:26:29 mdb Exp $
// $Id: ParlorCodes.java,v 1.4 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.data;
import com.threerings.presents.data.InvocationCodes;
import com.threerings.parlor.client.ParlorDirector;
import com.threerings.parlor.client.TableDirector;
/**
* Contains codes used by the parlor invocation services.
*/
public interface ParlorCodes extends InvocationCodes
{
/** The module name for the parlor services. */
public static final String MODULE_NAME = "parlor";
/** The message identifier for a game ready notification. This is
* mapped by the invocation services to a call to {@link
* ParlorDirector#handleGameReadyNotification}. */
public static final String GAME_READY_NOTIFICATION = "GameReady";
/** The message identifier for an invitation creation request or
* notification. The notification is mapped by the invocation services
* to a call to {@link ParlorDirector#handleInviteNotification}. */
public static final String INVITE_ID = "Invite";
/** The response identifier for an accepted invite request. This is
* mapped by the invocation services to a call to {@link
* ParlorDirector#handleInviteReceived}. */
public static final String INVITE_RECEIVED_RESPONSE = "InviteReceived";
/** The message identifier for an invitation cancellation request or
* notification. The notification is mapped by the invocation services
* to a call to {@link
* ParlorDirector#handleCancelInviteNotification}. */
public static final String CANCEL_INVITE_ID = "CancelInvite";
/** The message identifier for an invitation response request or
* notification. The notification is mapped by the invocation services
* to a call to {@link
* ParlorDirector#handleRespondInviteNotification}. */
public static final String RESPOND_INVITE_ID = "RespondInvite";
/** The response code for an accepted invitation. */
public static final int INVITATION_ACCEPTED = 0;
@@ -57,20 +24,6 @@ public interface ParlorCodes extends InvocationCodes
* received. */
public static final String INVITEE_NOT_ONLINE = "m.invitee_not_online";
/** The message identifier for a create table request. */
public static final String CREATE_TABLE_REQUEST = "CreateTable";
/** The response identifier for a table created response. This is
* mapped by the invocation services to a call to {@link
* TableDirector#handleTableCreated}. */
public static final String TABLE_CREATED_RESPONSE = "TableCreated";
/** The message identifier for a join table request. */
public static final String JOIN_TABLE_REQUEST = "JoinTable";
/** The message identifier for a leave table request. */
public static final String LEAVE_TABLE_REQUEST = "LeaveTable";
/** An error code returned when a user requests to join a table that
* doesn't exist. */
public static final String NO_SUCH_TABLE = "m.no_such_table";
@@ -0,0 +1,166 @@
//
// $Id: ParlorMarshaller.java,v 1.1 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.data;
import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.client.ParlorService.InviteListener;
import com.threerings.parlor.client.ParlorService.TableListener;
import com.threerings.parlor.game.GameConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService.InvocationListener;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* Provides the implementation of the {@link ParlorService} 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 ParlorMarshaller extends InvocationMarshaller
implements ParlorService
{
// documentation inherited
public static class InviteMarshaller extends ListenerMarshaller
implements InviteListener
{
/** The method id used to dispatch {@link #inviteReceived}
* responses. */
public static final int INVITE_RECEIVED = 0;
// documentation inherited from interface
public void inviteReceived (int arg1)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, INVITE_RECEIVED,
new Object[] { new Integer(arg1) }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case INVITE_RECEIVED:
((InviteListener)listener).inviteReceived(
((Integer)args[0]).intValue());
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
// documentation inherited
public static class TableMarshaller extends ListenerMarshaller
implements TableListener
{
/** The method id used to dispatch {@link #tableCreated}
* responses. */
public static final int TABLE_CREATED = 0;
// documentation inherited from interface
public void tableCreated (int arg1)
{
omgr.postEvent(new InvocationResponseEvent(
callerOid, requestId, TABLE_CREATED,
new Object[] { new Integer(arg1) }));
}
// documentation inherited
public void dispatchResponse (int methodId, Object[] args)
{
switch (methodId) {
case TABLE_CREATED:
((TableListener)listener).tableCreated(
((Integer)args[0]).intValue());
return;
default:
super.dispatchResponse(methodId, args);
}
}
}
/** The method id used to dispatch {@link #invite} requests. */
public static final int INVITE = 1;
// documentation inherited from interface
public void invite (Client arg1, String arg2, GameConfig arg3, InviteListener arg4)
{
InviteMarshaller listener4 = new InviteMarshaller();
listener4.listener = arg4;
sendRequest(arg1, INVITE, new Object[] {
arg2, arg3, listener4
});
}
/** The method id used to dispatch {@link #respond} requests. */
public static final int RESPOND = 2;
// documentation inherited from interface
public void respond (Client arg1, int arg2, int arg3, Object arg4, InvocationListener arg5)
{
ListenerMarshaller listener5 = new ListenerMarshaller();
listener5.listener = arg5;
sendRequest(arg1, RESPOND, new Object[] {
new Integer(arg2), new Integer(arg3), arg4, listener5
});
}
/** The method id used to dispatch {@link #cancel} requests. */
public static final int CANCEL = 3;
// documentation inherited from interface
public void cancel (Client arg1, int arg2, InvocationListener arg3)
{
ListenerMarshaller listener3 = new ListenerMarshaller();
listener3.listener = arg3;
sendRequest(arg1, CANCEL, new Object[] {
new Integer(arg2), listener3
});
}
/** The method id used to dispatch {@link #createTable} requests. */
public static final int CREATE_TABLE = 4;
// documentation inherited from interface
public void createTable (Client arg1, int arg2, GameConfig arg3, TableListener arg4)
{
TableMarshaller listener4 = new TableMarshaller();
listener4.listener = arg4;
sendRequest(arg1, CREATE_TABLE, new Object[] {
new Integer(arg2), arg3, listener4
});
}
/** The method id used to dispatch {@link #joinTable} requests. */
public static final int JOIN_TABLE = 5;
// documentation inherited from interface
public void joinTable (Client arg1, int arg2, int arg3, int arg4, InvocationListener arg5)
{
ListenerMarshaller listener5 = new ListenerMarshaller();
listener5.listener = arg5;
sendRequest(arg1, JOIN_TABLE, new Object[] {
new Integer(arg2), new Integer(arg3), new Integer(arg4), listener5
});
}
/** The method id used to dispatch {@link #leaveTable} requests. */
public static final int LEAVE_TABLE = 6;
// documentation inherited from interface
public void leaveTable (Client arg1, int arg2, int arg3, InvocationListener arg4)
{
ListenerMarshaller listener4 = new ListenerMarshaller();
listener4.listener = arg4;
sendRequest(arg1, LEAVE_TABLE, new Object[] {
new Integer(arg2), new Integer(arg3), listener4
});
}
// Class file generated on 00:26:01 08/11/02.
}
@@ -1,5 +1,5 @@
//
// $Id: Table.java,v 1.10 2002/07/23 05:54:52 mdb Exp $
// $Id: Table.java,v 1.11 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.data;
@@ -237,7 +237,7 @@ public class Table
}
// documentation inherited
public Object getKey ()
public Comparable getKey ()
{
return tableId;
}
@@ -1,17 +0,0 @@
//
// $Id: GameCodes.java,v 1.4 2002/04/15 16:28:02 shaper Exp $
package com.threerings.parlor.game;
import com.threerings.presents.data.InvocationCodes;
/**
* Contains codes used by the game services.
*/
public interface GameCodes extends InvocationCodes
{
/** The message identifier for a player ready notification. This is
* delivered by the game controller when the client has loaded the
* user interface for the game and is ready to play. */
public static final String PLAYER_READY_NOTIFICATION = "PlayerReady";
}
@@ -1,5 +1,5 @@
//
// $Id: GameController.java,v 1.17 2002/06/18 02:35:10 shaper Exp $
// $Id: GameController.java,v 1.18 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
@@ -31,7 +31,7 @@ import com.threerings.parlor.util.ParlorContext;
* distributed object events.
*/
public abstract class GameController extends PlaceController
implements AttributeChangeListener, GameCodes
implements AttributeChangeListener
{
/**
* Initializes this game controller with the game configuration that
@@ -70,10 +70,16 @@ public abstract class GameController extends PlaceController
// and add ourselves as a listener
_gobj.addListener(this);
// finally let the game manager know that we're ready to roll
MessageEvent mevt = new MessageEvent(
_gobj.getOid(), PLAYER_READY_NOTIFICATION, null);
_ctx.getDObjectManager().postEvent(mevt);
// we don't want to claim to be finished until any derived classes
// that overrode this method have executed, so we'll queue up a
// runnable here that will let the game manager know that we're
// ready on the next pass through the distributed event loop
_ctx.getClient().getInvoker().invokeLater(new Runnable() {
public void run () {
// finally let the game manager know that we're ready to roll
_gobj.service.playerReady(_ctx.getClient());
}
});
}
/**
@@ -0,0 +1,51 @@
//
// $Id: GameDispatcher.java,v 1.1 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
import com.threerings.parlor.game.GameMarshaller;
import com.threerings.parlor.game.GameService;
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 GameProvider}.
*/
public class GameDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public GameDispatcher (GameProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new GameMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case GameMarshaller.PLAYER_READY:
((GameProvider)provider).playerReady(
source
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: GameManager.java,v 1.37 2002/06/19 23:41:25 shaper Exp $
// $Id: GameManager.java,v 1.38 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
@@ -7,11 +7,12 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.MessageEvent;
import com.threerings.crowd.chat.ChatProvider;
import com.threerings.crowd.chat.SpeakProvider;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
@@ -23,6 +24,7 @@ import com.threerings.crowd.server.PlaceManagerDelegate;
import com.threerings.parlor.Log;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.server.ParlorSender;
/**
* The game manager handles the server side management of a game. It
@@ -34,34 +36,15 @@ import com.threerings.parlor.data.ParlorCodes;
* bodies in that location.
*/
public class GameManager extends PlaceManager
implements ParlorCodes, GameCodes, AttributeChangeListener
implements ParlorCodes, GameProvider, AttributeChangeListener
{
// documentation inherited
protected Class getPlaceObjectClass ()
{
return GameObject.class;
}
// documentation inherited
protected void didInit ()
{
super.didInit();
// cast our configuration object (do we need to do this?)
_gconfig = (GameConfig)_config;
// register our message handlers
MessageHandler handler = new PlayerReadyHandler();
registerMessageHandler(PLAYER_READY_NOTIFICATION, handler);
}
/**
* Initializes the game manager with the supplied game configuration
* object. This happens before startup and before the game object has
* been created.
* Provides the game manager with a list of the usernames of all
* players in the game. This happens before startup and before the
* game object has been created.
*
* @param players the usernames of all of the players in this game or
* null if the game has no specific set of players.
* a zero-length array if the game has no specific set of players.
*/
public void setPlayers (String[] players)
{
@@ -167,8 +150,9 @@ public class GameManager extends PlaceManager
public void systemMessage (
String msgbundle, String msg, boolean waitForStart)
{
if (waitForStart && ((_gameobj == null) ||
(_gameobj.state == GameObject.AWAITING_PLAYERS))) {
if (waitForStart &&
((_gameobj == null) ||
(_gameobj.state == GameObject.AWAITING_PLAYERS))) {
// queue up the message.
if (_startmsgs == null) {
_startmsgs = new ArrayList();
@@ -179,7 +163,13 @@ public class GameManager extends PlaceManager
}
// otherwise, just deliver the message
ChatProvider.sendSystemMessage(_gameobj.getOid(), msgbundle, msg);
SpeakProvider.sendSystemSpeak(_gameobj, msgbundle, msg);
}
// documentation inherited
protected Class getPlaceObjectClass ()
{
return GameObject.class;
}
// documentation inherited
@@ -193,24 +183,27 @@ public class GameManager extends PlaceManager
// stick the players into the game object
_gameobj.setPlayers(_players);
// create and fill in our game service object
GameMarshaller service = (GameMarshaller)
_invmgr.registerDispatcher(new GameDispatcher(this), false);
_gameobj.setService(service);
// let the players of this game know that we're ready to roll (if
// we have a specific set of players)
if (_players != null) {
Object[] args = new Object[] {
new Integer(_gameobj.getOid()) };
int gameOid = _gameobj.getOid();
for (int i = 0; i < _players.length; i++) {
BodyObject bobj = CrowdServer.lookupBody(_players[i]);
if (bobj == null) {
Log.warning("Unable to deliver game ready to " +
"non-existent player " +
"[gameOid=" + _gameobj.getOid() +
"[gameOid=" + gameOid +
", player=" + _players[i] + "].");
continue;
}
// deliver a game ready notification to the player
CrowdServer.invmgr.sendNotification(
bobj.getOid(), MODULE_NAME, GAME_READY_NOTIFICATION, args);
ParlorSender.gameIsReady(bobj, gameOid);
}
}
}
@@ -448,6 +441,35 @@ public class GameManager extends PlaceManager
});
}
// documentation inherited from interface
public void playerReady (ClientObject caller)
{
BodyObject plobj = (BodyObject)caller;
// make a note of this player's oid
int pidx = _gameobj.getPlayerIndex(plobj.username);
if (pidx == -1) {
Log.warning("Received playerReady() from non-player? " +
"[caller=" + caller + "].");
return;
}
_playerOids[pidx] = plobj.getOid();
// and check to see if we're all set
boolean allSet = true;
for (int ii = 0; ii < _players.length; ii++) {
if ((_playerOids[ii] == 0) &&
((_AIs == null) || (_AIs[ii] == -1))) {
allSet = false;
}
}
// if everyone is now ready to go, make a note of it
if (allSet) {
playersAllHere();
}
}
// documentation inherited
public void attributeChanged (AttributeChangedEvent event)
{
@@ -463,39 +485,6 @@ public class GameManager extends PlaceManager
}
}
/** Handles player ready notifications. */
protected class PlayerReadyHandler implements MessageHandler
{
public void handleEvent (MessageEvent event, PlaceManager pmgr)
{
int cloid = event.getSourceOid();
BodyObject body = (BodyObject)CrowdServer.omgr.getObject(cloid);
if (body == null) {
Log.warning("Player sent am ready notification and then " +
"disappeared [event=" + event + "].");
return;
}
// make a note of this player's oid and check to see if we're
// all set at the same time
boolean allSet = true;
for (int i = 0; i < _players.length; i++) {
if (_players[i].equals(body.username)) {
_playerOids[i] = body.getOid();
}
if ((_playerOids[i] == 0) &&
((_AIs == null) || (_AIs[i] == -1))) {
allSet = false;
}
}
// if everyone is now ready to go, make a note of it
if (allSet) {
playersAllHere();
}
}
}
/**
* A helper operation to distribute AI ticks to our delegates.
*/
@@ -515,9 +504,6 @@ public class GameManager extends PlaceManager
protected byte _level;
}
/** A reference to our game configuration. */
protected GameConfig _gconfig;
/** A reference to our game object. */
protected GameObject _gameobj;
@@ -0,0 +1,33 @@
//
// $Id: GameMarshaller.java,v 1.1 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
import com.threerings.parlor.game.GameService;
import com.threerings.presents.client.Client;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.InvocationResponseEvent;
/**
* Provides the implementation of the {@link GameService} 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 GameMarshaller extends InvocationMarshaller
implements GameService
{
/** The method id used to dispatch {@link #playerReady} requests. */
public static final int PLAYER_READY = 1;
// documentation inherited from interface
public void playerReady (Client arg1)
{
sendRequest(arg1, PLAYER_READY, new Object[] {
});
}
// Class file generated on 00:26:01 08/11/02.
}
@@ -1,5 +1,5 @@
//
// $Id: GameObject.dobj,v 1.10 2002/08/09 23:34:10 shaper Exp $
// $Id: GameObject.dobj,v 1.11 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
@@ -33,6 +33,9 @@ public class GameObject extends PlaceObject
/** A game state constant indicating that the game was cancelled. */
public static final int CANCELLED = GAME_OVER+3;
/** Provides general game invocation services. */
public GameMarshaller service;
/** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY},
* {@link #GAME_OVER}, or {@link #CANCELLED}. */
public int state;
@@ -1,5 +1,5 @@
//
// $Id: GameObject.java,v 1.5 2002/08/09 23:34:10 shaper Exp $
// $Id: GameObject.java,v 1.6 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
@@ -20,6 +20,9 @@ import com.threerings.crowd.data.PlaceObject;
*/
public class GameObject extends PlaceObject
{
/** The field name of the <code>service</code> field. */
public static final String SERVICE = "service";
/** The field name of the <code>state</code> field. */
public static final String STATE = "state";
@@ -45,6 +48,9 @@ public class GameObject extends PlaceObject
/** A game state constant indicating that the game was cancelled. */
public static final int CANCELLED = GAME_OVER+3;
/** Provides general game invocation services. */
public GameMarshaller service;
/** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY},
* {@link #GAME_OVER}, or {@link #CANCELLED}. */
public int state;
@@ -68,6 +74,20 @@ public class GameObject extends PlaceObject
ListUtil.indexOfEqual(players, username);
}
/**
* Requests that the <code>service</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.
*/
public void setService (GameMarshaller service)
{
this.service = service;
requestAttributeChange(SERVICE, service);
}
/**
* Requests that the <code>state</code> field be set to the specified
* value. The local value will be updated immediately and an event
@@ -0,0 +1,20 @@
//
// $Id: GameProvider.java,v 1.1 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider;
/**
* Provides access to the server-side implementation of the game
* invocation services.
*/
public interface GameProvider extends InvocationProvider
{
/**
* Called when the client has sent a {@link GameService#playerReady}
* service request.
*/
public void playerReady (ClientObject caller);
}
@@ -0,0 +1,20 @@
//
// $Id: GameService.java,v 1.1 2002/08/14 19:07:53 mdb Exp $
package com.threerings.parlor.game;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService;
/**
* Provides services used by game clients to request that actions be taken
* by the game manager.
*/
public interface GameService extends InvocationService
{
/**
* Lets the game manager know that the calling player is in the game
* room and ready to play.
*/
public void playerReady (Client client);
}
@@ -0,0 +1,90 @@
//
// $Id: ParlorDispatcher.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.parlor.server;
import com.threerings.parlor.client.ParlorService;
import com.threerings.parlor.client.ParlorService.InviteListener;
import com.threerings.parlor.client.ParlorService.TableListener;
import com.threerings.parlor.data.ParlorMarshaller;
import com.threerings.parlor.game.GameConfig;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationService.InvocationListener;
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 ParlorProvider}.
*/
public class ParlorDispatcher extends InvocationDispatcher
{
/**
* Creates a dispatcher that may be registered to dispatch invocation
* service requests for the specified provider.
*/
public ParlorDispatcher (ParlorProvider provider)
{
this.provider = provider;
}
// documentation inherited
public InvocationMarshaller createMarshaller ()
{
return new ParlorMarshaller();
}
// documentation inherited
public void dispatchRequest (
ClientObject source, int methodId, Object[] args)
throws InvocationException
{
switch (methodId) {
case ParlorMarshaller.INVITE:
((ParlorProvider)provider).invite(
source,
(String)args[0], (GameConfig)args[1], (InviteListener)args[2]
);
return;
case ParlorMarshaller.RESPOND:
((ParlorProvider)provider).respond(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (Object)args[2], (InvocationListener)args[3]
);
return;
case ParlorMarshaller.CANCEL:
((ParlorProvider)provider).cancel(
source,
((Integer)args[0]).intValue(), (InvocationListener)args[1]
);
return;
case ParlorMarshaller.CREATE_TABLE:
((ParlorProvider)provider).createTable(
source,
((Integer)args[0]).intValue(), (GameConfig)args[1], (TableListener)args[2]
);
return;
case ParlorMarshaller.JOIN_TABLE:
((ParlorProvider)provider).joinTable(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue(), (InvocationListener)args[3]
);
return;
case ParlorMarshaller.LEAVE_TABLE:
((ParlorProvider)provider).leaveTable(
source,
((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (InvocationListener)args[2]
);
return;
default:
super.dispatchRequest(source, methodId, args);
}
}
}
@@ -1,15 +1,15 @@
//
// $Id: ParlorManager.java,v 1.18 2002/04/15 16:28:02 shaper Exp $
// $Id: ParlorManager.java,v 1.19 2002/08/14 19:07:54 mdb Exp $
package com.threerings.parlor.server;
import com.samskivert.util.HashIntMap;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.parlor.Log;
import com.threerings.parlor.data.ParlorCodes;
@@ -26,6 +26,9 @@ import com.threerings.parlor.game.GameManager;
public class ParlorManager
implements ParlorCodes
{
/** Provides the server-side implementation of the parlor services. */
public ParlorProvider parprov;
/**
* Initializes the parlor manager. This should be called by the server
* that is making use of the parlor services on the single instance of
@@ -33,15 +36,17 @@ public class ParlorManager
*
* @param invmgr a reference to the invocation manager in use by this
* server.
* @param plreg a reference to the place registry to be used by the
* parlor manager when creating game places.
*/
public void init (InvocationManager invmgr)
public void init (InvocationManager invmgr, PlaceRegistry plreg)
{
// register our invocation provider
ParlorProvider pprov = new ParlorProvider(this);
invmgr.registerProvider(MODULE_NAME, pprov);
// create and register our invocation provider
parprov = new ParlorProvider(this);
invmgr.registerDispatcher(new ParlorDispatcher(parprov), true);
// keep this around for later
_invmgr = invmgr;
// keep this for later
_plreg = plreg;
}
/**
@@ -56,14 +61,14 @@ public class ParlorManager
* @return the invitation identifier for the newly created invitation
* record.
*
* @exception ServiceFailedException thrown if the invitation was not
* @exception InvocationException thrown if the invitation was not
* able to be processed for some reason (like the invited player has
* requested not to be disturbed). The explanation will be provided in
* the message data of the exception.
*/
public int invite (BodyObject inviter, BodyObject invitee,
GameConfig config)
throws ServiceFailedException
throws InvocationException
{
// Log.info("Received invitation request [inviter=" + inviter +
// ", invitee=" + invitee + ", config=" + config + "].");
@@ -79,10 +84,8 @@ public class ParlorManager
_invites.put(invite.inviteId, invite);
// deliver an invite notification to the invitee
Object[] args = new Object[] {
new Integer(invite.inviteId), inviter.username, config };
_invmgr.sendNotification(
invitee.getOid(), MODULE_NAME, INVITE_ID, args);
ParlorSender.sendInvite(invitee, invite.inviteId,
inviter.username, config);
// and let the caller know the invite id we assigned
return invite.inviteId;
@@ -126,10 +129,8 @@ public class ParlorManager
// let the other user know that a response was made to this
// invitation
Object[] args = new Object[] {
new Integer(invite.inviteId), new Integer(code), arg };
_invmgr.sendNotification(
invite.inviter.getOid(), MODULE_NAME, RESPOND_INVITE_ID, args);
ParlorSender.sendInviteResponse(
invite.inviter, invite.inviteId, code, arg);
switch (code) {
case INVITATION_ACCEPTED:
@@ -187,7 +188,7 @@ public class ParlorManager
// started up (which is done by the place registry once the
// game object creation has completed)
GameManager gmgr = (GameManager)
CrowdServer.plreg.createPlace(invite.config, null);
_plreg.createPlace(invite.config, null);
// provide the game manager with the player list
gmgr.setPlayers(new String[] {
@@ -242,8 +243,8 @@ public class ParlorManager
}
}
/** A reference to the invocation manager in operation on this server. */
protected InvocationManager _invmgr;
/** The place registry with which we operate. */
protected PlaceRegistry _plreg;
/** The table of pending invitations. */
protected HashIntMap _invites = new HashIntMap();
@@ -1,16 +1,20 @@
//
// $Id: ParlorProvider.java,v 1.12 2002/04/17 18:26:30 mdb Exp $
// $Id: ParlorProvider.java,v 1.13 2002/08/14 19:07:54 mdb Exp $
package com.threerings.parlor.server;
import com.threerings.presents.client.InvocationService.InvocationListener;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationProvider;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdServer;
import com.threerings.crowd.server.PlaceManager;
import com.threerings.parlor.Log;
import com.threerings.parlor.client.ParlorService.InviteListener;
import com.threerings.parlor.client.ParlorService.TableListener;
import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.game.GameConfig;
@@ -20,7 +24,7 @@ import com.threerings.parlor.game.GameConfig;
* Primarily these are the matchmaking mechanisms.
*/
public class ParlorProvider
extends InvocationProvider implements ParlorCodes
implements InvocationProvider, ParlorCodes
{
/**
* Constructs a parlor provider instance which will be used to handle
@@ -40,82 +44,79 @@ public class ParlorProvider
* Processes a request from the client to invite another user to play
* a game.
*/
public void handleInviteRequest (
BodyObject source, int invid, String invitee, GameConfig config)
throws ServiceFailedException
public void invite (ClientObject caller, String invitee,
GameConfig config, InviteListener listener)
throws InvocationException
{
// Log.info("Handling invite request [source=" + source +
// ", invid=" + invid + ", invitee=" + invitee +
// ", config=" + config + "].");
// ", invitee=" + invitee + ", config=" + config + "].");
BodyObject source = (BodyObject)caller;
String rsp = null;
// ensure that the invitee is online at present
BodyObject target = CrowdServer.lookupBody(invitee);
if (target == null) {
throw new ServiceFailedException(INVITEE_NOT_ONLINE);
throw new InvocationException(INVITEE_NOT_ONLINE);
}
// submit the invite request to the parlor manager
int inviteId = _pmgr.invite(source, target, config);
sendResponse(source, invid, INVITE_RECEIVED_RESPONSE,
new Integer(inviteId));
listener.inviteReceived(inviteId);
}
/**
* Processes a request from the client to respond to an outstanding
* invitation by accepting, refusing, or countering it.
*/
public void handleRespondInviteRequest (
BodyObject source, int invid, int inviteId, int code, Object arg)
public void respond (ClientObject caller, int inviteId, int code,
Object arg, InvocationListener listener)
{
// pass this on to the parlor manager
_pmgr.respondToInvite(source, inviteId, code, arg);
_pmgr.respondToInvite((BodyObject)caller, inviteId, code, arg);
}
/**
* Processes a request from the client to cancel an outstanding
* invitation.
*/
public void handleCancelInviteRequest (
BodyObject source, int invid, int inviteId)
public void cancel (ClientObject caller, int inviteId,
InvocationListener listener)
{
// pass this on to the parlor manager
_pmgr.cancelInvite(source, inviteId);
_pmgr.cancelInvite((BodyObject)caller, inviteId);
}
/**
* Processes a request from the client to create a new table.
*/
public void handleCreateTableRequest (
BodyObject source, int invid, int lobbyOid, GameConfig config)
throws ServiceFailedException
public void createTable (ClientObject caller, int lobbyOid,
GameConfig config, TableListener listener)
throws InvocationException
{
Log.info("Handling create table request [source=" + source +
", invid=" + invid + ", lobbyOid=" + lobbyOid +
", config=" + config + "].");
Log.info("Handling create table request [caller=" + caller +
", lobbyOid=" + lobbyOid + ", config=" + config + "].");
// pass the creation request on to the table manager
TableManager tmgr = getTableManager(lobbyOid);
int tableId = tmgr.createTable(source, config);
sendResponse(source, invid, TABLE_CREATED_RESPONSE,
new Integer(tableId));
int tableId = tmgr.createTable((BodyObject)caller, config);
listener.tableCreated(tableId);
}
/**
* Processes a request from the client to join an existing table.
*/
public void handleJoinTableRequest (
BodyObject source, int invid, int lobbyOid, int tableId, int position)
throws ServiceFailedException
public void joinTable (ClientObject caller, int lobbyOid, int tableId,
int position, InvocationListener listener)
throws InvocationException
{
Log.info("Handling join table request [source=" + source +
", invid=" + invid + ", lobbyOid=" + lobbyOid +
", tableId=" + tableId + ", position=" + position + "].");
Log.info("Handling join table request [caller=" + caller +
", lobbyOid=" + lobbyOid + ", tableId=" + tableId +
", position=" + position + "].");
// pass the join request on to the table manager
TableManager tmgr = getTableManager(lobbyOid);
tmgr.joinTable(source, tableId, position);
tmgr.joinTable((BodyObject)caller, tableId, position);
// there is normally no success response. the client will see
// themselves show up in the table that they joined
@@ -124,17 +125,16 @@ public class ParlorProvider
/**
* Processes a request from the client to leave an existing table.
*/
public void handleLeaveTableRequest (
BodyObject source, int invid, int lobbyOid, int tableId)
throws ServiceFailedException
public void leaveTable (ClientObject caller, int lobbyOid, int tableId,
InvocationListener listener)
throws InvocationException
{
Log.info("Handling leave table request [source=" + source +
", invid=" + invid + ", lobbyOid=" + lobbyOid +
", tableId=" + tableId + "].");
Log.info("Handling leave table request [caller=" + caller +
", lobbyOid=" + lobbyOid + ", tableId=" + tableId + "].");
// pass the join request on to the table manager
TableManager tmgr = getTableManager(lobbyOid);
tmgr.leaveTable(source, tableId);
tmgr.leaveTable((BodyObject)caller, tableId);
// there is normally no success response. the client will see
// themselves removed from the table they just left
@@ -145,25 +145,25 @@ public class ParlorProvider
* casts it to a table lobby manager and obtains the associated table
* manager reference.
*
* @exception ServiceFailedException thrown if something goes wrong
* @exception InvocationException thrown if something goes wrong
* along the way like no place manager exists or the place manager
* that does exist doesn't implement table lobby manager.
*/
protected TableManager getTableManager (int lobbyOid)
throws ServiceFailedException
throws InvocationException
{
PlaceManager plmgr = CrowdServer.plreg.getPlaceManager(lobbyOid);
if (plmgr == null) {
Log.warning("No place manager exists from which to obtain " +
"table manager reference [ploid=" + lobbyOid + "].");
throw new ServiceFailedException(INTERNAL_ERROR);
throw new InvocationException(INTERNAL_ERROR);
}
// sanity check
if (!(plmgr instanceof TableManagerProvider)) {
Log.warning("Place manager not a table lobby manager " +
"[plmgr=" + plmgr + "].");
throw new ServiceFailedException(INTERNAL_ERROR);
throw new InvocationException(INTERNAL_ERROR);
}
return ((TableManagerProvider)plmgr).getTableManager();
@@ -0,0 +1,67 @@
//
// $Id: ParlorSender.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.parlor.server;
import com.threerings.parlor.client.ParlorDecoder;
import com.threerings.parlor.client.ParlorReceiver;
import com.threerings.parlor.game.GameConfig;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationSender;
/**
* Used to issue notifications to a {@link ParlorReceiver} instance on a
* client.
*/
public class ParlorSender extends InvocationSender
{
/**
* Issues a notification that will result in a call to {@link
* ParlorReceiver#gameIsReady} on a client.
*/
public static void gameIsReady (
ClientObject target, int arg1)
{
sendNotification(
target, ParlorDecoder.RECEIVER_CODE, ParlorDecoder.GAME_IS_READY,
new Object[] { new Integer(arg1) });
}
/**
* Issues a notification that will result in a call to {@link
* ParlorReceiver#receivedInvite} on a client.
*/
public static void sendInvite (
ClientObject target, int arg1, String arg2, GameConfig arg3)
{
sendNotification(
target, ParlorDecoder.RECEIVER_CODE, ParlorDecoder.RECEIVED_INVITE,
new Object[] { new Integer(arg1), arg2, arg3 });
}
/**
* Issues a notification that will result in a call to {@link
* ParlorReceiver#receivedInviteResponse} on a client.
*/
public static void sendInviteResponse (
ClientObject target, int arg1, int arg2, Object arg3)
{
sendNotification(
target, ParlorDecoder.RECEIVER_CODE, ParlorDecoder.RECEIVED_INVITE_RESPONSE,
new Object[] { new Integer(arg1), new Integer(arg2), arg3 });
}
/**
* Issues a notification that will result in a call to {@link
* ParlorReceiver#receivedInviteCancellation} on a client.
*/
public static void sendInviteCancellation (
ClientObject target, int arg1)
{
sendNotification(
target, ParlorDecoder.RECEIVER_CODE, ParlorDecoder.RECEIVED_INVITE_CANCELLATION,
new Object[] { new Integer(arg1) });
}
// Generated on 11:25:47 08/12/02.
}
@@ -1,5 +1,5 @@
//
// $Id: TableManager.java,v 1.6 2002/07/23 05:54:52 mdb Exp $
// $Id: TableManager.java,v 1.7 2002/08/14 19:07:54 mdb Exp $
package com.threerings.parlor.server;
@@ -14,7 +14,7 @@ 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.server.ServiceFailedException;
import com.threerings.presents.server.InvocationException;
import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.PlaceObject;
@@ -69,12 +69,12 @@ public class TableManager
*
* @return the id of the newly created table.
*
* @exception ServiceFailedException thrown if the table creation was
* @exception InvocationException thrown if the table creation was
* not able processed for some reason. The explanation will be
* provided in the message data of the exception.
*/
public int createTable (BodyObject creator, GameConfig config)
throws ServiceFailedException
throws InvocationException
{
// make sure the game config implements TableConfig
if (!(config instanceof TableConfig)) {
@@ -82,7 +82,7 @@ public class TableManager
"using the table services [creator=" + creator +
", lobby=" + _plobj.getOid() +
", config=" + config + "].");
throw new ServiceFailedException(INTERNAL_ERROR);
throw new InvocationException(INTERNAL_ERROR);
}
// make sure the creator is an occupant of the lobby in which
@@ -91,7 +91,7 @@ public class TableManager
Log.warning("Requested to create a table in a lobby not " +
"occupied by the creator [creator=" + creator +
", loboid=" + _plobj.getOid() + "].");
throw new ServiceFailedException(INTERNAL_ERROR);
throw new InvocationException(INTERNAL_ERROR);
}
// create a brand spanking new table
@@ -105,7 +105,7 @@ public class TableManager
"table!? [table=" + table + ", creator=" + creator +
", error=" + error + "].");
// bail out now and abort the table creation process
throw new ServiceFailedException(error);
throw new InvocationException(error);
}
// stick the table into the table lobby object
@@ -126,7 +126,7 @@ public class TableManager
* the specified position. If the user successfully joins the table,
* the function will return normally. If they are not able to join for
* some reason (the slot is already full, etc.), a {@link
* ServiceFailedException} will be thrown with a message code that
* InvocationException} will be thrown with a message code that
* describes the reason for failure. If the user does successfully
* join, they will be added to the table entry in the tables set in
* the place object that is hosting the table.
@@ -136,17 +136,17 @@ public class TableManager
* @param tableId the id of the table to be joined.
* @param position the position at which to join the table.
*
* @exception ServiceFailedException thrown if the joining was not
* able processed for some reason. The explanation will be provided in
* the message data of the exception.
* @exception InvocationException thrown if the joining was not able
* processed for some reason. The explanation will be provided in the
* message data of the exception.
*/
public void joinTable (BodyObject joiner, int tableId, int position)
throws ServiceFailedException
throws InvocationException
{
// look the table up
Table table = (Table)_tables.get(tableId);
if (table == null) {
throw new ServiceFailedException(NO_SUCH_TABLE);
throw new InvocationException(NO_SUCH_TABLE);
}
// request that the user be added to the table at that position
@@ -154,7 +154,7 @@ public class TableManager
table.setOccupant(position, joiner.username, joiner.getOid());
// if that failed, report the error
if (error != null) {
throw new ServiceFailedException(error);
throw new InvocationException(error);
}
// determine whether or not it's time to start the game
@@ -188,29 +188,29 @@ public class TableManager
* table. If the user successfully leaves the table, the function will
* return normally. If they are not able to leave for some reason
* (they aren't sitting at the table, etc.), a {@link
* ServiceFailedException} will be thrown with a message code that
* InvocationException} will be thrown with a message code that
* describes the reason for failure.
*
* @param leaver the body object of the user that wishes to leave the
* table.
* @param tableId the id of the table to be left.
*
* @exception ServiceFailedException thrown if the leaving was not
* able processed for some reason. The explanation will be provided in
* the message data of the exception.
* @exception InvocationException thrown if the leaving was not able
* processed for some reason. The explanation will be provided in the
* message data of the exception.
*/
public void leaveTable (BodyObject leaver, int tableId)
throws ServiceFailedException
throws InvocationException
{
// look the table up
Table table = (Table)_tables.get(tableId);
if (table == null) {
throw new ServiceFailedException(NO_SUCH_TABLE);
throw new InvocationException(NO_SUCH_TABLE);
}
// request that the user be removed from the table
if (!table.clearOccupant(leaver.username)) {
throw new ServiceFailedException(NOT_AT_TABLE);
throw new InvocationException(NOT_AT_TABLE);
}
// remove the mapping from this user to the table
@@ -0,0 +1,56 @@
//
// $Id: BasicDirector.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import com.threerings.presents.util.PresentsContext;
/**
* Handles functionality common to nearly all client directors. They
* generally need to be session observers so that they can set themselves
* up when the client logs on (by overriding {@link #clientDidLogon}) and
* clean up after themselves when the client logs off (by overriding
* {@link #clientDidLogoff}).
*/
public class BasicDirector
implements SessionObserver
{
/**
* Derived directors will need to provide the basic director with a
* context that it can use to register itself with the necessary
* entities.
*/
protected BasicDirector (PresentsContext ctx)
{
// listen for session start and end
Client client = ctx.getClient();
client.addClientObserver(this);
// if we're already logged on, fire off a call to fetch services
if (client.isLoggedOn()) {
fetchServices(client);
}
}
// documentation inherited from interface
public void clientDidLogon (Client client)
{
fetchServices(client);
}
// documentation inherited from interface
public void clientDidLogoff (Client client)
{
}
/**
* Derived directors can override this method and obtain any services
* they'll need during their operation via calls to {@link
* Client#getService}. It will automatically be called when the client
* logs on or when the director is constructed if it is constructed
* after the client is already logged on.
*/
protected void fetchServices (Client client)
{
}
}
@@ -1,5 +1,5 @@
//
// $Id: Client.java,v 1.27 2002/06/04 16:34:24 mdb Exp $
// $Id: Client.java,v 1.28 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
@@ -196,6 +196,40 @@ public class Client
return _invdir;
}
/**
* Returns the first bootstrap service that could be located that
* implements the supplied {@link InvocationService} derivation.
* <code>null</code> is returned if no such service could be found.
*/
public InvocationService getService (Class sclass)
{
int scount = _bstrap.services.size();
for (int ii = 0; ii < scount; ii++) {
InvocationService service = (InvocationService)
_bstrap.services.get(ii);
if (sclass.isInstance(service)) {
return service;
}
}
return null;
}
/**
* Like {@link #getService} except that a {@link RuntimeException} is
* thrown if the service is not available. Useful to avoid redundant
* error checking when you know that the shit will hit the fan if a
* particular invocation service is not available.
*/
public InvocationService requireService (Class sclass)
{
InvocationService isvc = getService(sclass);
if (isvc == null) {
throw new RuntimeException(sclass.getName() + " isn't available. " +
"I can't bear to go on.");
}
return isvc;
}
/**
* Returns a reference to the bootstrap data provided to this client
* at logon time.
@@ -219,7 +253,7 @@ public class Client
/**
* Returns true if we are logged on, false if we're not.
*/
public synchronized boolean loggedOn ()
public synchronized boolean isLoggedOn ()
{
// if we have a communicator, we're logged on
return (_comm != null);
@@ -312,7 +346,7 @@ public class Client
notifyObservers(Client.CLIENT_FAILED_TO_LOGON, cause);
}
};
_invdir.init(_comm.getDObjectManager(), _cloid, _bstrap.invOid, rl);
_invdir.init(_comm.getDObjectManager(), _cloid, rl);
// we can't quite call initialization completed at this point
// because we need for the invocation director to fully initialize
@@ -356,6 +390,8 @@ public class Client
public void run () {
// clear out our communicator reference
_comm = null;
// and let our invocation director know we're logged off
_invdir.cleanup();
}
});
}
@@ -0,0 +1,34 @@
//
// $Id: InvocationDecoder.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log;
/**
* Provides the basic functionality used to dispatch invocation
* notification events.
*/
public abstract class InvocationDecoder
{
/** The receiver for which we're decoding and dipatching
* notifications. */
public InvocationReceiver receiver;
/**
* Returns the generated hash code that is used to identify this
* invocation notification service.
*/
public abstract String getReceiverCode ();
/**
* Dispatches the specified method to our receiver.
*/
public void dispatchNotification (int methodId, Object[] args)
{
Log.warning("Requested to dispatch unknown method " +
"[receiver=" + receiver + ", methodId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
}
}
@@ -1,70 +1,69 @@
//
// $Id: InvocationDirector.java,v 1.20 2002/04/16 21:37:23 mdb Exp $
// $Id: InvocationDirector.java,v 1.21 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.samskivert.util.ResultListener;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log;
import com.threerings.presents.data.*;
import com.threerings.presents.dobj.*;
import com.threerings.presents.util.ClassUtil;
import com.threerings.presents.client.InvocationReceiver.Registration;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.data.InvocationMarshaller.ListenerMarshaller;
import com.threerings.presents.data.InvocationMarshaller;
import com.threerings.presents.dobj.DEvent;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.EventListener;
import com.threerings.presents.dobj.InvocationNotificationEvent;
import com.threerings.presents.dobj.InvocationRequestEvent;
import com.threerings.presents.dobj.InvocationResponseEvent;
import com.threerings.presents.dobj.ObjectAccessException;
import com.threerings.presents.dobj.Subscriber;
/**
* The invocation services provide client to server invocations (service
* requests) and server to client invocations (responses and
* notifications). Via this mechanism, the client can make requests of the
* server, be notified of its response and the server can asynchronously
* invoke code on the client.
*
* <p> Invocations are like remote procedure calls in that they are named
* and take arguments. They are simple in that the arguments can only be
* of a small set of supported types (the set of distributed object field
* types) and there is no special facility provided for referencing
* non-local objects (it is assumed that the distributed object facility
* will already be in use for any objects that should be shared).
*
* <p> The client invocation director delivers invocation requests to the
* server invocation manager and maps the responses back to the proper
* response target objects when they arrive. It also maintains the mapping
* of invocation receivers that can receive asynchronous invocation
* notifications at any time from the server.
* Handles the client side management of the invocation services.
*/
public class InvocationDirector
implements MessageListener
implements EventListener
{
/**
* Initializes the invocation director.
* Initializes the invocation director. This is called when the client
* establishes a connection with the server.
*
* @param omgr the distributed object manager via which the invocation
* manager will send and receive events.
* @param cloid the oid of the object on which invocation
* notifications as well as invocation responses will be received.
* @param imoid the oid of the object on the server to which to
* deliver invocation requests.
* @param initListener a result listener that will be notified when
* the invocation director is up and running (meaning it has
* subscribed successfully to the client object and is ready to
* process invocation requests); or when initialization has failed.
*/
public void init (DObjectManager omgr, final int cloid, int imoid,
public void init (DObjectManager omgr, final int cloid,
final ResultListener initListener)
{
// keep this for later
_omgr = omgr;
_imoid = imoid;
_cloid = cloid;
// add ourselves as a subscriber to the client object
_omgr.subscribeToObject(cloid, new Subscriber() {
public void objectAvailable (DObject object)
{
// keep a handle on this bad boy
_clobj = (ClientObject)object;
// add ourselves as a message listener
object.addListener(InvocationDirector.this);
_clobj.addListener(InvocationDirector.this);
// assign a mapping to already registered receivers
assignReceiverIds();
// let the client know that we're ready to go now that
// we've got our subscription to the client object
@@ -83,224 +82,243 @@ public class InvocationDirector
}
/**
* Sends an invocation request to the server. If a response target is
* supplied, the response will be delivered to that object by calling
* a member function on it whose name is defined in the response
* generated by the invocation implementation. In general, this is a
* derivative of the invocation procedure name. For example, if the
* caller invoked a procedure named <code>Switch</code>, the response
* may be delivered via a call to the <code>handleSwitchSuccess</code>
* method on the response target object. The signature of that method
* would be defined by the arguments provided in the response message
* with the addition of a first argument which is the invocation
* identifier for this particular request (that is also returned by
* this function).
*
* @param module the name of the invocation module to use.
* @param procedure the name of the procedure within that module.
* @param args the arguments of the invocation.
* @param rsptarget the object that will receive the response, or null
* if no response is desired.
*
* @return a unique identifier associated with this invocation
* request. This identifier will be passed as the first argument to
* the response function.
* Clears out our session information. This is called when the client
* ends its session with the server.
*/
public int invoke (String module, String procedure, Object[] args,
Object rsptarget)
public void cleanup ()
{
int invid = nextInvocationId();
// wipe our client object, receiver mappings and listener mappings
_clobj = null;
_receivers.clear();
_listeners.clear();
// if null arguments were supplied, assume zero arguments
if (args == null) {
args = new Object[0];
// also reset our counters
_requestId = 0;
_receiverId = 0;
}
/**
* Registers an invocation notification receiver by way of its
* notification event decoder.
*/
public void registerReceiver (InvocationDecoder decoder)
{
// add the receiver to the list
_reclist.add(decoder);
// if we're already online, assign a receiver id to this decoder
if (_clobj != null) {
assignReceiverId(decoder);
}
}
/**
* Removes a receiver registration.
*/
public void unregisterReceiver (String receiverCode)
{
// remove the receiver from the list
for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) {
InvocationDecoder decoder = (InvocationDecoder)iter.next();
if (decoder.getReceiverCode().equals(receiverCode)) {
iter.remove();
}
}
// we need an args array for a message that can contain the
// invocation names, an invocation id and the invocation arguments
Object[] iargs = new Object[args.length+3];
iargs[0] = module;
iargs[1] = procedure;
iargs[2] = new Integer(invid);
System.arraycopy(args, 0, iargs, 3, args.length);
// create a message event on the invocation manager object
MessageEvent event = new MessageEvent(
_imoid, InvocationObject.REQUEST_NAME, iargs);
// if we have a response target, register that for later receipt
// of the response
if (rsptarget != null) {
_targets.put(invid, rsptarget);
// if we're logged on, clear out any receiver id mapping
if (_clobj != null) {
_clobj.removeFromReceivers(receiverCode);
}
}
/**
* Assigns a receiver id to this decoder and publishes it in the
* {@link ClientObject#receivers} field.
*/
protected void assignReceiverId (InvocationDecoder decoder)
{
Registration reg = new Registration(
decoder.getReceiverCode(), nextReceiverId());
// stick the mapping into the client object
_clobj.addToReceivers(reg);
// and map the receiver in our receivers table
_receivers.put(reg.receiverId, decoder);
}
/**
* Called when we log on; generates mappings for all receivers
* registered prior to logon.
*/
protected void assignReceiverIds ()
{
// pack all the set add events into a single transaction
_clobj.startTransaction();
try {
for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) {
assignReceiverId((InvocationDecoder)iter.next());
}
} finally {
_clobj.commitTransaction();
}
}
/**
* Requests that the specified invocation request be packaged up and
* sent to the supplied invocation oid.
*/
public void sendRequest (
int invOid, int invCode, int methodId, Object[] args)
{
// configure any invocation listener marshallers among the
// arguments
int acount = args.length;
for (int ii = 0; ii < acount; ii++) {
Object arg = args[ii];
if (arg instanceof ListenerMarshaller) {
ListenerMarshaller lm = (ListenerMarshaller)arg;
lm.callerOid = _clobj.getOid();
lm.requestId = nextRequestId();
// create a mapping for this marshaller so that we can
// properly dispatch responses sent to it
_listeners.put(lm.requestId, lm);
}
}
// create an invocation request event
InvocationRequestEvent event =
new InvocationRequestEvent(invOid, invCode, methodId, args);
// because invocation directors are used on the server, we set the
// source oid here so that invocation requests are properly
// attributed to the right client object when created by
// server-side entities only sort of pretending to be a client
event.setSourceOid(_cloid);
event.setSourceOid(_clobj.getOid());
// and finally ship off the invocation message
// Log.info("Sending invocation request " + event + ".");
// now dispatch the event
_omgr.postEvent(event);
return invid;
}
/**
* Registers the supplied invocation receiver instance as the handler
* for all invocation notifications for the specified module.
* Process notification and response events arriving on user object.
*/
public void registerReceiver (String module, InvocationReceiver receiver)
public void eventReceived (DEvent event)
{
if (_receivers == null) {
_receivers = new HashMap();
}
_receivers.put(module, receiver);
}
if (event instanceof InvocationResponseEvent) {
InvocationResponseEvent ire = (InvocationResponseEvent)event;
handleInvocationResponse(
ire.getRequestId(), ire.getMethodId(), ire.getArgs());
/**
* Removes the registration for the supplied invocation receiver
* instance as the handler for invocation notifications for the
* specified module.
*/
public void unregisterReceiver (String module)
{
if (_receivers != null) {
_receivers.remove(module);
} else if (event instanceof InvocationNotificationEvent) {
InvocationNotificationEvent ine =
(InvocationNotificationEvent)event;
handleInvocationNotification(
ine.getReceiverId(), ine.getMethodId(), ine.getArgs());
}
}
/**
* Process incoming message requests on user object.
* Dispatches an invocation response.
*/
public void messageReceived (MessageEvent event)
protected void handleInvocationResponse (
int reqId, int methodId, Object[] args)
{
String name = event.getName();
if (name.equals(InvocationObject.RESPONSE_NAME)) {
handleInvocationResponse(event.getArgs());
} else if (name.equals(InvocationObject.NOTIFICATION_NAME)) {
handleInvocationNotification(event.getArgs());
}
}
/**
* Processes an invocation response message.
*/
protected void handleInvocationResponse (Object[] args)
{
String name = (String)args[0];
int invid = ((Integer)args[1]).intValue();
Object rsptarg = _targets.get(invid);
if (rsptarg == null) {
Log.warning("No target for invocation response " +
"[args=" + StringUtil.toString(args) + "].");
// look up the invocation marshaller registered for that response
ListenerMarshaller listener = (ListenerMarshaller)
_listeners.remove(reqId);
if (listener == null) {
Log.warning("Received invocation response for which we have " +
"no registered listener [reqId=" + reqId +
", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
return;
}
// prune the invocation id and method arguments from the full
// message arguments
Object[] rargs = new Object[args.length-1];
System.arraycopy(args, 1, rargs, 0, rargs.length);
// Log.info("Dispatching invocation response " +
// "[listener=" + listener + ", methId=" + methodId +
// ", args=" + StringUtil.toString(args) + "].");
// and invoke the response method; we'd cache these but the key
// for the method would have to include all of the class names of
// all of the arguments and would probably be more expensive to
// create than just reflecting the method (we should really test
// this because that's half intuition and half wild-ass guess, but
// we're going with it for now)
String mname = "handle" + name;
Method rspmeth = ClassUtil.getMethod(mname, rsptarg, rargs);
if (rspmeth == null) {
Log.warning("Unable to resolve response method " +
"[target=" + rsptarg.getClass().getName() +
", method=" + mname +
", args=" + StringUtil.toString(rargs) + "].");
return;
}
// and invoke it
// dispatch the response
try {
// Log.info("Invoking method [meth=" +
// rsptarg.getClass().getName() + "." + rspmeth.getName() +
// ", args=" + StringUtil.toString(rargs) + "].");
rspmeth.invoke(rsptarg, rargs);
} catch (Exception e) {
Log.warning("Error invoking response target method " +
"[target=" + rsptarg + ", method=" + rspmeth + "].");
Log.logStackTrace(e);
listener.dispatchResponse(methodId, args);
} catch (Throwable t) {
Log.warning("Invocation response listener choked " +
"[listener=" + listener + ", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
Log.logStackTrace(t);
}
}
/**
* Processes an invocation notification message.
* Dispatches an invocation notification.
*/
protected void handleInvocationNotification (Object[] args)
protected void handleInvocationNotification (
int receiverId, int methodId, Object[] args)
{
String module = (String)args[0];
String proc = (String)args[1];
InvocationReceiver receiver = null;
if (_receivers != null) {
receiver = (InvocationReceiver)_receivers.get(module);
}
if (receiver == null) {
Log.warning("No receiver registered for notification " +
"[args=" + StringUtil.toString(args) + "].");
// look up the decoder registered for this receiver
InvocationDecoder decoder = (InvocationDecoder)
_receivers.get(receiverId);
if (decoder == null) {
Log.warning("Received notification for which we have no " +
"registered receiver [recvId=" + receiverId +
", methodId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
return;
}
// prune the method arguments from the full message arguments
Object[] nargs = new Object[args.length-2];
System.arraycopy(args, 2, nargs, 0, nargs.length);
// Log.info("Dispatching invocation notification " +
// "[receiver=" + decoder.receiver + ", methodId=" + methodId +
// ", args=" + StringUtil.toString(args) + "].");
// and invoke the receiver method; we'd cache these but the key
// for the method would have to include all of the class names of
// all of the arguments and would probably be more expensive to
// create than just reflecting the method (we should really test
// this because that's half intuition and half wild-ass guess, but
// we're going with it for now)
String mname = "handle" + proc + "Notification";
Method rspmeth = ClassUtil.getMethod(mname, receiver, nargs);
if (rspmeth == null) {
Log.warning("Unable to resolve receiver method " +
"[target=" + receiver.getClass().getName() +
", method=" + mname +
", args=" + StringUtil.toString(nargs) + "].");
return;
}
// and invoke it
try {
rspmeth.invoke(receiver, nargs);
} catch (Exception e) {
Log.warning("Error invoking receiver method " +
"[receiver=" + receiver + ", method=" + rspmeth + "].");
Log.logStackTrace(e);
decoder.dispatchNotification(methodId, args);
} catch (Throwable t) {
Log.warning("Invocation notification receiver choked " +
"[receiver=" + decoder.receiver +
", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
Log.logStackTrace(t);
}
}
public synchronized int nextInvocationId ()
/**
* Used to generate monotonically increasing invocation request ids.
*/
protected synchronized short nextRequestId ()
{
return _invocationId++;
return _requestId++;
}
protected static class Response
/**
* Used to generate monotonically increasing invocation receiver ids.
*/
protected synchronized short nextReceiverId ()
{
public String name;
public Object target;
public Response (String name, Object target)
{
this.name = name;
this.target = target;
}
return _receiverId++;
}
/** The distributed object manager with which we interact. */
protected DObjectManager _omgr;
protected int _imoid;
protected int _cloid;
protected int _invocationId;
protected HashIntMap _targets = new HashIntMap();
protected HashMap _receivers = new HashMap();
/** Our client object; invocation responses and notifications are
* received on this object. */
protected ClientObject _clobj;
/** Used to generate monotonically increasing request ids. */
protected short _requestId;
/** Used to generate monotonically increasing receiver ids. */
protected short _receiverId;
/** Used to keep track of invocation service listeners which will
* receive responses from invocation service requests. */
protected HashIntMap _listeners = new HashIntMap();
/** Used to keep track of invocation notification receivers. */
protected HashIntMap _receivers = new HashIntMap();
/** All registered receivers are maintained in a list so that we can
* assign receiver ids to them when we go online. */
protected ArrayList _reclist = new ArrayList();
}
@@ -1,36 +1,70 @@
//
// $Id: InvocationReceiver.java,v 1.4 2001/10/11 04:07:52 mdb Exp $
// $Id: InvocationReceiver.java,v 1.5 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import com.threerings.presents.dobj.DSet;
/**
* Classes registered to process invocation notifications should implement
* the invocation receiver interface and register themselves with the
* invocation director. Because the invocation notification procedures are
* looked up using reflection, there are no methods to implement in the
* receiver interface, but it serves as a useful point for documentation
* and as a useful indicator that the class in question is serving as an
* invocation receiver.
* Invocation notification receipt interfaces should be defined as
* extending this interface. Actual notification receivers will implement
* the requisite receiver interface definition and register themselves
* with the {@link InvocationDirector} using the generated {@link
* InvocationDispatcher} class specific to the notification receiver
* interface in question. For example:
*
* <p> Invocation notifications are identified by a module name and a
* procedure name. The module name identifies which invocation receiver
* instance will receive the notification. Receivers are registered with
* the invocation director as handling all notification procedures for a
* particular module. The notification procedure name is used to construct
* a method name which is then reflected and invoked.
*
* <p> The name construction is as follows: a notification message
* requesting the invocation of a procedure named <code>Tell</code> will
* result in a method named <code>handleTellNotification</code> being
* invoked on the invocation receiver instance. The signature of that
* method is defined by the arguments supplied with the invocation
* notification message. These arguments must always be of the same type
* and must exactly match the signature of the implementing method (with
* the standard reflection argument type conversion process taken into
* account).
* <pre>
* public class FooDirector implements FooReceiver
* {
* public FooDirector (PresentsContext ctx)
* {
* InvocationDirector idir = ctx.getClient().getInvocationDirector();
* idir.registerReceiver(new FooDispatcher(this));
* }
* }
* </pre>
*
* @see InvocationDirector#registerReceiver
*/
public interface InvocationReceiver
{
/**
* Used to maintain a registry of invocation receivers that can be
* used to convert (large) hash codes into (small) registration
* numbers.
*/
public static class Registration implements DSet.Entry
{
/** The unique hash code associated with this invocation receiver
* class. */
public String receiverCode;
/** The unique id assigned to this invocation receiver class at
* registration time. */
public short receiverId;
/** Creates and initializes a registration instance. */
public Registration (String receiverCode, short receiverId)
{
this.receiverCode = receiverCode;
this.receiverId = receiverId;
}
/** Creates a blank instance suitable for unserialization. */
public Registration ()
{
}
// documentation inherited from interface
public Comparable getKey ()
{
return receiverCode;
}
/** Generates a string representation of this instance. */
public String toString ()
{
return "[" + receiverCode + " => " + receiverId + "]";
}
}
}
@@ -0,0 +1,86 @@
//
// $Id: InvocationService.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
/**
* Serves as the base interface for invocation services. An invocation
* service can be defined by extending this interface and defining service
* methods, as well as response listeners (which must extend {@link
* InvocationListener}). For example:
*
* <pre>
* public interface LocationService extends InvocationService
* {
*
* // Used to communicate responses to moveTo() requests.
* public interface MoveListener extends InvocationListener
* {
* // Called in response to a successful moveTo() request.
* public void moveSucceeded (PlaceConfig config);
* }
*
* // Requests that this client's body be moved to the specified
* // location.
* //
* // @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.
* public void moveTo (int placeId, MoveListener listener);
* }
* </pre>
*
* From this interface, a <code>LocationProvider</code> interface will be
* generated which should be implemented by whatever server entity that
* will actually provide the server side of this invocation service. That
* provider interface would look like the following:
*
* <pre>
* public interface LocationProvider extends InvocationProvider
* {
* // Requests that this client's body be moved to the specified
* // location.
* //
* // @param caller the client object of the client that invoked this
* // remotely callable method.
* // @param placeId the object id of the place object to which the
* // body should be moved.
* // @param listener the listener that should be informed of success
* // or failure.
* public void moveTo (ClientObject caller, int placeId,
* MoveListener listener)
* throws InvocationException;
* }
* </pre>
*/
public interface InvocationService
{
/**
* Invocation service methods that require a response should take a
* listener argument that can be notified of request success or
* failure. The listener argument should extend this interface so that
* generic failure can be reported in all cases. For example:
*
* <pre>
* // Used to communicate responses to <code>moveTo</code> requests.
* public interface MoveListener extends InvocationListener
* {
* // Called in response to a successful <code>moveTo</code>
* // request.
* public void moveSucceeded (PlaceConfig config);
* }
* </pre>
*/
public static interface InvocationListener
{
/**
* Called to report request failure. If the invocation services
* system detects failure of any kind, it will report it via this
* callback. Particular services may also make use of this
* callback to report failures of their own, or they may opt to
* define more specific failure callbacks.
*/
public void requestFailed (String cause);
}
}
@@ -0,0 +1,32 @@
//
// $Id: LoggingListener.java,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import com.samskivert.util.Log;
/**
* Implements the basic {@link InvocationListener} and logs the failure.
*/
public class LoggingListener
implements InvocationService.InvocationListener
{
/**
* Constructs a listener that will report the supplied error message
* along with the reason for failure to the supplied log object.
*/
public LoggingListener (Log log, String errmsg)
{
_log = log;
_errmsg = errmsg;
}
// documentation inherited from interface
public void requestFailed (String reason)
{
_log.warning(_errmsg + " [reason=" + reason + "].");
}
protected Log _log;
protected String _errmsg;
}
@@ -1,36 +1,32 @@
//
// $Id: TimeBaseService.java,v 1.2 2002/05/29 18:44:36 mdb Exp $
// $Id: TimeBaseService.java,v 1.3 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.client;
import com.threerings.presents.Log;
import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector;
import com.threerings.presents.data.TimeBaseCodes;
import com.threerings.presents.client.InvocationService;
/**
* Provides a means by which to obtain access to a time base object which
* can be used to convert delta times into absolute times.
*/
public class TimeBaseService
implements TimeBaseCodes
public interface TimeBaseService extends InvocationService
{
/**
* Requests the oid of the specified time base object be returned. The
* supplied response target must provide two methods to handle the
* responses generated by this request:
*
* <pre>
* public void handleTimeOidResponse (int invid, int timeOid);
* public void handleGetTimeOidFailed (int invid, String reason);
* </pre>
* Used to communicated the result of a {@link #getTimeOid} request.
*/
public static void getTimeOid (
Client client, String timeBase, Object rsptarget)
public static interface GotTimeBaseListener extends InvocationListener
{
InvocationDirector invdir = client.getInvocationDirector();
invdir.invoke(MODULE_NAME, GET_TIME_OID_REQUEST,
new Object[] { timeBase }, rsptarget);
Log.debug("Sent getTimeOid request [timeBase=" + timeBase + "].");
/**
* Communicates the result of a successful {@link #getTimeOid}
* request.
*/
public void gotTimeOid (int timeOid);
}
/**
* Requests the oid of the specified time base object be fetched.
*/
public void getTimeOid (
Client client, String timeBase, GotTimeBaseListener listener);
}
@@ -0,0 +1,28 @@
//
// $Id: ClientObject.dobj,v 1.1 2002/08/14 19:07:54 mdb Exp $
package com.threerings.presents.data;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.DSet;
/**
* Every client in the system has an associated client object to which
* only they subscribe. The client object can be used to deliver messages
* solely to a particular client as well as to publish client-specific
* data.
*/
public class ClientObject extends DObject
{
/** Used to publish all invocation service receivers registered on
* this client. */
public DSet receivers = new DSet();
/**
* Returns a short string identifying this client.
*/
public String who ()
{
return "(" + getOid() + ")";
}
}

Some files were not shown because too many files have changed in this diff Show More