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 #!/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 # This script is used to generate invocation receiver marshaller and
# unmarshaller classes based on receiver interface definitions. # unmarshaller classes based on receiver interface definitions.
@@ -10,6 +10,7 @@ use Getopt::Long;
use Template; use Template;
use File::Basename; use File::Basename;
use POSIX qw(strftime); use POSIX qw(strftime);
use MD5;
# use Dumpvalue; # use Dumpvalue;
@@ -84,14 +85,14 @@ while ($srcfile = shift) {
open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n"; open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n";
while (<JPO>) { while (<JPO>) {
if (/^public interface (\S+) extends (\S+)/) { if (/^public interface (\S+) extends ([^, ]+)/) {
$ifcname = $1; $ifcname = $1;
$imports{$ifcname} = 1; $imports{$ifcname} = 1;
my $sname = $2; my $sname = $2;
# if this interface does not extend InvocationReceiver, we # if this interface does not extend InvocationReceiver, we
# don't want to do nothin' # don't want to do nothin'
die "$script: Receiver interface does not extend " . die "$script: $ifcname does not extend " .
"InvocationReceiver\n (extends $sname).\n" "InvocationReceiver\n (extends $sname).\n"
if ($sname ne $ISVC_CNAME); if ($sname ne $ISVC_CNAME);
@@ -156,7 +157,8 @@ while ($srcfile = shift) {
# create a template processor # create a template processor
my $ttconfig = { my $ttconfig = {
RELATIVE => 1 RELATIVE => 1,
ABSOLUTE => 1,
}; };
my $tt = Template->new($ttconfig); my $tt = Template->new($ttconfig);
my $genstamp = strftime("%T %D", localtime); my $genstamp = strftime("%T %D", localtime);
@@ -207,7 +209,7 @@ while ($srcfile = shift) {
my @dimportsl = sort keys %dimports; my @dimportsl = sort keys %dimports;
my %ddata = ( "imports" => \@dimportsl, my %ddata = ( "imports" => \@dimportsl,
"name" => $svcname, "name" => $svcname,
"receiver_id" => 0, # TODO: do something here "receiver_code" => MD5->hexhash($ifcname),
"package" => $dcdrpkg, "package" => $dcdrpkg,
"methods" => \@methods, "methods" => \@methods,
"genstamp" => $genstamp, "genstamp" => $genstamp,
@@ -232,8 +234,12 @@ sub grab_imports {
# skip primitive types # skip primitive types
next unless ($class =~ m/\./); next unless ($class =~ m/\./);
# trim array delimiters
my $tclass = $class;
$tclass =~ s:[\[\]]::g;
# stuff the class into our import table # 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 #!/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 # This script is used to generate invocation service marshaller and
# unmarshaller classes based on invocation service interface definitions. # 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"; open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n";
while (<JPO>) { while (<JPO>) {
if (/^public interface (\S+) extends (\S+)/) { if (/^public interface (\S+) extends ([^, ]+)/) {
$ifcname = $1; $ifcname = $1;
$imports{$ifcname} = 1; $imports{$ifcname} = 1;
my $sname = $2; my $sname = $2;
# if this interface does not extend InvocationService, we # if this interface does not extend InvocationService, we
# don't want to do nothin' # don't want to do nothin'
die "$script: Service interface does not extend " . die "$script: $ifcname does not extend " .
"InvocationService\n (extends $sname).\n" "InvocationService\n (extends $sname).\n"
if ($sname ne $ISVC_CNAME); if ($sname ne $ISVC_CNAME);
@@ -203,7 +203,8 @@ while ($srcfile = shift) {
# create a template processor # create a template processor
my $ttconfig = { my $ttconfig = {
RELATIVE => 1 RELATIVE => 1,
ABSOLUTE => 1,
}; };
my $tt = Template->new($ttconfig); my $tt = Template->new($ttconfig);
my $genstamp = strftime("%T %D", localtime); my $genstamp = strftime("%T %D", localtime);
@@ -284,16 +285,20 @@ sub grab_imports {
# skip primitive types # skip primitive types
next unless ($class =~ m/\./); next unless ($class =~ m/\./);
# trim array delimiters
my $tclass = $class;
$tclass =~ s:[\[\]]::g;
# stuff the class into our import table # 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 # 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 # service class we're currently processing, we need to import its
# marshaller as well # marshaller as well
if ($class =~ m/Listener$/ && if ($tclass =~ m/Listener$/ &&
$class !~ m/^$spackage/ && $tclass !~ m/^$spackage/ &&
$class !~ $ISVC_CNAME) { $tclass !~ $ISVC_CNAME) {
my $mclass = $class; my $mclass = $tclass;
$mclass =~ s:\.client\.:.data.:g; $mclass =~ s:\.client\.:.data.:g;
$mclass =~ s:Listener:Marshaller:g; $mclass =~ s:Listener:Marshaller:g;
$mclass =~ s:Service:Marshaller:g; $mclass =~ s:Service:Marshaller:g;
+6 -2
View File
@@ -306,7 +306,7 @@ Presents Notes -*- outline -*-
public ChatDirector (PresentsContext ctx) public ChatDirector (PresentsContext ctx)
{ {
InvocationDirector invdir = ctx.getClient().getInvocationDirector(); InvocationDirector invdir = ctx.getClient().getInvocationDirector();
invdir.registerReceiver(new ChatDispatcher(this)); invdir.registerReceiver(new ChatDecoder(this));
} }
// documentation inherited from interface // 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 specification and is responsible for converting notification events into
the appropriate method calls on the receiver implementation with which the appropriate method calls on the receiver implementation with which
it is constructed. it is constructed.
@@ -354,3 +354,7 @@ Presents Notes -*- outline -*-
"received" are converted to start with "send". If the interface method "received" are converted to start with "send". If the interface method
does not start with "received" its name will not be changed in the does not start with "received" its name will not be changed in the
sender class definition. 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 %]; package [% package %];
@@ -12,8 +12,8 @@ import [% import %];
*/ */
public class [% name %]Decoder extends InvocationDecoder public class [% name %]Decoder extends InvocationDecoder
{ {
/** The id used to identify this notification receiver. */ /** The generated hash code used to identify this receiver class. */
public static final short RECEIVER_ID = [% receiver_id %]; public static final String RECEIVER_CODE = "[% receiver_code %]";
[% FOREACH method = methods -%] [% FOREACH method = methods -%]
/** The method id used to dispatch {@link [% name %]Receiver#[% method.mname %]} /** The method id used to dispatch {@link [% name %]Receiver#[% method.mname %]}
@@ -31,9 +31,9 @@ public class [% name %]Decoder extends InvocationDecoder
} }
// documentation inherited // documentation inherited
public int getReceiverId () public String getReceiverCode ()
{ {
return RECEIVER_ID; return RECEIVER_CODE;
} }
// documentation inherited // 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 %]; package [% package %];
@@ -36,7 +36,7 @@ public class [% name %]Dispatcher extends InvocationDispatcher
[% FOREACH method = methods -%] [% FOREACH method = methods -%]
case [% name %]Marshaller.[% method.mcode %]: case [% name %]Marshaller.[% method.mcode %]:
(([% name %]Provider)provider).[% method.mname %]( (([% name %]Provider)provider).[% method.mname %](
source, source[% IF method.unwrapped_args %],[% END %]
[% method.unwrapped_args %] [% method.unwrapped_args %]
); );
return; 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 %]; package [% package %];
@@ -34,8 +34,8 @@ public class [% name %]Marshaller extends InvocationMarshaller
callerOid, requestId, [% lm.mcode %], callerOid, requestId, [% lm.mcode %],
new Object[] { [% lm.wrapped_args %] })); new Object[] { [% lm.wrapped_args %] }));
} }
[% END -%]
[% END -%]
// documentation inherited // documentation inherited
public void dispatchResponse (int methodId, Object[] args) 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 %]; package [% package %];
@@ -19,10 +19,10 @@ public class [% name %]Sender extends InvocationSender
* [% name %]Receiver#[% m.mname %]} on a client. * [% name %]Receiver#[% m.mname %]} on a client.
*/ */
public static void [% m.sname %] ( public static void [% m.sname %] (
ClientObject target, [% m.arglist %]) ClientObject target[% IF m.arglist %], [% END %][% m.arglist %])
{ {
sendNotification( sendNotification(
target, [% name %]Decoder.RECEIVER_ID, [% name %]Decoder.[% m.mcode %], target, [% name %]Decoder.RECEIVER_CODE, [% name %]Decoder.[% m.mcode %],
new Object[] { [% m.wrapped_args %] }); 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; package com.threerings.admin.client;
import com.threerings.presents.client.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.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 public interface AdminService extends InvocationService
implements AdminCodes
{ {
/** /**
* Requests the list of config objects. This will result in a call to * Used to communicate a response to a {@link #getConfigInfo} request.
*
* <pre>
* public void handleConfigInfoResponse (int invid, String[] keys,
* int[] oids)
* </pre>
*
* on the response target.
*/ */
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); * Delivers a successful response to a {@link #getConfigInfo}
Log.debug("Sent getConfigInfo request."); * 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; 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. * displays their fields in a tree widget to be viewed and edited.
*/ */
public class ConfigEditorPanel extends JTabbedPane public class ConfigEditorPanel extends JTabbedPane
implements AdminService.ConfigInfoListener
{ {
/** /**
* Constructs an editor panel which will use the supplied context to * 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 // if we have no children, ship off a getConfigInfo request to
// find out what config objects are available for editing // find out what config objects are available for editing
if (getComponentCount() == 0) { 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."); 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 * Called in response to our getConfigInfo server-side service
* request. * 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)); Log.info("Got config info: " + StringUtil.toString(keys));
// create object editor panels for each of the categories // 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 * Called when the panel is hidden; this instructs all of our object
* editors to clear out their subscriptions. * 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; package com.threerings.admin.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider; 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.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 public class AdminProvider implements InvocationProvider
implements AdminCodes
{ {
/** /**
* Constructs an admin provider and registers it with the invocation * 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) 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 * Handles a request for the list of config objects.
* and oids for all registered configuration objects.
*/ */
public void handleGetConfigInfoRequest (BodyObject source, int invid) public void getConfigInfo (
throws ServiceFailedException ClientObject caller, AdminService.ConfigInfoListener listener)
{ {
// we don't have to validate the request because the user can't do // 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 // 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++) { for (int ii = 0; ii < keys.length; ii++) {
oids[ii] = ConfObjRegistry.getObject(keys[ii]).getOid(); 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; package com.threerings.crowd.chat;
@@ -8,7 +8,6 @@ import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.samskivert.util.Tuple;
import com.threerings.presents.client.*; import com.threerings.presents.client.*;
import com.threerings.presents.dobj.*; 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 * services. It handles both place constrained chat as well as direct
* messaging. * messaging.
*/ */
public class ChatDirector public class ChatDirector extends BasicDirector
implements LocationObserver, MessageListener, InvocationReceiver, implements ChatCodes, LocationObserver, MessageListener, ChatReceiver
ChatCodes
{ {
/** /**
* An interface that can receive information about the {@link * An interface that can receive information about the {@link
@@ -50,6 +48,8 @@ public class ChatDirector
*/ */
public ChatDirector (CrowdContext ctx, MessageManager msgmgr, String bundle) public ChatDirector (CrowdContext ctx, MessageManager msgmgr, String bundle)
{ {
super(ctx);
// keep the context around // keep the context around
_ctx = ctx; _ctx = ctx;
_msgmgr = msgmgr; _msgmgr = msgmgr;
@@ -57,7 +57,7 @@ public class ChatDirector
// register for chat notifications // register for chat notifications
_ctx.getClient().getInvocationDirector().registerReceiver( _ctx.getClient().getInvocationDirector().registerReceiver(
MODULE_NAME, this); new ChatDecoder(this));
// register ourselves as a location observer // register ourselves as a location observer
_ctx.getLocationDirector().addLocationObserver(this); _ctx.getLocationDirector().addLocationObserver(this);
@@ -218,41 +218,55 @@ public class ChatDirector
} }
/** /**
* Requests that a speak message be generated and delivered to all * Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the
* users that occupy the place object that we currently occupy. * 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.
*/ */
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 // make sure we're currently in a place
if (_place == null) { if (_place == null) {
return -1; return;
}
// 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;
}
} }
// dispatch a speak request on the active place object // dispatch a speak request on the active place object
int reqid = requestSpeak(_place.speakService, message, mode);
_ctx.getClient().getInvocationDirector().nextInvocationId(); }
Object[] args = new Object[] { new Integer(reqid), message };
MessageEvent mevt = new MessageEvent( /**
_place.getOid(), SPEAK_REQUEST, args); * Requests that a speak message with the specified mode be generated
_ctx.getDObjectManager().postEvent(mevt); * and delivered via the supplied speak service instance (which will
// TODO: when this gets changed such that we actually validate * be associated with a particular "speak object"). The message will
// this on the server, we have to make sure that the * first be validated by all registered {@link ChatValidator}s (and
// user is not on a portal before we allow the 'shout' to go through * possibly vetoed) before being dispatched.
return reqid; *
* @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 * @param target the username of the user to which the tell message
* should be delivered. * should be delivered.
* @param message the contents of the tell message. * @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 // make sure they can say what they want to say
for (Iterator iter = _validators.iterator(); iter.hasNext(); ) { for (Iterator iter = _validators.iterator(); iter.hasNext(); ) {
if (!((ChatValidator) iter.next()).validateTell( if (!((ChatValidator) iter.next()).validateTell(
target, message)) { target, message)) {
return -1; return;
} }
} }
int invid = ChatService.tell(_ctx.getClient(), target, message, this); // create a listener that will report success or failure
// cache the tell info for use when reporting success or failure ChatService.TellListener listener = new ChatService.TellListener() {
// to our various chat displays public void tellSucceeded () {
_tells.put(invid, new Tuple(target, message)); String msg = MessageBundle.tcompose(
return invid; "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()); _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 // documentation inherited
public boolean locationMayChange (int placeId) public boolean locationMayChange (int placeId)
{ {
@@ -340,88 +369,33 @@ public class ChatDirector
public void messageReceived (MessageEvent event) public void messageReceived (MessageEvent event)
{ {
String name = event.getName(); String name = event.getName();
if (name.equals(ChatService.SPEAK_NOTIFICATION)) { if (name.equals(SPEAK_NOTIFICATION)) {
handleSpeakMessage( handleSpeakMessage(
getLocalType(event.getTargetOid()), event.getArgs()); getLocalType(event.getTargetOid()), event.getArgs());
} else if (name.equals(ChatService.SYSTEM_NOTIFICATION)) { } else if (name.equals(SYSTEM_NOTIFICATION)) {
handleSystemMessage( handleSystemMessage(
getLocalType(event.getTargetOid()), event.getArgs()); getLocalType(event.getTargetOid()), event.getArgs());
} }
} }
/** // documentation inherited from interface
* Called by the invocation director when another client has requested public void receivedTell (String speaker, String bundle, String message)
* a tell message be delivered to this client.
*/
public void handleTellNotification (String source, String message)
{ {
// bail if the speaker is blocked // ignore messages from blocked users
if (isBlocked(source)) { if (isBlocked(speaker)) {
return; return;
} }
// if the message need be translated, do so
if (bundle != null) {
message = xlate(bundle, message);
}
UserMessage um = new UserMessage( UserMessage um = new UserMessage(
message, ChatCodes.TELL_CHAT_TYPE, source, ChatCodes.DEFAULT_MODE); message, ChatCodes.TELL_CHAT_TYPE, speaker, ChatCodes.DEFAULT_MODE);
dispatchMessage(um); dispatchMessage(um);
addChatter(source); addChatter(speaker);
}
/**
* 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));
} }
/** /**
@@ -519,9 +493,20 @@ public class ChatDirector
return (_muter != null) && _muter.isMuted(username); 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. */ /** Our active chat context. */
protected CrowdContext _ctx; protected CrowdContext _ctx;
/** Provides access to chat-related server-side services. */
protected ChatService _cservice;
/** The message manager. */ /** The message manager. */
protected MessageManager _msgmgr; protected MessageManager _msgmgr;
@@ -544,16 +529,15 @@ public class ChatDirector
/** An optionally present mutelist director. */ /** An optionally present mutelist director. */
protected MuteDirector _muter; 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. */ /** Usernames of users we've recently chatted with. */
protected LinkedList _chatters = new LinkedList(); protected LinkedList _chatters = new LinkedList();
/** Observers that are watching our chatters list. */ /** Observers that are watching our chatters list. */
protected ArrayList _chatterObservers = new ArrayList(); protected ArrayList _chatterObservers = new ArrayList();
/** Used by {@link #nextRequestId}. */
protected int _requestId;
/** The maximum number of chatter usernames to track. */ /** The maximum number of chatter usernames to track. */
protected static final int MAX_CHATTERS = 6; 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; package com.threerings.crowd.chat;
import com.threerings.presents.client.Client; import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector; import com.threerings.presents.client.InvocationService;
import com.threerings.crowd.Log;
/** /**
* The chat services provide a mechanism by which the client can broadcast * The chat services provide a mechanism by which the client can broadcast
* chat messages to all clients that are subscribed to a particular place * chat messages to all clients that are subscribed to a particular place
* object or directly to a particular client. These services should not be * object or directly to a particular client. These services should not be
* used directly, but instead should be accessed via the chat director. * used directly, but instead should be accessed via the {@link
* * ChatDirector}.
* @see 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 * Requests that a tell message be delivered to the user with username
* equal to <code>target</code>. * 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 * @param target the username of the user to which the tell message
* should be delivered. * should be delivered.
* @param message the contents of the message. * @param message the contents of the message.
* @param rsptarget the chat director reference that will receive the * @param listener the reference that will receive the tell response.
* tell response.
*
* @return the invocation request id of the generated tell request.
*/ */
public static int tell (Client client, String target, String message, public void tell (Client client, String target, String message,
ChatDirector rsptarget) TellListener listener);
{
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);
}
} }
@@ -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; package com.threerings.crowd.chat;
@@ -10,19 +10,13 @@ import com.threerings.presents.data.InvocationCodes;
*/ */
public interface ChatCodes extends InvocationCodes public interface ChatCodes extends InvocationCodes
{ {
/** The module name for the chat services. */ /** The chat localtype code for chat messages delivered on the place
public static final String MODULE_NAME = "chat"; * 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 * explicitly provided with other chat message sources via {@link
* ChatDirector#addAuxiliarySource}. */ * ChatDirector#addAuxiliarySource}. */
public static final String PLACE_CHAT_TYPE = "placeChat"; 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. */ /** The message identifier for a speak notification message. */
public static final String SPEAK_NOTIFICATION = "spknot"; public static final String SPEAK_NOTIFICATION = "spknot";
@@ -32,31 +26,16 @@ public interface ChatCodes extends InvocationCodes
/** The chat localtype for tells. */ /** The chat localtype for tells. */
public static final String TELL_CHAT_TYPE = "tellChat"; public static final String TELL_CHAT_TYPE = "tellChat";
/** The message identifier for a tell request. */ /** The default mode used by {@link SpeakService#speak} requests. */
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. */
public static final byte DEFAULT_MODE = 0; public static final byte DEFAULT_MODE = 0;
/** The think mode to indicate that the user is thinking /** A {@link SpeakService#speak} mode to indicate that the user is
* what they're saying, or is it that they're saying what they're * thinking what they're saying, or is it that they're saying what
* thinking? */ * they're thinking? */
public static final byte THINK_MODE = 1; 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; public static final byte EMOTE_MODE = 2;
/** An error code delivered when the user targeted for a tell /** 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; 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.dobj.MessageEvent;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.InvocationProvider; import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.Log;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdServer; 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 * The chat provider handles the server side of the chat-related
* invocation services. * invocation services.
*/ */
public class ChatProvider 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 * Processes a request from a client to deliver a tell message to
* another client. * another client.
*/ */
public void handleTellRequest ( public void tell (ClientObject caller, String target, String message,
BodyObject source, int invid, 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); BodyObject tobj = CrowdServer.lookupBody(target);
if (tobj == null) { if (tobj == null) {
sendResponse(source, invid, TELL_FAILED_RESPONSE, throw new InvocationException(USER_NOT_ONLINE);
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);
} }
// 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, * Delivers a tell notification to the specified target player,
* originating with the specified speaker. * originating with the specified speaker.
* *
* @param targetOid the body object id of the user that will receive * @param target the body object of the user that will receive the
* the tell message. * tell message.
* @param speaker the username of the user that generated the message * @param speaker the username of the user that generated the message
* (or some special speaker name for server messages). * (or some special speaker name for server messages).
* @param bundle the bundle identifier that will be used by the client * @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. * @param message the text of the chat message.
*/ */
public static void sendTellMessage ( public static void sendTellMessage (
int targetOid, String speaker, String bundle, String message) BodyObject target, String speaker, String bundle, String message)
{ {
Object[] args = null; ChatSender.sendTell(target, speaker, bundle, message);
if (bundle == null) {
args = new Object[] { speaker, message };
} else {
args = new Object[] { speaker, bundle, message };
}
CrowdServer.invmgr.sendNotification(
targetOid, MODULE_NAME, TELL_NOTIFICATION, args);
} }
/** /** The distributed object manager used by the chat services. */
* Sends a chat notification to the specified place object originating protected static DObjectManager _omgr;
* 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);
}
} }
@@ -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; package com.threerings.crowd.client;
@@ -10,9 +10,9 @@ import com.samskivert.util.ObserverList;
import com.samskivert.util.ObserverList.ObserverOp; import com.samskivert.util.ObserverList.ObserverOp;
import com.samskivert.util.ResultListener; import com.samskivert.util.ResultListener;
import com.threerings.presents.client.BasicDirector;
import com.threerings.presents.client.Client; 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.DObject;
import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.DObjectManager;
import com.threerings.presents.dobj.ObjectAccessException; 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 * provides a mechanism for ratifying a request to move to a new place
* before actually issuing the request. * before actually issuing the request.
*/ */
public class LocationDirector public class LocationDirector extends BasicDirector
implements LocationCodes, SessionObserver, Subscriber, InvocationReceiver implements LocationCodes, Subscriber, LocationReceiver,
LocationService.MoveListener
{ {
/** /**
* Used to recover from a moveTo request that was accepted but * Used to recover from a moveTo request that was accepted but
@@ -55,15 +56,14 @@ public class LocationDirector
*/ */
public LocationDirector (CrowdContext ctx) public LocationDirector (CrowdContext ctx)
{ {
super(ctx);
// keep this around for later // keep this around for later
_ctx = ctx; _ctx = ctx;
// register ourselves as a client observer
ctx.getClient().addClientObserver(this);
// register for location notifications // register for location notifications
_ctx.getClient().getInvocationDirector().registerReceiver( _ctx.getClient().getInvocationDirector().registerReceiver(
MODULE_NAME, this); new LocationDecoder(this));
} }
/** /**
@@ -137,7 +137,7 @@ public class LocationDirector
_pendingPlaceId = placeId; _pendingPlaceId = placeId;
// issue a moveTo request // issue a moveTo request
LocationService.moveTo(_ctx.getClient(), placeId, this); _lservice.moveTo(_ctx.getClient(), placeId, this);
return true; return true;
} }
@@ -314,7 +314,9 @@ public class LocationDirector
// documentation inherited from interface // documentation inherited from interface
public void clientDidLogon (Client client) public void clientDidLogon (Client client)
{ {
// get a copy of our body object super.clientDidLogon(client);
// subscribe to our body object
Subscriber sub = new Subscriber() { Subscriber sub = new Subscriber() {
public void objectAvailable (DObject object) public void objectAvailable (DObject object)
{ {
@@ -332,6 +334,14 @@ public class LocationDirector
client.getDObjectManager().subscribeToObject(cloid, sub); 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) protected void gotBodyObject (BodyObject clobj)
{ {
// check to see if we are already in a location, in which case // check to see if we are already in a location, in which case
@@ -341,17 +351,19 @@ public class LocationDirector
// documentation inherited from interface // documentation inherited from interface
public void clientDidLogoff (Client client) public void clientDidLogoff (Client client)
{ {
super.clientDidLogoff(client);
// clear ourselves out and inform observers of our departure // clear ourselves out and inform observers of our departure
didLeavePlace(); didLeavePlace();
// let our observers know that we're no longer in a location // let our observers know that we're no longer in a location
_observers.apply(_didChangeOp); _observers.apply(_didChangeOp);
_lservice = null;
} }
/** // documentation inherited from interface
* Called in response to a successful <code>moveTo</code> request. public void moveSucceeded (PlaceConfig config)
*/
public void handleMoveSucceeded (int invid, PlaceConfig config)
{ {
// handle the successful move // handle the successful move
didMoveTo(_pendingPlaceId, config); didMoveTo(_pendingPlaceId, config);
@@ -360,10 +372,8 @@ public class LocationDirector
_pendingPlaceId = -1; _pendingPlaceId = -1;
} }
/** // documentation inherited from interface
* Called in response to a failed <code>moveTo</code> request. public void requestFailed (String reason)
*/
public void handleMoveFailed (int invid, String reason)
{ {
// clear out our pending request oid // clear out our pending request oid
int placeId = _pendingPlaceId; int placeId = _pendingPlaceId;
@@ -376,13 +386,8 @@ public class LocationDirector
notifyFailure(placeId, reason); notifyFailure(placeId, reason);
} }
/** // documentation inherited from interface
* Called when the server has decided to forcibly move us to another public void forcedMove (int placeId)
* 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)
{ {
Log.info("Moving at request of server [placeId=" + 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. */ /** The context through which we access needed services. */
protected CrowdContext _ctx; protected CrowdContext _ctx;
/** Provides access to location services. */
protected LocationService _lservice;
/** Our location observer list. */ /** Our location observer list. */
protected ObserverList _observers = protected ObserverList _observers =
new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY); 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; package com.threerings.crowd.client;
import com.threerings.presents.client.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.Log;
import com.threerings.crowd.data.LocationCodes; import com.threerings.crowd.data.PlaceConfig;
/** /**
* The location services provide a mechanism by which the client can * The location services provide a mechanism by which the client can
* request to move from place to place in the server. These services * request to move from place to place in the server. These services
* should not be used directly, but instead should be accessed via the * should not be used directly, but instead should be accessed via the
* location director. * {@link LocationDirector}.
*
* @see LocationDirector
*/ */
public class LocationService implements LocationCodes public interface LocationService extends InvocationService
{ {
/** /**
* Requests that that this client's body be moved to the specified * Used to communicate responses to {@link #moveTo} requests.
* location.
*/ */
public static void moveTo (Client client, int placeId, public static interface MoveListener extends InvocationListener
LocationDirector rsptarget)
{ {
InvocationDirector invdir = client.getInvocationDirector(); /**
Object[] args = new Object[] { new Integer(placeId) }; * Called in response to a successful {@link #moveTo} request.
invdir.invoke(MODULE_NAME, MOVE_TO_REQUEST, args, rsptarget); */
Log.debug("Sent moveTo request [place=" + placeId + "]."); 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; package com.threerings.crowd.data;
@@ -18,9 +18,7 @@ public class BodyObject extends ClientObject
*/ */
public int location = -1; public int location = -1;
/** // documentation inherited
* Returns a short string identifying this body.
*/
public String who () public String who ()
{ {
return username + " (" + getOid() + ")"; 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; package com.threerings.crowd.data;
@@ -24,9 +24,7 @@ public class BodyObject extends ClientObject
*/ */
public int location = -1; public int location = -1;
/** // documentation inherited
* Returns a short string identifying this body.
*/
public String who () public String who ()
{ {
return username + " (" + getOid() + ")"; 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; package com.threerings.crowd.data;
import com.threerings.presents.data.InvocationCodes; import com.threerings.presents.data.InvocationCodes;
import com.threerings.crowd.client.LocationDirector;
/** /**
* Contains codes used by the location invocation services. * Contains codes used by the location invocation services.
*/ */
public interface LocationCodes extends InvocationCodes 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 /** An error code indicating that a place identified by a particular
* place id does not exist. Usually generated by a failed moveTo * place id does not exist. Usually generated by a failed moveTo
* request. */ * 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; package com.threerings.crowd.data;
@@ -40,7 +40,7 @@ public class OccupantInfo extends SimpleStreamableObject
} }
// documentation inherited // documentation inherited
public Object getKey () public Comparable getKey ()
{ {
return bodyOid; 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; 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 public class PlaceObject extends DObject
{ {
@@ -19,4 +23,7 @@ public class PlaceObject extends DObject
* to be known by everyone in the place. * to be known by everyone in the place.
*/ */
public DSet occupantInfo = new DSet(); 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; 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 public class PlaceObject extends DObject
{ {
@@ -13,6 +17,9 @@ public class PlaceObject extends DObject
/** The field name of the <code>occupantInfo</code> field. */ /** The field name of the <code>occupantInfo</code> field. */
public static final String OCCUPANT_INFO = "occupantInfo"; 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 * Tracks the oid of the body objects of all of the occupants of this
* place. * place.
@@ -26,6 +33,9 @@ public class PlaceObject extends DObject
*/ */
public DSet occupantInfo = new DSet(); 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 * Requests that the specified oid be added to the
* <code>occupants</code> oid list. The list will not change until 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; this.occupantInfo = occupantInfo;
requestAttributeChange(OCCUPANT_INFO, 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; package com.threerings.crowd.server;
@@ -8,6 +8,7 @@ import java.util.Iterator;
import com.threerings.presents.server.PresentsServer; import com.threerings.presents.server.PresentsServer;
import com.threerings.crowd.Log; import com.threerings.crowd.Log;
import com.threerings.crowd.chat.ChatProvider;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
/** /**
@@ -40,10 +41,13 @@ public class CrowdServer extends PresentsServer
// create our place registry // create our place registry
plreg = new PlaceRegistry(invmgr, omgr); plreg = new PlaceRegistry(invmgr, omgr);
// register our invocation service providers // initialize the chat services
String[] providers = null; ChatProvider.init(invmgr, omgr);
providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers);
registerProviders(providers); // // register our invocation service providers
// String[] providers = null;
// providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers);
// registerProviders(providers);
Log.info("Crowd server initialized."); 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; package com.threerings.crowd.server;
import com.threerings.presents.dobj.DObject;
import com.threerings.presents.dobj.RootDObjectManager; 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.InvocationManager;
import com.threerings.presents.server.InvocationProvider; import com.threerings.presents.server.InvocationProvider;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.Log; import com.threerings.crowd.Log;
import com.threerings.crowd.client.LocationService;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.data.LocationCodes; import com.threerings.crowd.data.LocationCodes;
import com.threerings.crowd.data.OccupantInfo; 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. * This class provides the server end of the location services.
*/ */
public class LocationProvider extends InvocationProvider public class LocationProvider
implements LocationCodes implements LocationCodes, InvocationProvider
{ {
/** /**
* Constructs a location provider and registers it with the invocation * Creates a location provider and prepares it for operation.
* manager to handle location services. This is done automatically by
* the {@link PlaceRegistry}.
*/ */
public static void init ( public LocationProvider (InvocationManager invmgr, RootDObjectManager omgr,
InvocationManager invmgr, RootDObjectManager omgr, PlaceRegistry plreg) PlaceRegistry plreg)
{ {
// we'll need these later // we'll need these later
_invmgr = invmgr; _invmgr = invmgr;
_omgr = omgr; _omgr = omgr;
_plreg = plreg; _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 ( public void moveTo (ClientObject caller, int placeId,
BodyObject source, int invid, int placeId) LocationService.MoveListener listener)
throws InvocationException
{ {
try { // do the move and send the response
// do the move listener.moveSucceeded(moveTo((BodyObject)caller, placeId));
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());
}
} }
/** /**
@@ -68,8 +67,8 @@ public class LocationProvider extends InvocationProvider
* successful for some reason (which will be communicated as an error * successful for some reason (which will be communicated as an error
* code in the exception's message data). * code in the exception's message data).
*/ */
public static PlaceConfig moveTo (BodyObject source, int placeId) public PlaceConfig moveTo (BodyObject source, int placeId)
throws ServiceFailedException throws InvocationException
{ {
int bodoid = source.getOid(); int bodoid = source.getOid();
@@ -78,7 +77,7 @@ public class LocationProvider extends InvocationProvider
if (pmgr == null) { if (pmgr == null) {
Log.info("Requested to move to non-existent place " + Log.info("Requested to move to non-existent place " +
"[source=" + source + ", place=" + placeId + "]."); "[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, // 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 (!source.acquireLock("moveToLock")) {
// if we're still locked, a previous moveTo request hasn't // if we're still locked, a previous moveTo request hasn't
// been fully processed // been fully processed
throw new ServiceFailedException(MOVE_IN_PROGRESS); throw new InvocationException(MOVE_IN_PROGRESS);
} }
PlaceObject place = pmgr.getPlaceObject(); PlaceObject place = pmgr.getPlaceObject();
try { try {
place.startTransaction();
source.startTransaction(); source.startTransaction();
// remove them from any previous location // remove them from any previous location
leaveOccupiedPlace(source); 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 // generate a new occupant info record and add it to the
// target location // target location
OccupantInfo info = pmgr.buildOccupantInfo(source); OccupantInfo info = pmgr.buildOccupantInfo(source);
@@ -124,11 +114,15 @@ public class LocationProvider extends InvocationProvider
place.addToOccupantInfo(info); place.addToOccupantInfo(info);
} }
// set the body's new location
source.setLocation(place.getOid());
// add the body oid to the place object's occupant list // add the body oid to the place object's occupant list
place.addToOccupants(bodoid); place.addToOccupants(bodoid);
} finally { } finally {
place.commitTransaction(); place.commitTransaction();
source.commitTransaction();
} }
} finally { } finally {
@@ -144,7 +138,7 @@ public class LocationProvider extends InvocationProvider
* Removes the specified body from the place object they currently * Removes the specified body from the place object they currently
* occupy. Does nothing if the body is not currently in a place. * 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 oldloc = source.location;
int bodoid = source.getOid(); int bodoid = source.getOid();
@@ -200,18 +194,16 @@ public class LocationProvider extends InvocationProvider
// first remove them from their old place // first remove them from their old place
leaveOccupiedPlace(source); leaveOccupiedPlace(source);
// then send a move notification // then send a forced move notification
_invmgr.sendNotification( LocationSender.forcedMove(source, placeId);
source.getOid(), MODULE_NAME, MOVE_NOTIFICATION,
new Object[] { new Integer(placeId) });
} }
/** The invocation manager with which we interoperate. */ /** The invocation manager with which we interoperate. */
protected static InvocationManager _invmgr; protected InvocationManager _invmgr;
/** The distributed object manager with which we interoperate. */ /** The distributed object manager with which we interoperate. */
protected static RootDObjectManager _omgr; protected RootDObjectManager _omgr;
/** The place registry with which we interoperate. */ /** 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; package com.threerings.crowd.server;
@@ -7,6 +7,10 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Properties; 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.DObjectManager;
import com.threerings.presents.dobj.MessageEvent; import com.threerings.presents.dobj.MessageEvent;
import com.threerings.presents.dobj.MessageListener; 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.PlaceConfig;
import com.threerings.crowd.data.PlaceObject; 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 * The place manager is the server-side entity that handles all
* place-related interaction. It subscribes to the place object and reacts * place-related interaction. It subscribes to the place object and reacts
@@ -42,7 +50,8 @@ import com.threerings.crowd.data.PlaceObject;
* listeners. * listeners.
*/ */
public class PlaceManager public class PlaceManager
implements MessageListener, OidListListener, ObjectDeathListener implements MessageListener, OidListListener, ObjectDeathListener,
SpeakProvider.SpeakerValidator
{ {
/** /**
* An interface used to allow the registration of standard message * 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. * Called by the place registry after creating this place manager.
*/ */
public void init ( public void init (
PlaceRegistry registry, PlaceConfig config, DObjectManager omgr) PlaceRegistry registry, InvocationManager invmgr,
DObjectManager omgr, PlaceConfig config)
{ {
_registry = registry; _registry = registry;
_config = config; _invmgr = invmgr;
_omgr = omgr; _omgr = omgr;
_config = config;
// let derived classes do initialization stuff // let derived classes do initialization stuff
didInit(); didInit();
@@ -135,6 +146,13 @@ public class PlaceManager
// keep track of this // keep track of this
_plobj = plobj; _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 // we'll need to hear about place object events
plobj.addListener(this); plobj.addListener(this);
@@ -379,6 +397,13 @@ public class PlaceManager
didShutdown(); 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 * Generates a string representation of this manager. Does so in a way
* that makes it easier for derived classes to add to the string * 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. */ /** A distributed object manager for doing dobj stuff. */
protected DObjectManager _omgr; protected DObjectManager _omgr;
@@ -447,9 +479,6 @@ public class PlaceManager
/** A reference to the configuration for our place. */ /** A reference to the configuration for our place. */
protected PlaceConfig _config; 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. */ /** Message handlers are used to process message events. */
protected HashMap _msghandlers; 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; package com.threerings.crowd.server;
@@ -43,17 +43,23 @@ public class PlaceRegistry
public void placeCreated (PlaceObject place, PlaceManager pmgr); 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 * Creates and initializes the place registry; called by the server
* during its initialization phase. * during its initialization phase.
*/ */
public PlaceRegistry (InvocationManager invmgr, RootDObjectManager omgr) public PlaceRegistry (InvocationManager invmgr, RootDObjectManager omgr)
{ {
// we'll need this later // create and register our location provider
_omgr = omgr; locprov = new LocationProvider(invmgr, omgr, this);
invmgr.registerDispatcher(new LocationDispatcher(locprov), true);
// register the location provider // we'll need these later
LocationProvider.init(invmgr, omgr, this); _omgr = omgr;
_invmgr = invmgr;
} }
/** /**
@@ -86,7 +92,7 @@ public class PlaceRegistry
// create a place manager for this place // create a place manager for this place
PlaceManager pmgr = (PlaceManager)pmgrClass.newInstance(); PlaceManager pmgr = (PlaceManager)pmgrClass.newInstance();
// let the pmgr know about us and its configuration // 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 // stick the manager on the creation queue because we know
// we'll get our calls to objectAvailable()/requestFailed() in // 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. */ /** The distributed object manager with which we operate. */
protected RootDObjectManager _omgr; 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; package com.threerings.micasa.client;
@@ -93,7 +93,7 @@ public class MiCasaApplet extends Applet
// hide the frame and log off // hide the frame and log off
_frame.setVisible(false); _frame.setVisible(false);
Client client = _client.getContext().getClient(); Client client = _client.getContext().getClient();
if (client.loggedOn()) { if (client.isLoggedOn()) {
client.logoff(false); 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; package com.threerings.micasa.client;
@@ -55,7 +55,7 @@ public class MiCasaClient
_frame.addWindowListener(new WindowAdapter() { _frame.addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent evt) { public void windowClosing (WindowEvent evt) {
// if we're logged on, log off // if we're logged on, log off
if (_client.loggedOn()) { if (_client.isLoggedOn()) {
_client.logoff(true); _client.logoff(true);
} else { } else {
// otherwise get the heck out // 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; package com.threerings.micasa.lobby;
@@ -15,8 +15,7 @@ import com.threerings.parlor.game.GameConfig;
import com.threerings.micasa.Log; import com.threerings.micasa.Log;
import com.threerings.micasa.util.MiCasaContext; import com.threerings.micasa.util.MiCasaContext;
public class LobbyController public class LobbyController extends PlaceController
extends PlaceController
implements InvitationHandler, InvitationResponseObserver implements InvitationHandler, InvitationResponseObserver
{ {
public void init (CrowdContext ctx, PlaceConfig config) public void init (CrowdContext ctx, PlaceConfig config)
@@ -61,35 +60,38 @@ public class LobbyController
} }
} }
public void invitationReceived (int inviteId, String inviter, // documentation inherited from interface
GameConfig config) public void invitationReceived (Invitation invite)
{ {
Log.info("Invitation received [inviteId=" + inviteId + Log.info("Invitation received [invite=" + invite + "].");
", inviter=" + inviter + ", config=" + config + "].");
// accept the invitation. we're game... // 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 + "]."); ", 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 + "]."); ", 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; package com.threerings.micasa.lobby;
import java.util.Properties; import java.util.Properties;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.threerings.crowd.chat.ChatService; import com.threerings.crowd.chat.ChatCodes;
import com.threerings.crowd.chat.ChatMessageHandler;
import com.threerings.crowd.server.PlaceManager; import com.threerings.crowd.server.PlaceManager;
import com.threerings.micasa.Log; 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 // let the lobby registry know that we're up and running
_lobreg.lobbyReady(_plobj.getOid(), _gameIdent, _name); _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 /** 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; 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.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 public interface LobbyProvider extends InvocationProvider
extends InvocationProvider implements LobbyCodes
{ {
/**
* 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 * Processes a request by the client to obtain a list of the lobby
* categories available on this server. * categories available on this server.
*/ */
public void handleGetCategoriesRequest ( public void getCategories (ClientObject caller,
BodyObject source, int invid) CategoriesListener listener);
{
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);
}
/** /**
* Processes a request by the client to obtain a list of lobbies * Processes a request by the client to obtain a list of lobbies
* matching the supplied category string. * matching the supplied category string.
*/ */
public void handleGetLobbiesRequest ( public void getLobbies (ClientObject caller, String category,
BodyObject source, int invid, String category) LobbiesListener listener);
{
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;
} }
@@ -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; package com.threerings.micasa.lobby;
import java.util.*; import java.util.*;
import com.samskivert.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.presents.server.InvocationManager;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.micasa.Log; 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.MiCasaConfig;
import com.threerings.micasa.server.MiCasaServer; 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 * sense, the client will be able to use them to search for lobbies
* containing games of similar types using the provided facilities. * 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 * Initializes the registry. It will use the supplied configuration
@@ -75,9 +81,8 @@ public class LobbyRegistry implements LobbyCodes
*/ */
public void init (InvocationManager invmgr) public void init (InvocationManager invmgr)
{ {
// register our invocation service handler // register ourselves as an invocation service handler
LobbyProvider provider = new LobbyProvider(this); invmgr.registerDispatcher(new LobbyDispatcher(this), true);
invmgr.registerProvider(MODULE_NAME, provider);
// create our lobby managers // create our lobby managers
String[] lmgrs = null; String[] lmgrs = null;
@@ -178,6 +183,30 @@ public class LobbyRegistry implements LobbyCodes
return cats; 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 * Called by our lobby managers once they have started up and are
* ready to do their lobby duties. * 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; package com.threerings.micasa.lobby;
@@ -18,8 +18,6 @@ import com.samskivert.util.StringUtil;
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
import com.threerings.micasa.Log; import com.threerings.micasa.Log;
import com.threerings.micasa.lobby.Lobby;
import com.threerings.micasa.lobby.LobbyService;
import com.threerings.micasa.util.MiCasaContext; 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 * 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. * a lobby is double-clicked, it moves the client into that lobby room.
*/ */
public class LobbySelector public class LobbySelector extends JPanel
extends JPanel implements ActionListener implements ActionListener, LobbyService.CategoriesListener,
LobbyService.LobbiesListener
{ {
/** /**
* Constructs a new lobby selector component. It will wait until it is * Constructs a new lobby selector component. It will wait until it is
@@ -64,16 +63,18 @@ public class LobbySelector
}; };
_loblist.addMouseListener(ml); _loblist.addMouseListener(ml);
add(_loblist, BorderLayout.CENTER); add(_loblist, BorderLayout.CENTER);
}
// we'll need to know when we are made visible because we'll want // documentation inherited
// to issue a request for our categories when that happens public void addNotify ()
addAncestorListener(new AncestorAdapter () { {
public void ancestorAdded (AncestorEvent evt) { super.addNotify();
// issue a get categories request to get the category list
LobbyService.getCategories( // get a handle on our lobby service instance
_ctx.getClient(), LobbySelector.this); _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
} }
} }
/** // documentation inherited from interface
* Called in response to a {@link LobbyService#getCategories} public void gotCategories (String[] categories)
* invocation service request.
*/
public void handleGotCategories (int invid, String[] categories)
{ {
// append these to our "unselected" item // append these to our "unselected" item
for (int i = 0; i < categories.length; i++) { for (int i = 0; i < categories.length; i++) {
@@ -102,11 +100,8 @@ public class LobbySelector
} }
} }
/** // documentation inherited from interface
* Called in response to a {@link LobbyService#getLobbies} invocation public void gotLobbies (List lobbies)
* service request.
*/
public void handleGotLobbies (int invid, List lobbies)
{ {
// create a list model for this category // create a list model for this category
DefaultListModel model = new DefaultListModel(); DefaultListModel model = new DefaultListModel();
@@ -130,16 +125,13 @@ public class LobbySelector
_pendingCategory = null; _pendingCategory = null;
} }
/** // documentation inherited from interface
* Called in response to a lobby invocation request when the request public void requestFailed (String reason)
* failed.
*/
public void handleRequestFailed (int invid, String reason)
{ {
Log.info("Request failed [invid=" + invid + Log.info("Request failed [reason=" + reason + "].");
", reason=" + reason + "].");
// clear out our pending category indicator // clear out our pending category indicator in case this was a
// failed getLobbies() request
_pendingCategory = null; _pendingCategory = null;
} }
@@ -158,7 +150,7 @@ public class LobbySelector
// make a note that we're loading up this category // make a note that we're loading up this category
_pendingCategory = category; _pendingCategory = category;
// issue a request to load up the lobbies in this 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 { } else {
Log.info("Ignoring category select request because " + Log.info("Ignoring category select request because " +
@@ -201,6 +193,8 @@ public class LobbySelector
} }
protected MiCasaContext _ctx; protected MiCasaContext _ctx;
protected LobbyService _lservice;
protected JComboBox _combo; protected JComboBox _combo;
protected JList _loblist; 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; package com.threerings.micasa.lobby;
import com.threerings.presents.client.Client; import java.util.List;
import com.threerings.presents.client.InvocationDirector;
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 * Provides an interface to the various parlor services that are directly
* are directly invokable by the client (by means of the invocation * invokable by the client (by means of the invocation services).
* services).
*/ */
public class LobbyService public interface LobbyService extends InvocationService
implements LobbyCodes
{ {
/**
* 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 * Requests the list of lobby cateogories that are available on this
* server. * server.
* *
* @param client a connected, operational client instance. * @param client a connected, operational client instance.
* @param rsptarget the object reference that will receive and process * @param listener the listener that will receive and process the
* the response. The response will come in the form of a method call * response.
* to <code>handleGotCategories</code> or
* <code>handleRequestFailed</code>.
*
* @return the invocation request id of the generated request.
*/ */
public static int getCategories (Client client, Object rsptarget) public void getCategories (Client client, CategoriesListener listener);
{
InvocationDirector invdir = client.getInvocationDirector();
Log.debug("Sending get categories.");
return invdir.invoke(
MODULE_NAME, GET_CATEGORIES_REQUEST, null, rsptarget);
}
/** /**
* Requests information on all active lobbies that match the specified * 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 client a connected, operational client instance.
* @param category the category of game for which a list of lobbies is * @param category the category of game for which a list of lobbies is
* desired. * desired.
* @param rsptarget the object reference that will receive and process * @param listener the listener that will receive and process the
* the response. The response will come in the form of a method call * response.
* to <code>handleLobbyList</code> or
* <code>handleRequestFailed</code>.
*
* @return the invocation request id of the generated request.
*/ */
public static int getLobbies ( public void getLobbies (Client client, String category,
Client client, String category, Object rsptarget) LobbiesListener listener);
{
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);
}
} }
@@ -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; package com.threerings.micasa.server;
@@ -40,7 +40,7 @@ public class MiCasaServer extends CrowdServer
clmgr.setClientClass(MiCasaClient.class); clmgr.setClientClass(MiCasaClient.class);
// initialize our parlor manager // initialize our parlor manager
parmgr.init(invmgr); parmgr.init(invmgr, plreg);
// initialize the lobby registry // initialize the lobby registry
lobreg.init(invmgr); 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; package com.threerings.micasa.simulator.client;
@@ -73,8 +73,11 @@ public class ClientController extends Controller
config = (GameConfig) config = (GameConfig)
Class.forName(_info.gameConfigClass).newInstance(); Class.forName(_info.gameConfigClass).newInstance();
// send the game creation request // get the simulator service and use it to request that our
SimulatorDirector.createGame( // game be created
SimulatorService sservice = (SimulatorService)
client.requireService(SimulatorService.class);
sservice.createGame(
client, config, _info.simClass, _info.playerCount); client, config, _info.simClass, _info.playerCount);
// our work here is done, as the location manager will move us // 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; package com.threerings.micasa.simulator.client;
@@ -53,7 +53,7 @@ public class SimpleClient
_frame.getFrame().addWindowListener(new WindowAdapter() { _frame.getFrame().addWindowListener(new WindowAdapter() {
public void windowClosing (WindowEvent evt) { public void windowClosing (WindowEvent evt) {
// if we're logged on, log off // if we're logged on, log off
if (_client.loggedOn()) { if (_client.isLoggedOn()) {
_client.logoff(true); _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.DObjectManager;
import com.threerings.presents.dobj.MessageEvent; 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.BodyObject;
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
import com.threerings.parlor.game.GameCodes;
import com.threerings.parlor.game.GameConfig; 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 * Initializes the simulant with a body object and the game config for
* the game they'll be engaged in. * 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; _self = self;
_config = config; _config = config;
_gmgr = gmgr;
_omgr = omgr; _omgr = omgr;
} }
@@ -37,9 +39,7 @@ public abstract class Simulant implements GameCodes
public void willEnterPlace (PlaceObject plobj) public void willEnterPlace (PlaceObject plobj)
{ {
// let the game manager know that the simulant's ready // let the game manager know that the simulant's ready
MessageEvent mevt = new MessageEvent( _gmgr.playerReady(_self);
plobj.getOid(), PLAYER_READY_NOTIFICATION, null);
postEvent(mevt);
} }
/** /**
@@ -59,6 +59,9 @@ public abstract class Simulant implements GameCodes
/** The game config object. */ /** The game config object. */
protected GameConfig _config; protected GameConfig _config;
/** The game manager for the game we're playing. */
protected GameManager _gmgr;
/** Our body object. */ /** Our body object. */
protected BodyObject _self; 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; 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.BodyObject;
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
import com.threerings.crowd.server.LocationProvider;
import com.threerings.crowd.server.PlaceManager; 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.CreationObserver;
import com.threerings.crowd.server.PlaceRegistry;
import com.threerings.parlor.game.GameConfig; import com.threerings.parlor.game.GameConfig;
import com.threerings.parlor.game.GameManager; import com.threerings.parlor.game.GameManager;
import com.threerings.parlor.game.GameObject; import com.threerings.parlor.game.GameObject;
import com.threerings.micasa.Log; 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 * The simulator manager is responsible for handling the simulator
* services on the server side. * services on the server side.
*/ */
public class SimulatorManager public class SimulatorManager
implements SimulatorCodes
{ {
/** /**
* Initializes the simulator manager manager. This should be called by * Initializes the simulator manager manager. This should be called by
@@ -48,7 +44,7 @@ public class SimulatorManager
{ {
// register our simulator provider // register our simulator provider
SimulatorProvider sprov = new SimulatorProvider(this); SimulatorProvider sprov = new SimulatorProvider(this);
invmgr.registerProvider(MODULE_NAME, sprov); invmgr.registerDispatcher(new SimulatorDispatcher(sprov), true);
// keep these for later // keep these for later
_plreg = plreg; _plreg = plreg;
@@ -172,7 +168,7 @@ public class SimulatorManager
// give the simulant its body // give the simulant its body
BodyObject bobj = (BodyObject)_sims.get(ii - 1); 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 // give the simulant a chance to engage in place antics
sim.willEnterPlace(_gobj); sim.willEnterPlace(_gobj);
@@ -180,7 +176,7 @@ public class SimulatorManager
// move the simulant into the game room since they have no // move the simulant into the game room since they have no
// location director to move them automagically // location director to move them automagically
try { try {
LocationProvider.moveTo(bobj, _gobj.getOid()); _plreg.locprov.moveTo(bobj, _gobj.getOid());
} catch (Exception e) { } catch (Exception e) {
Log.warning("Failed to move simulant into room " + Log.warning("Failed to move simulant into room " +
"[e=" + e + "]."); "[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; package com.threerings.micasa.simulator.server;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.server.InvocationProvider; import com.threerings.presents.server.InvocationProvider;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.parlor.game.GameConfig; import com.threerings.parlor.game.GameConfig;
@@ -14,7 +16,7 @@ import com.threerings.micasa.Log;
* side, passing them off to the {@link SimulatorManager}. * side, passing them off to the {@link SimulatorManager}.
*/ */
public class SimulatorProvider public class SimulatorProvider
extends InvocationProvider implements InvocationProvider
{ {
/** /**
* Constructs a simulator provider. * Constructs a simulator provider.
@@ -27,15 +29,14 @@ public class SimulatorProvider
/** /**
* Processes a request from the client to create a new game. * Processes a request from the client to create a new game.
*/ */
public void handleCreateGameRequest ( public void createGame (ClientObject caller, GameConfig config,
BodyObject source, int invid, GameConfig config, String simClass, int playerCount)
String simClass, int playerCount)
{ {
Log.info("handleCreateGameRequest [source=" + source + Log.info("handleCreateGameRequest [caller=" + caller +
", config=" + config + ", simClass=" + simClass + ", config=" + config + ", simClass=" + simClass +
", playerCount=" + playerCount + "]."); ", playerCount=" + playerCount + "].");
_simmgr.createGame(source, config, simClass, playerCount); _simmgr.createGame((BodyObject)caller, config, simClass, playerCount);
} }
/** The simulator manager. */ /** 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; package com.threerings.parlor.client;
@@ -16,18 +16,14 @@ public interface InvitationHandler
/** /**
* Called when an invitation is received from another player. * Called when an invitation is received from another player.
* *
* @param inviteId this invitation's unique id. * @param invite the received invitation.
* @param inviter the username of the user that sent the invitation.
* @param config the configuration of the game to which we are being
* invited.
*/ */
public void invitationReceived (int inviteId, String inviter, public void invitationReceived (Invitation invite);
GameConfig config);
/** /**
* Called when an invitation is cancelled by the inviting player. * 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; package com.threerings.parlor.client;
@@ -17,30 +17,27 @@ public interface InvitationResponseObserver
/** /**
* Called if the invitation was accepted. * Called if the invitation was accepted.
* *
* @param inviteId the unique id of the invitation for which we * @param invite the invitation for which we received a response.
* received a response.
*/ */
public void invitationAccepted (int inviteId); public void invitationAccepted (Invitation invite);
/** /**
* Called if the invitation was refused. * Called if the invitation was refused.
* *
* @param inviteId the unique id of the invitation for which we * @param invite the invitation for which we received a response.
* received a response.
* @param message a message provided by the invited user explaining * @param message a message provided by the invited user explaining
* the reason for their refusal, or the empty string if no message was * the reason for their refusal, or the empty string if no message was
* provided. * 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 * Called if the invitation was countered with an alternate game
* configuration. * configuration.
* *
* @param inviteId the unique id of the invitation for which we * @param invite the invitation for which we received a response.
* received a response.
* @param config the game configuration proposed by the invited * @param config the game configuration proposed by the invited
* player. * 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; package com.threerings.parlor.client;
@@ -7,7 +7,8 @@ import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import com.samskivert.util.HashIntMap; 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.Log;
import com.threerings.parlor.data.ParlorCodes; 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 will actually create and display the user interface for the game
* that started. * that started.
*/ */
public class ParlorDirector public class ParlorDirector extends BasicDirector
implements ParlorCodes, InvocationReceiver implements ParlorCodes, ParlorReceiver
{ {
/** /**
* Constructs a parlor director and provides it with the parlor * Constructs a parlor director and provides it with the parlor
@@ -35,12 +36,16 @@ public class ParlorDirector
*/ */
public ParlorDirector (ParlorContext ctx) public ParlorDirector (ParlorContext ctx)
{ {
super(ctx);
_ctx = ctx; _ctx = ctx;
// register ourselves with the invocation director as handling // register as a session observer
// parlor notifications _ctx.getClient().addClientObserver(this);
_ctx.getClient().getInvocationDirector().
registerReceiver(MODULE_NAME, 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 * @param observer the entity that will be notified if this invitation
* is accepted, refused or countered. * is accepted, refused or countered.
* *
* @return a unique id associated with this invitation that can be * @return an invitation object that can be used to manage the
* used to discern between various outstanding invitations by the * outstanding invitation.
* invitation observer.
*/ */
public int invite (String invitee, GameConfig config, public Invitation invite (String invitee, GameConfig config,
InvitationResponseObserver observer) InvitationResponseObserver observer)
{ {
// generate the invocation service request // create the invitation record
int invid = ParlorService.invite( Invitation invite = new Invitation(
_ctx.getClient(), invitee, config, this); _ctx, _pservice, invitee, config, observer);
// submit the invitation request to the server
// create an invitation record and put it in the submitted table _pservice.invite(_ctx.getClient(), invitee, config, invite);
Invitation invite = new Invitation(invitee, config, observer); // and return the invitation to the caller
_submittedInvites.put(invid, invite); return invite;
return invite.inviteId;
} }
/** // documentation inherited
* Accept an invitation. protected void fetchServices (Client client)
*
* @param inviteId the id of the invitation to accept.
*/
public void accept (int inviteId)
{ {
Invitation invite = getInviteByLocalId(inviteId); // get a handle on our parlor services
if (invite == null) { _pservice = (ParlorService)client.requireService(ParlorService.class);
// 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);
} }
/** // documentation inherited from interface
* Refuse an invitation. public void gameIsReady (int gameOid)
*
* @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)
{ {
Log.info("Handling game ready [goid=" + gameOid + "]."); Log.info("Handling game ready [goid=" + gameOid + "].");
@@ -229,55 +129,31 @@ public class ParlorDirector
} }
} }
/** // documentation inherited from interface
* Called by the invocation services when another user has invited us public void receivedInvite (
* 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 (
int remoteId, String inviter, GameConfig config) int remoteId, String inviter, GameConfig config)
{ {
// create an invitation record for this invitation // create an invitation record for this invitation
Invitation invite = new Invitation(inviter, config, null); Invitation invite = new Invitation(
invite.remoteId = remoteId; _ctx, _pservice, inviter, config, null);
invite.inviteId = remoteId;
// put it in the pending invitations table // put it in the pending invitations table
_pendingInvites.put(remoteId, invite); _pendingInvites.put(remoteId, invite);
try { try {
// notify the invitation handler of the incoming invitation // notify the invitation handler of the incoming invitation
_handler.invitationReceived(invite.inviteId, inviter, config); _handler.invitationReceived(invite);
} catch (Exception e) { } catch (Exception e) {
Log.warning("Invitation handler choked on invite " + Log.warning("Invitation handler choked on invite " +
"notification [inviteId=" + invite.inviteId + "notification " + invite + ".");
", inviter=" + inviter +
", config=" + config + "].");
Log.logStackTrace(e); Log.logStackTrace(e);
} }
} }
/** // documentation inherited from interface
* Called by the invocation services when another user has responded public void receivedInviteResponse (
* 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 (
int remoteId, int code, Object arg) int remoteId, int code, Object arg)
{ {
// look up the invitation record for this invitation // 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 " + Log.warning("Have no record of invitation for which we " +
"received a response?! [remoteId=" + remoteId + "received a response?! [remoteId=" + remoteId +
", code=" + code + ", arg=" + arg + "]."); ", code=" + code + ", arg=" + arg + "].");
return;
}
// make sure we have an observer to notify } else {
if (invite.observer == null) { invite.receivedResponse(code, arg);
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);
} }
} }
/** // documentation inherited from interface
* Called by the invocation services when an outstanding invitation public void receivedInviteCancellation (int remoteId)
* has been cancelled by the inviting user.
*
* @param remoteId the unique indentifier for the invitation.
*/
public void handleCancelInviteNotification (int remoteId)
{ {
// TBD // TBD
} }
/** /**
* Called by the invocation services when an invitation request was * Register a new invitation in our pending invitations table. The
* received by the server and delivered to the intended invitee. * invitation will call this when it knows its invitation id.
*
* @param invid the invocation id of the invitation request.
*/ */
public void handleInviteReceived (int invid, int remoteId) protected void registerInvitation (Invitation invite)
{ {
// remove the invitation record from the submitted table and put _pendingInvites.put(invite.inviteId, invite);
// 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);
}
} }
/** /**
* Called by the invocation services when an invitation request failed * Called by an invitation when it knows it is no longer and can be
* or was rejected for some reason (this is different than an * cleared from the pending invitations table.
* 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.
*/ */
public void handleInviteFailed (int invid, String reason) protected void clearInvitation (Invitation invite)
{ {
Log.info("Handling invite failed [invid=" + invid + _pendingInvites.remove(invite.inviteId);
", 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;
} }
/** An active parlor context. */ /** An active parlor context. */
protected ParlorContext _ctx; protected ParlorContext _ctx;
/** Provides access to parlor server side services. */
protected ParlorService _pservice;
/** The entity that has registered itself to handle incoming /** The entity that has registered itself to handle incoming
* invitation notifications. */ * invitation notifications. */
protected InvitationHandler _handler; 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) /** A table of acknowledged (but not yet accepted or refused)
* invitation requests, keyed on invitation id. */ * invitation requests, keyed on invitation id. */
protected HashIntMap _pendingInvites = new HashIntMap(); 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 /** We notify the entities on this list when we get a game ready
* notification. */ * notification. */
protected ArrayList _grobs = new ArrayList(); 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; package com.threerings.parlor.client;
import com.threerings.presents.client.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; import com.threerings.parlor.game.GameConfig;
/** /**
* This class provides an interface to the various parlor services that * Provides an interface to the various parlor invocation services.
* are directly invokable by the client (by means of the invocation * Presently these services are limited to the various matchmaking
* services). Presently these services are limited to the various * mechanisms. It is unlikely that client code will want to make direct
* matchmaking mechanisms. It is unlikely that client code will want to * use of this class, instead they would make use of the programmatic
* make direct use of this class, instead they would make use of the * interface provided by the {@link ParlorDirector}.
* programmatic interface provided by the {@link ParlorDirector}.
*
* @see 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 * You probably don't want to call this directly, but want to generate
* your invitation request via {@link ParlorDirector#invite}. Requests * 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 invitee the username of the user to be invited.
* @param config a game config object detailing the type and * @param config a game config object detailing the type and
* configuration of the game to be created. * configuration of the game to be created.
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
public static int invite (Client client, String invitee, public void invite (Client client, String invitee, GameConfig config,
GameConfig config, Object rsptgt) InviteListener listener);
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { invitee, config };
return invdir.invoke(MODULE_NAME, INVITE_ID, args, rsptgt);
}
/** /**
* You probably don't want to call this directly, but want to call one * 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 * 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 * object in the case of a counter-invitation, or null in the case of
* an accepted invitation). * an accepted invitation).
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
public static int respond (Client client, int inviteId, int code, public void respond (Client client, int inviteId, int code, Object arg,
Object arg, Object rsptgt) InvocationListener listener);
{
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);
}
/** /**
* You probably don't want to call this directly, but want to call * 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 client a connected, operational client instance.
* @param inviteId the unique id previously assigned by the server to * @param inviteId the unique id previously assigned by the server to
* this invitation. * this invitation.
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
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(); public void tableCreated (int tableId);
Object[] args = new Object[] { new Integer(inviteId) };
return invdir.invoke(MODULE_NAME, CANCEL_INVITE_ID, args, rsptgt);
} }
/** /**
@@ -108,18 +97,10 @@ public class ParlorService implements ParlorCodes
* created table. * created table.
* @param config the game config for the game to be matchmade by the * @param config the game config for the game to be matchmade by the
* table. * table.
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
public static int createTable ( public void createTable (Client client, int lobbyOid, GameConfig config,
Client client, int lobbyOid, GameConfig config, Object rsptgt) TableListener listener);
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(lobbyOid), config };
return invdir.invoke(MODULE_NAME, CREATE_TABLE_REQUEST, args, rsptgt);
}
/** /**
* You probably don't want to call this directly, but want to call * 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. * to be added.
* @param position the position at the table to which this user desires * @param position the position at the table to which this user desires
* to be added. * to be added.
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
public static int joinTable (Client client, int lobbyOid, int tableId, public void joinTable (Client client, int lobbyOid, int tableId,
int position, Object rsptgt) int position, InvocationListener listener);
{
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);
}
/** /**
* You probably don't want to call this directly, but want to call * You probably don't want to call this directly, but want to call
* {@link TableDirector#leaveTable}. Requests that the current user * {@link TableDirector#leaveTable}. Requests that the current user be
* be removed from the specified table. * removed from the specified table.
* *
* @param client a connected, operational client instance. * @param client a connected, operational client instance.
* @param lobbyOid the oid of the lobby that contains the table. * @param lobbyOid the oid of the lobby that contains the table.
* @param tableId the unique id of the table from which this user * @param tableId the unique id of the table from which this user
* wishes to be removed. * wishes to be removed.
* @param rsptgt the object reference that will receive and process * @param listener will receive and process the response.
* the response.
*
* @return the invocation request id of the generated request.
*/ */
public static int leaveTable ( public void leaveTable (Client client, int lobbyOid, int tableId,
Client client, int lobbyOid, int tableId, Object rsptgt) InvocationListener listener);
{
InvocationDirector invdir = client.getInvocationDirector();
Object[] args = new Object[] { new Integer(lobbyOid),
new Integer(tableId) };
return invdir.invoke(MODULE_NAME, LEAVE_TABLE_REQUEST, args, rsptgt);
}
} }
@@ -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; package com.threerings.parlor.client;
import java.util.ArrayList; 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.EntryAddedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.EntryRemovedEvent; import com.threerings.presents.dobj.EntryRemovedEvent;
import com.threerings.presents.dobj.EntryUpdatedEvent;
import com.threerings.presents.dobj.SetListener; import com.threerings.presents.dobj.SetListener;
import com.threerings.crowd.data.BodyObject; 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 * lobby in which the table matchmaking takes place implements the {@link
* TableLobbyObject} interface. * TableLobbyObject} interface.
*/ */
public class TableDirector public class TableDirector extends BasicDirector
implements SetListener implements SetListener, ParlorService.TableListener
{ {
/** /**
* Creates a new table director to manage tables with the specified * Creates a new table director to manage tables with the specified
@@ -54,6 +57,8 @@ public class TableDirector
public TableDirector ( public TableDirector (
ParlorContext ctx, String tableField, TableObserver observer) ParlorContext ctx, String tableField, TableObserver observer)
{ {
super(ctx);
// keep track of this stuff // keep track of this stuff
_ctx = ctx; _ctx = ctx;
_tableField = tableField; _tableField = tableField;
@@ -140,8 +145,7 @@ public class TableDirector
} }
// go ahead and issue the create request // go ahead and issue the create request
ParlorService.createTable( _pservice.createTable(_ctx.getClient(), _lobby.getOid(), config, this);
_ctx.getClient(), _lobby.getOid(), config, this);
} }
/** /**
@@ -166,8 +170,8 @@ public class TableDirector
} }
// issue the join request // issue the join request
ParlorService.joinTable( _pservice.joinTable(_ctx.getClient(), _lobby.getOid(), tableId,
_ctx.getClient(), _lobby.getOid(), tableId, position, this); position, this);
} }
/** /**
@@ -185,8 +189,14 @@ public class TableDirector
} }
// issue the leave request // issue the leave request
ParlorService.leaveTable( _pservice.leaveTable(_ctx.getClient(), _lobby.getOid(), tableId, this);
_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 // documentation inherited
@@ -234,54 +244,19 @@ public class TableDirector
} }
} }
/** // documentation inherited from interface
* Called by the invocation services when a table creation request was public void tableCreated (int tableId)
* 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)
{ {
// nothing much to do here // nothing much to do here
Log.info("Table creation succeeded [tableId=" + tableId + "]."); Log.info("Table creation succeeded [tableId=" + tableId + "].");
} }
/** // documentation inherited from interface
* Called by the invocation services when a table creation request public void requestFailed (String reason)
* 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)
{ {
Log.warning("Table creation failed [reason=" + 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 * Checks to see if we're a member of this table and notes it as our
* table, if so. * table, if so.
@@ -338,6 +313,9 @@ public class TableDirector
/** A context by which we can access necessary client services. */ /** A context by which we can access necessary client services. */
protected ParlorContext _ctx; protected ParlorContext _ctx;
/** Parlor server-side services. */
protected ParlorService _pservice;
/** The place object in which we're currently managing tables. */ /** The place object in which we're currently managing tables. */
protected PlaceObject _lobby; 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; package com.threerings.parlor.data;
import com.threerings.presents.data.InvocationCodes; 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. * Contains codes used by the parlor invocation services.
*/ */
public interface ParlorCodes extends InvocationCodes 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. */ /** The response code for an accepted invitation. */
public static final int INVITATION_ACCEPTED = 0; public static final int INVITATION_ACCEPTED = 0;
@@ -57,20 +24,6 @@ public interface ParlorCodes extends InvocationCodes
* received. */ * received. */
public static final String INVITEE_NOT_ONLINE = "m.invitee_not_online"; 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 /** An error code returned when a user requests to join a table that
* doesn't exist. */ * doesn't exist. */
public static final String NO_SUCH_TABLE = "m.no_such_table"; 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; package com.threerings.parlor.data;
@@ -237,7 +237,7 @@ public class Table
} }
// documentation inherited // documentation inherited
public Object getKey () public Comparable getKey ()
{ {
return tableId; 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; package com.threerings.parlor.game;
@@ -31,7 +31,7 @@ import com.threerings.parlor.util.ParlorContext;
* distributed object events. * distributed object events.
*/ */
public abstract class GameController extends PlaceController public abstract class GameController extends PlaceController
implements AttributeChangeListener, GameCodes implements AttributeChangeListener
{ {
/** /**
* Initializes this game controller with the game configuration that * 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 // and add ourselves as a listener
_gobj.addListener(this); _gobj.addListener(this);
// finally let the game manager know that we're ready to roll // we don't want to claim to be finished until any derived classes
MessageEvent mevt = new MessageEvent( // that overrode this method have executed, so we'll queue up a
_gobj.getOid(), PLAYER_READY_NOTIFICATION, null); // runnable here that will let the game manager know that we're
_ctx.getDObjectManager().postEvent(mevt); // 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; package com.threerings.parlor.game;
@@ -7,11 +7,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import com.threerings.presents.data.ClientObject;
import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.AttributeChangeListener;
import com.threerings.presents.dobj.AttributeChangedEvent; import com.threerings.presents.dobj.AttributeChangedEvent;
import com.threerings.presents.dobj.MessageEvent; 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.BodyObject;
import com.threerings.crowd.data.PlaceObject; 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.Log;
import com.threerings.parlor.data.ParlorCodes; import com.threerings.parlor.data.ParlorCodes;
import com.threerings.parlor.server.ParlorSender;
/** /**
* The game manager handles the server side management of a game. It * 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. * bodies in that location.
*/ */
public class GameManager extends PlaceManager 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 * Provides the game manager with a list of the usernames of all
* object. This happens before startup and before the game object has * players in the game. This happens before startup and before the
* been created. * game object has been created.
* *
* @param players the usernames of all of the players in this game or * @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) public void setPlayers (String[] players)
{ {
@@ -167,8 +150,9 @@ public class GameManager extends PlaceManager
public void systemMessage ( public void systemMessage (
String msgbundle, String msg, boolean waitForStart) String msgbundle, String msg, boolean waitForStart)
{ {
if (waitForStart && ((_gameobj == null) || if (waitForStart &&
(_gameobj.state == GameObject.AWAITING_PLAYERS))) { ((_gameobj == null) ||
(_gameobj.state == GameObject.AWAITING_PLAYERS))) {
// queue up the message. // queue up the message.
if (_startmsgs == null) { if (_startmsgs == null) {
_startmsgs = new ArrayList(); _startmsgs = new ArrayList();
@@ -179,7 +163,13 @@ public class GameManager extends PlaceManager
} }
// otherwise, just deliver the message // 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 // documentation inherited
@@ -193,24 +183,27 @@ public class GameManager extends PlaceManager
// stick the players into the game object // stick the players into the game object
_gameobj.setPlayers(_players); _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 // let the players of this game know that we're ready to roll (if
// we have a specific set of players) // we have a specific set of players)
if (_players != null) { if (_players != null) {
Object[] args = new Object[] { int gameOid = _gameobj.getOid();
new Integer(_gameobj.getOid()) };
for (int i = 0; i < _players.length; i++) { for (int i = 0; i < _players.length; i++) {
BodyObject bobj = CrowdServer.lookupBody(_players[i]); BodyObject bobj = CrowdServer.lookupBody(_players[i]);
if (bobj == null) { if (bobj == null) {
Log.warning("Unable to deliver game ready to " + Log.warning("Unable to deliver game ready to " +
"non-existent player " + "non-existent player " +
"[gameOid=" + _gameobj.getOid() + "[gameOid=" + gameOid +
", player=" + _players[i] + "]."); ", player=" + _players[i] + "].");
continue; continue;
} }
// deliver a game ready notification to the player // deliver a game ready notification to the player
CrowdServer.invmgr.sendNotification( ParlorSender.gameIsReady(bobj, gameOid);
bobj.getOid(), MODULE_NAME, GAME_READY_NOTIFICATION, args);
} }
} }
} }
@@ -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 // documentation inherited
public void attributeChanged (AttributeChangedEvent event) 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. * A helper operation to distribute AI ticks to our delegates.
*/ */
@@ -515,9 +504,6 @@ public class GameManager extends PlaceManager
protected byte _level; protected byte _level;
} }
/** A reference to our game configuration. */
protected GameConfig _gconfig;
/** A reference to our game object. */ /** A reference to our game object. */
protected GameObject _gameobj; 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; package com.threerings.parlor.game;
@@ -33,6 +33,9 @@ public class GameObject extends PlaceObject
/** A game state constant indicating that the game was cancelled. */ /** A game state constant indicating that the game was cancelled. */
public static final int CANCELLED = GAME_OVER+3; 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}, /** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY},
* {@link #GAME_OVER}, or {@link #CANCELLED}. */ * {@link #GAME_OVER}, or {@link #CANCELLED}. */
public int state; 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; package com.threerings.parlor.game;
@@ -20,6 +20,9 @@ import com.threerings.crowd.data.PlaceObject;
*/ */
public class GameObject extends 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. */ /** The field name of the <code>state</code> field. */
public static final String STATE = "state"; 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. */ /** A game state constant indicating that the game was cancelled. */
public static final int CANCELLED = GAME_OVER+3; 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}, /** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY},
* {@link #GAME_OVER}, or {@link #CANCELLED}. */ * {@link #GAME_OVER}, or {@link #CANCELLED}. */
public int state; public int state;
@@ -68,6 +74,20 @@ public class GameObject extends PlaceObject
ListUtil.indexOfEqual(players, username); 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 * Requests that the <code>state</code> field be set to the specified
* value. The local value will be updated immediately and an event * 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; package com.threerings.parlor.server;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.threerings.presents.server.InvocationException;
import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationManager;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.data.BodyObject; 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.Log;
import com.threerings.parlor.data.ParlorCodes; import com.threerings.parlor.data.ParlorCodes;
@@ -26,6 +26,9 @@ import com.threerings.parlor.game.GameManager;
public class ParlorManager public class ParlorManager
implements ParlorCodes 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 * Initializes the parlor manager. This should be called by the server
* that is making use of the parlor services on the single instance of * 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 * @param invmgr a reference to the invocation manager in use by this
* server. * 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 // create and register our invocation provider
ParlorProvider pprov = new ParlorProvider(this); parprov = new ParlorProvider(this);
invmgr.registerProvider(MODULE_NAME, pprov); invmgr.registerDispatcher(new ParlorDispatcher(parprov), true);
// keep this around for later // keep this for later
_invmgr = invmgr; _plreg = plreg;
} }
/** /**
@@ -56,14 +61,14 @@ public class ParlorManager
* @return the invitation identifier for the newly created invitation * @return the invitation identifier for the newly created invitation
* record. * 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 * able to be processed for some reason (like the invited player has
* requested not to be disturbed). The explanation will be provided in * requested not to be disturbed). The explanation will be provided in
* the message data of the exception. * the message data of the exception.
*/ */
public int invite (BodyObject inviter, BodyObject invitee, public int invite (BodyObject inviter, BodyObject invitee,
GameConfig config) GameConfig config)
throws ServiceFailedException throws InvocationException
{ {
// Log.info("Received invitation request [inviter=" + inviter + // Log.info("Received invitation request [inviter=" + inviter +
// ", invitee=" + invitee + ", config=" + config + "]."); // ", invitee=" + invitee + ", config=" + config + "].");
@@ -79,10 +84,8 @@ public class ParlorManager
_invites.put(invite.inviteId, invite); _invites.put(invite.inviteId, invite);
// deliver an invite notification to the invitee // deliver an invite notification to the invitee
Object[] args = new Object[] { ParlorSender.sendInvite(invitee, invite.inviteId,
new Integer(invite.inviteId), inviter.username, config }; inviter.username, config);
_invmgr.sendNotification(
invitee.getOid(), MODULE_NAME, INVITE_ID, args);
// and let the caller know the invite id we assigned // and let the caller know the invite id we assigned
return invite.inviteId; return invite.inviteId;
@@ -126,10 +129,8 @@ public class ParlorManager
// let the other user know that a response was made to this // let the other user know that a response was made to this
// invitation // invitation
Object[] args = new Object[] { ParlorSender.sendInviteResponse(
new Integer(invite.inviteId), new Integer(code), arg }; invite.inviter, invite.inviteId, code, arg);
_invmgr.sendNotification(
invite.inviter.getOid(), MODULE_NAME, RESPOND_INVITE_ID, args);
switch (code) { switch (code) {
case INVITATION_ACCEPTED: case INVITATION_ACCEPTED:
@@ -187,7 +188,7 @@ public class ParlorManager
// started up (which is done by the place registry once the // started up (which is done by the place registry once the
// game object creation has completed) // game object creation has completed)
GameManager gmgr = (GameManager) GameManager gmgr = (GameManager)
CrowdServer.plreg.createPlace(invite.config, null); _plreg.createPlace(invite.config, null);
// provide the game manager with the player list // provide the game manager with the player list
gmgr.setPlayers(new String[] { gmgr.setPlayers(new String[] {
@@ -242,8 +243,8 @@ public class ParlorManager
} }
} }
/** A reference to the invocation manager in operation on this server. */ /** The place registry with which we operate. */
protected InvocationManager _invmgr; protected PlaceRegistry _plreg;
/** The table of pending invitations. */ /** The table of pending invitations. */
protected HashIntMap _invites = new HashIntMap(); 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; 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.InvocationProvider;
import com.threerings.presents.server.ServiceFailedException;
import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.BodyObject;
import com.threerings.crowd.server.CrowdServer; import com.threerings.crowd.server.CrowdServer;
import com.threerings.crowd.server.PlaceManager; import com.threerings.crowd.server.PlaceManager;
import com.threerings.parlor.Log; 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.data.ParlorCodes;
import com.threerings.parlor.game.GameConfig; import com.threerings.parlor.game.GameConfig;
@@ -20,7 +24,7 @@ import com.threerings.parlor.game.GameConfig;
* Primarily these are the matchmaking mechanisms. * Primarily these are the matchmaking mechanisms.
*/ */
public class ParlorProvider public class ParlorProvider
extends InvocationProvider implements ParlorCodes implements InvocationProvider, ParlorCodes
{ {
/** /**
* Constructs a parlor provider instance which will be used to handle * 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 * Processes a request from the client to invite another user to play
* a game. * a game.
*/ */
public void handleInviteRequest ( public void invite (ClientObject caller, String invitee,
BodyObject source, int invid, String invitee, GameConfig config) GameConfig config, InviteListener listener)
throws ServiceFailedException throws InvocationException
{ {
// Log.info("Handling invite request [source=" + source + // Log.info("Handling invite request [source=" + source +
// ", invid=" + invid + ", invitee=" + invitee + // ", invitee=" + invitee + ", config=" + config + "].");
// ", config=" + config + "].");
BodyObject source = (BodyObject)caller;
String rsp = null; String rsp = null;
// ensure that the invitee is online at present // ensure that the invitee is online at present
BodyObject target = CrowdServer.lookupBody(invitee); BodyObject target = CrowdServer.lookupBody(invitee);
if (target == null) { if (target == null) {
throw new ServiceFailedException(INVITEE_NOT_ONLINE); throw new InvocationException(INVITEE_NOT_ONLINE);
} }
// submit the invite request to the parlor manager // submit the invite request to the parlor manager
int inviteId = _pmgr.invite(source, target, config); int inviteId = _pmgr.invite(source, target, config);
sendResponse(source, invid, INVITE_RECEIVED_RESPONSE, listener.inviteReceived(inviteId);
new Integer(inviteId));
} }
/** /**
* Processes a request from the client to respond to an outstanding * Processes a request from the client to respond to an outstanding
* invitation by accepting, refusing, or countering it. * invitation by accepting, refusing, or countering it.
*/ */
public void handleRespondInviteRequest ( public void respond (ClientObject caller, int inviteId, int code,
BodyObject source, int invid, int inviteId, int code, Object arg) Object arg, InvocationListener listener)
{ {
// pass this on to the parlor manager // 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 * Processes a request from the client to cancel an outstanding
* invitation. * invitation.
*/ */
public void handleCancelInviteRequest ( public void cancel (ClientObject caller, int inviteId,
BodyObject source, int invid, int inviteId) InvocationListener listener)
{ {
// pass this on to the parlor manager // 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. * Processes a request from the client to create a new table.
*/ */
public void handleCreateTableRequest ( public void createTable (ClientObject caller, int lobbyOid,
BodyObject source, int invid, int lobbyOid, GameConfig config) GameConfig config, TableListener listener)
throws ServiceFailedException throws InvocationException
{ {
Log.info("Handling create table request [source=" + source + Log.info("Handling create table request [caller=" + caller +
", invid=" + invid + ", lobbyOid=" + lobbyOid + ", lobbyOid=" + lobbyOid + ", config=" + config + "].");
", config=" + config + "].");
// pass the creation request on to the table manager // pass the creation request on to the table manager
TableManager tmgr = getTableManager(lobbyOid); TableManager tmgr = getTableManager(lobbyOid);
int tableId = tmgr.createTable(source, config); int tableId = tmgr.createTable((BodyObject)caller, config);
sendResponse(source, invid, TABLE_CREATED_RESPONSE, listener.tableCreated(tableId);
new Integer(tableId));
} }
/** /**
* Processes a request from the client to join an existing table. * Processes a request from the client to join an existing table.
*/ */
public void handleJoinTableRequest ( public void joinTable (ClientObject caller, int lobbyOid, int tableId,
BodyObject source, int invid, int lobbyOid, int tableId, int position) int position, InvocationListener listener)
throws ServiceFailedException throws InvocationException
{ {
Log.info("Handling join table request [source=" + source + Log.info("Handling join table request [caller=" + caller +
", invid=" + invid + ", lobbyOid=" + lobbyOid + ", lobbyOid=" + lobbyOid + ", tableId=" + tableId +
", tableId=" + tableId + ", position=" + position + "]."); ", position=" + position + "].");
// pass the join request on to the table manager // pass the join request on to the table manager
TableManager tmgr = getTableManager(lobbyOid); 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 // there is normally no success response. the client will see
// themselves show up in the table that they joined // 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. * Processes a request from the client to leave an existing table.
*/ */
public void handleLeaveTableRequest ( public void leaveTable (ClientObject caller, int lobbyOid, int tableId,
BodyObject source, int invid, int lobbyOid, int tableId) InvocationListener listener)
throws ServiceFailedException throws InvocationException
{ {
Log.info("Handling leave table request [source=" + source + Log.info("Handling leave table request [caller=" + caller +
", invid=" + invid + ", lobbyOid=" + lobbyOid + ", lobbyOid=" + lobbyOid + ", tableId=" + tableId + "].");
", tableId=" + tableId + "].");
// pass the join request on to the table manager // pass the join request on to the table manager
TableManager tmgr = getTableManager(lobbyOid); TableManager tmgr = getTableManager(lobbyOid);
tmgr.leaveTable(source, tableId); tmgr.leaveTable((BodyObject)caller, tableId);
// there is normally no success response. the client will see // there is normally no success response. the client will see
// themselves removed from the table they just left // 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 * casts it to a table lobby manager and obtains the associated table
* manager reference. * 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 * along the way like no place manager exists or the place manager
* that does exist doesn't implement table lobby manager. * that does exist doesn't implement table lobby manager.
*/ */
protected TableManager getTableManager (int lobbyOid) protected TableManager getTableManager (int lobbyOid)
throws ServiceFailedException throws InvocationException
{ {
PlaceManager plmgr = CrowdServer.plreg.getPlaceManager(lobbyOid); PlaceManager plmgr = CrowdServer.plreg.getPlaceManager(lobbyOid);
if (plmgr == null) { if (plmgr == null) {
Log.warning("No place manager exists from which to obtain " + Log.warning("No place manager exists from which to obtain " +
"table manager reference [ploid=" + lobbyOid + "]."); "table manager reference [ploid=" + lobbyOid + "].");
throw new ServiceFailedException(INTERNAL_ERROR); throw new InvocationException(INTERNAL_ERROR);
} }
// sanity check // sanity check
if (!(plmgr instanceof TableManagerProvider)) { if (!(plmgr instanceof TableManagerProvider)) {
Log.warning("Place manager not a table lobby manager " + Log.warning("Place manager not a table lobby manager " +
"[plmgr=" + plmgr + "]."); "[plmgr=" + plmgr + "].");
throw new ServiceFailedException(INTERNAL_ERROR); throw new InvocationException(INTERNAL_ERROR);
} }
return ((TableManagerProvider)plmgr).getTableManager(); 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; 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.ObjectDestroyedEvent;
import com.threerings.presents.dobj.ObjectRemovedEvent; import com.threerings.presents.dobj.ObjectRemovedEvent;
import com.threerings.presents.dobj.OidListListener; 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.BodyObject;
import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.data.PlaceObject;
@@ -69,12 +69,12 @@ public class TableManager
* *
* @return the id of the newly created table. * @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 * not able processed for some reason. The explanation will be
* provided in the message data of the exception. * provided in the message data of the exception.
*/ */
public int createTable (BodyObject creator, GameConfig config) public int createTable (BodyObject creator, GameConfig config)
throws ServiceFailedException throws InvocationException
{ {
// make sure the game config implements TableConfig // make sure the game config implements TableConfig
if (!(config instanceof TableConfig)) { if (!(config instanceof TableConfig)) {
@@ -82,7 +82,7 @@ public class TableManager
"using the table services [creator=" + creator + "using the table services [creator=" + creator +
", lobby=" + _plobj.getOid() + ", lobby=" + _plobj.getOid() +
", config=" + config + "]."); ", config=" + config + "].");
throw new ServiceFailedException(INTERNAL_ERROR); throw new InvocationException(INTERNAL_ERROR);
} }
// make sure the creator is an occupant of the lobby in which // 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 " + Log.warning("Requested to create a table in a lobby not " +
"occupied by the creator [creator=" + creator + "occupied by the creator [creator=" + creator +
", loboid=" + _plobj.getOid() + "]."); ", loboid=" + _plobj.getOid() + "].");
throw new ServiceFailedException(INTERNAL_ERROR); throw new InvocationException(INTERNAL_ERROR);
} }
// create a brand spanking new table // create a brand spanking new table
@@ -105,7 +105,7 @@ public class TableManager
"table!? [table=" + table + ", creator=" + creator + "table!? [table=" + table + ", creator=" + creator +
", error=" + error + "]."); ", error=" + error + "].");
// bail out now and abort the table creation process // 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 // 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 specified position. If the user successfully joins the table,
* the function will return normally. If they are not able to join for * the function will return normally. If they are not able to join for
* some reason (the slot is already full, etc.), a {@link * 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 * describes the reason for failure. If the user does successfully
* join, they will be added to the table entry in the tables set in * join, they will be added to the table entry in the tables set in
* the place object that is hosting the table. * 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 tableId the id of the table to be joined.
* @param position the position at which to join the table. * @param position the position at which to join the table.
* *
* @exception ServiceFailedException thrown if the joining was not * @exception InvocationException thrown if the joining was not able
* able processed for some reason. The explanation will be provided in * processed for some reason. The explanation will be provided in the
* the message data of the exception. * message data of the exception.
*/ */
public void joinTable (BodyObject joiner, int tableId, int position) public void joinTable (BodyObject joiner, int tableId, int position)
throws ServiceFailedException throws InvocationException
{ {
// look the table up // look the table up
Table table = (Table)_tables.get(tableId); Table table = (Table)_tables.get(tableId);
if (table == null) { 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 // 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()); table.setOccupant(position, joiner.username, joiner.getOid());
// if that failed, report the error // if that failed, report the error
if (error != null) { if (error != null) {
throw new ServiceFailedException(error); throw new InvocationException(error);
} }
// determine whether or not it's time to start the game // 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 * table. If the user successfully leaves the table, the function will
* return normally. If they are not able to leave for some reason * return normally. If they are not able to leave for some reason
* (they aren't sitting at the table, etc.), a {@link * (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. * describes the reason for failure.
* *
* @param leaver the body object of the user that wishes to leave the * @param leaver the body object of the user that wishes to leave the
* table. * table.
* @param tableId the id of the table to be left. * @param tableId the id of the table to be left.
* *
* @exception ServiceFailedException thrown if the leaving was not * @exception InvocationException thrown if the leaving was not able
* able processed for some reason. The explanation will be provided in * processed for some reason. The explanation will be provided in the
* the message data of the exception. * message data of the exception.
*/ */
public void leaveTable (BodyObject leaver, int tableId) public void leaveTable (BodyObject leaver, int tableId)
throws ServiceFailedException throws InvocationException
{ {
// look the table up // look the table up
Table table = (Table)_tables.get(tableId); Table table = (Table)_tables.get(tableId);
if (table == null) { if (table == null) {
throw new ServiceFailedException(NO_SUCH_TABLE); throw new InvocationException(NO_SUCH_TABLE);
} }
// request that the user be removed from the table // request that the user be removed from the table
if (!table.clearOccupant(leaver.username)) { 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 // 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; package com.threerings.presents.client;
@@ -196,6 +196,40 @@ public class Client
return _invdir; 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 * Returns a reference to the bootstrap data provided to this client
* at logon time. * at logon time.
@@ -219,7 +253,7 @@ public class Client
/** /**
* Returns true if we are logged on, false if we're not. * 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 // if we have a communicator, we're logged on
return (_comm != null); return (_comm != null);
@@ -312,7 +346,7 @@ public class Client
notifyObservers(Client.CLIENT_FAILED_TO_LOGON, cause); 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 // we can't quite call initialization completed at this point
// because we need for the invocation director to fully initialize // because we need for the invocation director to fully initialize
@@ -356,6 +390,8 @@ public class Client
public void run () { public void run () {
// clear out our communicator reference // clear out our communicator reference
_comm = null; _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; package com.threerings.presents.client;
import java.lang.reflect.Method; import java.util.ArrayList;
import java.util.HashMap; import java.util.Iterator;
import com.samskivert.util.HashIntMap; import com.samskivert.util.HashIntMap;
import com.samskivert.util.StringUtil;
import com.samskivert.util.ResultListener; import com.samskivert.util.ResultListener;
import com.samskivert.util.StringUtil;
import com.threerings.presents.Log; import com.threerings.presents.Log;
import com.threerings.presents.data.*; import com.threerings.presents.client.InvocationReceiver.Registration;
import com.threerings.presents.dobj.*;
import com.threerings.presents.util.ClassUtil; 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 * Handles the client side management of the invocation services.
* 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.
*/ */
public class InvocationDirector 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 * @param omgr the distributed object manager via which the invocation
* manager will send and receive events. * manager will send and receive events.
* @param cloid the oid of the object on which invocation * @param cloid the oid of the object on which invocation
* notifications as well as invocation responses will be received. * 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 * @param initListener a result listener that will be notified when
* the invocation director is up and running (meaning it has * the invocation director is up and running (meaning it has
* subscribed successfully to the client object and is ready to * subscribed successfully to the client object and is ready to
* process invocation requests); or when initialization has failed. * 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) final ResultListener initListener)
{ {
// keep this for later
_omgr = omgr; _omgr = omgr;
_imoid = imoid;
_cloid = cloid;
// add ourselves as a subscriber to the client object // add ourselves as a subscriber to the client object
_omgr.subscribeToObject(cloid, new Subscriber() { _omgr.subscribeToObject(cloid, new Subscriber() {
public void objectAvailable (DObject object) public void objectAvailable (DObject object)
{ {
// keep a handle on this bad boy
_clobj = (ClientObject)object;
// add ourselves as a message listener // 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 // let the client know that we're ready to go now that
// we've got our subscription to the client object // 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 * Clears out our session information. This is called when the client
* supplied, the response will be delivered to that object by calling * ends its session with the server.
* 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.
*/ */
public int invoke (String module, String procedure, Object[] args, public void cleanup ()
Object rsptarget)
{ {
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 // also reset our counters
if (args == null) { _requestId = 0;
args = new Object[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 // if we're logged on, clear out any receiver id mapping
// invocation names, an invocation id and the invocation arguments if (_clobj != null) {
Object[] iargs = new Object[args.length+3]; _clobj.removeFromReceivers(receiverCode);
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);
} }
}
/**
* 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 // because invocation directors are used on the server, we set the
// source oid here so that invocation requests are properly // source oid here so that invocation requests are properly
// attributed to the right client object when created by // attributed to the right client object when created by
// server-side entities only sort of pretending to be a client // 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); _omgr.postEvent(event);
return invid;
} }
/** /**
* Registers the supplied invocation receiver instance as the handler * Process notification and response events arriving on user object.
* for all invocation notifications for the specified module.
*/ */
public void registerReceiver (String module, InvocationReceiver receiver) public void eventReceived (DEvent event)
{ {
if (_receivers == null) { if (event instanceof InvocationResponseEvent) {
_receivers = new HashMap(); InvocationResponseEvent ire = (InvocationResponseEvent)event;
} handleInvocationResponse(
_receivers.put(module, receiver); ire.getRequestId(), ire.getMethodId(), ire.getArgs());
}
/** } else if (event instanceof InvocationNotificationEvent) {
* Removes the registration for the supplied invocation receiver InvocationNotificationEvent ine =
* instance as the handler for invocation notifications for the (InvocationNotificationEvent)event;
* specified module. handleInvocationNotification(
*/ ine.getReceiverId(), ine.getMethodId(), ine.getArgs());
public void unregisterReceiver (String module)
{
if (_receivers != null) {
_receivers.remove(module);
} }
} }
/** /**
* 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(); // look up the invocation marshaller registered for that response
if (name.equals(InvocationObject.RESPONSE_NAME)) { ListenerMarshaller listener = (ListenerMarshaller)
handleInvocationResponse(event.getArgs()); _listeners.remove(reqId);
} else if (name.equals(InvocationObject.NOTIFICATION_NAME)) { if (listener == null) {
handleInvocationNotification(event.getArgs()); Log.warning("Received invocation response for which we have " +
} "no registered listener [reqId=" + reqId +
} ", methId=" + methodId +
", args=" + StringUtil.toString(args) + "].");
/**
* 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) + "].");
return; return;
} }
// prune the invocation id and method arguments from the full // Log.info("Dispatching invocation response " +
// message arguments // "[listener=" + listener + ", methId=" + methodId +
Object[] rargs = new Object[args.length-1]; // ", args=" + StringUtil.toString(args) + "].");
System.arraycopy(args, 1, rargs, 0, rargs.length);
// and invoke the response method; we'd cache these but the key // dispatch the response
// 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
try { try {
// Log.info("Invoking method [meth=" + listener.dispatchResponse(methodId, args);
// rsptarg.getClass().getName() + "." + rspmeth.getName() + } catch (Throwable t) {
// ", args=" + StringUtil.toString(rargs) + "]."); Log.warning("Invocation response listener choked " +
rspmeth.invoke(rsptarg, rargs); "[listener=" + listener + ", methId=" + methodId +
} catch (Exception e) { ", args=" + StringUtil.toString(args) + "].");
Log.warning("Error invoking response target method " + Log.logStackTrace(t);
"[target=" + rsptarg + ", method=" + rspmeth + "].");
Log.logStackTrace(e);
} }
} }
/** /**
* 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]; // look up the decoder registered for this receiver
String proc = (String)args[1]; InvocationDecoder decoder = (InvocationDecoder)
_receivers.get(receiverId);
InvocationReceiver receiver = null; if (decoder == null) {
if (_receivers != null) { Log.warning("Received notification for which we have no " +
receiver = (InvocationReceiver)_receivers.get(module); "registered receiver [recvId=" + receiverId +
} ", methodId=" + methodId +
if (receiver == null) { ", args=" + StringUtil.toString(args) + "].");
Log.warning("No receiver registered for notification " +
"[args=" + StringUtil.toString(args) + "].");
return; return;
} }
// prune the method arguments from the full message arguments // Log.info("Dispatching invocation notification " +
Object[] nargs = new Object[args.length-2]; // "[receiver=" + decoder.receiver + ", methodId=" + methodId +
System.arraycopy(args, 2, nargs, 0, nargs.length); // ", 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 { try {
rspmeth.invoke(receiver, nargs); decoder.dispatchNotification(methodId, args);
} catch (Exception e) { } catch (Throwable t) {
Log.warning("Error invoking receiver method " + Log.warning("Invocation notification receiver choked " +
"[receiver=" + receiver + ", method=" + rspmeth + "]."); "[receiver=" + decoder.receiver +
Log.logStackTrace(e); ", 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; return _receiverId++;
public Object target;
public Response (String name, Object target)
{
this.name = name;
this.target = target;
}
} }
/** The distributed object manager with which we interact. */
protected DObjectManager _omgr; protected DObjectManager _omgr;
protected int _imoid;
protected int _cloid;
protected int _invocationId; /** Our client object; invocation responses and notifications are
protected HashIntMap _targets = new HashIntMap(); * received on this object. */
protected HashMap _receivers = new HashMap(); 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; package com.threerings.presents.client;
import com.threerings.presents.dobj.DSet;
/** /**
* Classes registered to process invocation notifications should implement * Invocation notification receipt interfaces should be defined as
* the invocation receiver interface and register themselves with the * extending this interface. Actual notification receivers will implement
* invocation director. Because the invocation notification procedures are * the requisite receiver interface definition and register themselves
* looked up using reflection, there are no methods to implement in the * with the {@link InvocationDirector} using the generated {@link
* receiver interface, but it serves as a useful point for documentation * InvocationDispatcher} class specific to the notification receiver
* and as a useful indicator that the class in question is serving as an * interface in question. For example:
* invocation receiver.
* *
* <p> Invocation notifications are identified by a module name and a * <pre>
* procedure name. The module name identifies which invocation receiver * public class FooDirector implements FooReceiver
* instance will receive the notification. Receivers are registered with * {
* the invocation director as handling all notification procedures for a * public FooDirector (PresentsContext ctx)
* particular module. The notification procedure name is used to construct * {
* a method name which is then reflected and invoked. * InvocationDirector idir = ctx.getClient().getInvocationDirector();
* * idir.registerReceiver(new FooDispatcher(this));
* <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 * </pre>
* 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).
* *
* @see InvocationDirector#registerReceiver * @see InvocationDirector#registerReceiver
*/ */
public interface InvocationReceiver 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; package com.threerings.presents.client;
import com.threerings.presents.Log;
import com.threerings.presents.client.Client; import com.threerings.presents.client.Client;
import com.threerings.presents.client.InvocationDirector; import com.threerings.presents.client.InvocationService;
import com.threerings.presents.data.TimeBaseCodes;
/** /**
* Provides a means by which to obtain access to a time base object which * Provides a means by which to obtain access to a time base object which
* can be used to convert delta times into absolute times. * can be used to convert delta times into absolute times.
*/ */
public class TimeBaseService public interface TimeBaseService extends InvocationService
implements TimeBaseCodes
{ {
/** /**
* Requests the oid of the specified time base object be returned. The * Used to communicated the result of a {@link #getTimeOid} request.
* 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>
*/ */
public static void getTimeOid ( public static interface GotTimeBaseListener extends InvocationListener
Client client, String timeBase, Object rsptarget)
{ {
InvocationDirector invdir = client.getInvocationDirector(); /**
invdir.invoke(MODULE_NAME, GET_TIME_OID_REQUEST, * Communicates the result of a successful {@link #getTimeOid}
new Object[] { timeBase }, rsptarget); * request.
Log.debug("Sent getTimeOid request [timeBase=" + timeBase + "]."); */
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