diff --git a/bin/genreceiver b/bin/genreceiver index e2e97e32d..51aabecb7 100755 --- a/bin/genreceiver +++ b/bin/genreceiver @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: genreceiver,v 1.1 2002/08/11 05:53:13 mdb Exp $ +# $Id: genreceiver,v 1.2 2002/08/14 19:07:48 mdb Exp $ # # This script is used to generate invocation receiver marshaller and # unmarshaller classes based on receiver interface definitions. @@ -10,6 +10,7 @@ use Getopt::Long; use Template; use File::Basename; use POSIX qw(strftime); +use MD5; # use Dumpvalue; @@ -84,14 +85,14 @@ while ($srcfile = shift) { open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n"; while () { - if (/^public interface (\S+) extends (\S+)/) { + if (/^public interface (\S+) extends ([^, ]+)/) { $ifcname = $1; $imports{$ifcname} = 1; my $sname = $2; # if this interface does not extend InvocationReceiver, we # don't want to do nothin' - die "$script: Receiver interface does not extend " . + die "$script: $ifcname does not extend " . "InvocationReceiver\n (extends $sname).\n" if ($sname ne $ISVC_CNAME); @@ -156,7 +157,8 @@ while ($srcfile = shift) { # create a template processor my $ttconfig = { - RELATIVE => 1 + RELATIVE => 1, + ABSOLUTE => 1, }; my $tt = Template->new($ttconfig); my $genstamp = strftime("%T %D", localtime); @@ -207,7 +209,7 @@ while ($srcfile = shift) { my @dimportsl = sort keys %dimports; my %ddata = ( "imports" => \@dimportsl, "name" => $svcname, - "receiver_id" => 0, # TODO: do something here + "receiver_code" => MD5->hexhash($ifcname), "package" => $dcdrpkg, "methods" => \@methods, "genstamp" => $genstamp, @@ -232,8 +234,12 @@ sub grab_imports { # skip primitive types next unless ($class =~ m/\./); + # trim array delimiters + my $tclass = $class; + $tclass =~ s:[\[\]]::g; + # stuff the class into our import table - $imports{$class} = 1; + $imports{$tclass} = 1; } } diff --git a/bin/genservice b/bin/genservice index b39a82f1b..0ddea579b 100755 --- a/bin/genservice +++ b/bin/genservice @@ -1,6 +1,6 @@ #!/usr/bin/perl -w # -# $Id: genservice,v 1.2 2002/08/11 05:53:29 mdb Exp $ +# $Id: genservice,v 1.3 2002/08/14 19:07:48 mdb Exp $ # # This script is used to generate invocation service marshaller and # unmarshaller classes based on invocation service interface definitions. @@ -87,14 +87,14 @@ while ($srcfile = shift) { open(JPO, "$jpcmd|") or die "$script: Can't invoke '$jpcmd': $!\n"; while () { - if (/^public interface (\S+) extends (\S+)/) { + if (/^public interface (\S+) extends ([^, ]+)/) { $ifcname = $1; $imports{$ifcname} = 1; my $sname = $2; # if this interface does not extend InvocationService, we # don't want to do nothin' - die "$script: Service interface does not extend " . + die "$script: $ifcname does not extend " . "InvocationService\n (extends $sname).\n" if ($sname ne $ISVC_CNAME); @@ -203,7 +203,8 @@ while ($srcfile = shift) { # create a template processor my $ttconfig = { - RELATIVE => 1 + RELATIVE => 1, + ABSOLUTE => 1, }; my $tt = Template->new($ttconfig); my $genstamp = strftime("%T %D", localtime); @@ -284,16 +285,20 @@ sub grab_imports { # skip primitive types next unless ($class =~ m/\./); + # trim array delimiters + my $tclass = $class; + $tclass =~ s:[\[\]]::g; + # stuff the class into our import table - $imports{$class} = 1; + $imports{$tclass} = 1; # if this class is a listener and not in the same package as the # service class we're currently processing, we need to import its # marshaller as well - if ($class =~ m/Listener$/ && - $class !~ m/^$spackage/ && - $class !~ $ISVC_CNAME) { - my $mclass = $class; + if ($tclass =~ m/Listener$/ && + $tclass !~ m/^$spackage/ && + $tclass !~ $ISVC_CNAME) { + my $mclass = $tclass; $mclass =~ s:\.client\.:.data.:g; $mclass =~ s:Listener:Marshaller:g; $mclass =~ s:Service:Marshaller:g; diff --git a/docs/presents/notes.txt b/docs/presents/notes.txt index a2a17b21d..82840351b 100644 --- a/docs/presents/notes.txt +++ b/docs/presents/notes.txt @@ -306,7 +306,7 @@ Presents Notes -*- outline -*- public ChatDirector (PresentsContext ctx) { InvocationDirector invdir = ctx.getClient().getInvocationDirector(); - invdir.registerReceiver(new ChatDispatcher(this)); + invdir.registerReceiver(new ChatDecoder(this)); } // documentation inherited from interface @@ -316,7 +316,7 @@ Presents Notes -*- outline -*- } } - The dispatcher class referenced above is generated from the interface + The decoder class referenced above is generated from the interface specification and is responsible for converting notification events into the appropriate method calls on the receiver implementation with which it is constructed. @@ -354,3 +354,7 @@ Presents Notes -*- outline -*- "received" are converted to start with "send". If the interface method does not start with "received" its name will not be changed in the sender class definition. + +* 8/13/2002 +** True Names +Invocation -> Remote? That'd be a hairy pain... diff --git a/lib/decoder.tmpl b/lib/decoder.tmpl index 30b201b54..b94366313 100644 --- a/lib/decoder.tmpl +++ b/lib/decoder.tmpl @@ -1,5 +1,5 @@ // -// $Id: decoder.tmpl,v 1.1 2002/08/11 05:53:13 mdb Exp $ +// $Id: decoder.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $ package [% package %]; @@ -12,8 +12,8 @@ import [% import %]; */ public class [% name %]Decoder extends InvocationDecoder { - /** The id used to identify this notification receiver. */ - public static final short RECEIVER_ID = [% receiver_id %]; + /** The generated hash code used to identify this receiver class. */ + public static final String RECEIVER_CODE = "[% receiver_code %]"; [% FOREACH method = methods -%] /** The method id used to dispatch {@link [% name %]Receiver#[% method.mname %]} @@ -31,9 +31,9 @@ public class [% name %]Decoder extends InvocationDecoder } // documentation inherited - public int getReceiverId () + public String getReceiverCode () { - return RECEIVER_ID; + return RECEIVER_CODE; } // documentation inherited diff --git a/lib/dispatcher.tmpl b/lib/dispatcher.tmpl index f9b1f1f0e..6c8220c79 100644 --- a/lib/dispatcher.tmpl +++ b/lib/dispatcher.tmpl @@ -1,5 +1,5 @@ // -// $Id: dispatcher.tmpl,v 1.1 2002/08/09 03:36:09 mdb Exp $ +// $Id: dispatcher.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $ package [% package %]; @@ -36,7 +36,7 @@ public class [% name %]Dispatcher extends InvocationDispatcher [% FOREACH method = methods -%] case [% name %]Marshaller.[% method.mcode %]: (([% name %]Provider)provider).[% method.mname %]( - source, + source[% IF method.unwrapped_args %],[% END %] [% method.unwrapped_args %] ); return; diff --git a/lib/marshaller.tmpl b/lib/marshaller.tmpl index a58719e6d..f1ba51d8a 100644 --- a/lib/marshaller.tmpl +++ b/lib/marshaller.tmpl @@ -1,5 +1,5 @@ // -// $Id: marshaller.tmpl,v 1.1 2002/08/09 03:36:09 mdb Exp $ +// $Id: marshaller.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $ package [% package %]; @@ -34,8 +34,8 @@ public class [% name %]Marshaller extends InvocationMarshaller callerOid, requestId, [% lm.mcode %], new Object[] { [% lm.wrapped_args %] })); } -[% END -%] +[% END -%] // documentation inherited public void dispatchResponse (int methodId, Object[] args) { diff --git a/lib/sender.tmpl b/lib/sender.tmpl index 484215641..c6df2182b 100644 --- a/lib/sender.tmpl +++ b/lib/sender.tmpl @@ -1,5 +1,5 @@ // -// $Id: sender.tmpl,v 1.1 2002/08/11 05:53:13 mdb Exp $ +// $Id: sender.tmpl,v 1.2 2002/08/14 19:07:48 mdb Exp $ package [% package %]; @@ -19,10 +19,10 @@ public class [% name %]Sender extends InvocationSender * [% name %]Receiver#[% m.mname %]} on a client. */ public static void [% m.sname %] ( - ClientObject target, [% m.arglist %]) + ClientObject target[% IF m.arglist %], [% END %][% m.arglist %]) { sendNotification( - target, [% name %]Decoder.RECEIVER_ID, [% name %]Decoder.[% m.mcode %], + target, [% name %]Decoder.RECEIVER_CODE, [% name %]Decoder.[% m.mcode %], new Object[] { [% m.wrapped_args %] }); } diff --git a/src/java/com/threerings/admin/client/AdminService.java b/src/java/com/threerings/admin/client/AdminService.java index 14f18dec5..446ede2e3 100644 --- a/src/java/com/threerings/admin/client/AdminService.java +++ b/src/java/com/threerings/admin/client/AdminService.java @@ -1,34 +1,32 @@ // -// $Id: AdminService.java,v 1.1 2002/06/07 06:22:24 mdb Exp $ +// $Id: AdminService.java,v 1.2 2002/08/14 19:07:48 mdb Exp $ package com.threerings.admin.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; import com.threerings.admin.Log; -import com.threerings.admin.data.AdminCodes; /** - * Handles the client side of the admin invocation services. + * Defines the client side of the admin invocation services. */ -public class AdminService - implements AdminCodes +public interface AdminService extends InvocationService { /** - * Requests the list of config objects. This will result in a call to - * - *
-     *   public void handleConfigInfoResponse (int invid, String[] keys,
-     *                                         int[] oids)
-     * 
- * - * on the response target. + * Used to communicate a response to a {@link #getConfigInfo} request. */ - public static void getConfigInfo (Client client, Object rsptarget) + public static interface ConfigInfoListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - invdir.invoke(MODULE_NAME, GET_CONFIG_INFO_REQUEST, null, rsptarget); - Log.debug("Sent getConfigInfo request."); + /** + * Delivers a successful response to a {@link #getConfigInfo} + * request. + */ + public void gotConfigInfo (String[] keys, int[] oids); } + + /** + * Requests the list of config objects. + */ + public void getConfigInfo (Client client, ConfigInfoListener listener); } diff --git a/src/java/com/threerings/admin/client/ConfigEditorPanel.java b/src/java/com/threerings/admin/client/ConfigEditorPanel.java index 30959db0c..b0a2d5d70 100644 --- a/src/java/com/threerings/admin/client/ConfigEditorPanel.java +++ b/src/java/com/threerings/admin/client/ConfigEditorPanel.java @@ -1,5 +1,5 @@ // -// $Id: ConfigEditorPanel.java,v 1.3 2002/07/09 21:13:20 ray Exp $ +// $Id: ConfigEditorPanel.java,v 1.4 2002/08/14 19:07:48 mdb Exp $ package com.threerings.admin.client; @@ -20,6 +20,7 @@ import com.threerings.admin.Log; * displays their fields in a tree widget to be viewed and edited. */ public class ConfigEditorPanel extends JTabbedPane + implements AdminService.ConfigInfoListener { /** * Constructs an editor panel which will use the supplied context to @@ -39,8 +40,11 @@ public class ConfigEditorPanel extends JTabbedPane // if we have no children, ship off a getConfigInfo request to // find out what config objects are available for editing if (getComponentCount() == 0) { + // locate our admin service and use it to make a request + AdminService service = (AdminService) + _ctx.getClient().requireService(AdminService.class); Log.info("Sending get config info."); - AdminService.getConfigInfo(_ctx.getClient(), this); + service.getConfigInfo(_ctx.getClient(), this); } } @@ -48,7 +52,7 @@ public class ConfigEditorPanel extends JTabbedPane * Called in response to our getConfigInfo server-side service * request. */ - public void handleConfigInfo (int invid, String[] keys, int[] oids) + public void gotConfigInfo (String[] keys, int[] oids) { Log.info("Got config info: " + StringUtil.toString(keys)); // create object editor panels for each of the categories @@ -60,6 +64,12 @@ public class ConfigEditorPanel extends JTabbedPane } } + // documentation inherited from interface + public void requestFailed (String reason) + { + Log.warning("Failed to get config info [reason=" + reason + "]."); + } + /** * Called when the panel is hidden; this instructs all of our object * editors to clear out their subscriptions. diff --git a/src/java/com/threerings/admin/data/AdminCodes.java b/src/java/com/threerings/admin/data/AdminCodes.java deleted file mode 100644 index 403a4897b..000000000 --- a/src/java/com/threerings/admin/data/AdminCodes.java +++ /dev/null @@ -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"; -} diff --git a/src/java/com/threerings/admin/data/AdminMarshaller.java b/src/java/com/threerings/admin/data/AdminMarshaller.java new file mode 100644 index 000000000..371b89a5c --- /dev/null +++ b/src/java/com/threerings/admin/data/AdminMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/admin/server/AdminDispatcher.java b/src/java/com/threerings/admin/server/AdminDispatcher.java new file mode 100644 index 000000000..b41112b3f --- /dev/null +++ b/src/java/com/threerings/admin/server/AdminDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/admin/server/AdminProvider.java b/src/java/com/threerings/admin/server/AdminProvider.java index 84fa2f246..5c963b1fe 100644 --- a/src/java/com/threerings/admin/server/AdminProvider.java +++ b/src/java/com/threerings/admin/server/AdminProvider.java @@ -1,22 +1,20 @@ // -// $Id: AdminProvider.java,v 1.1 2002/06/07 06:22:24 mdb Exp $ +// $Id: AdminProvider.java,v 1.2 2002/08/14 19:07:48 mdb Exp $ package com.threerings.admin.server; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; - -import com.threerings.crowd.data.BodyObject; import com.threerings.admin.Log; -import com.threerings.admin.data.AdminCodes; +import com.threerings.admin.client.AdminService; /** - * Provides the server-side interface to various administrator services. + * Provides the server-side implementation of various administrator + * services. */ -public class AdminProvider extends InvocationProvider - implements AdminCodes +public class AdminProvider implements InvocationProvider { /** * Constructs an admin provider and registers it with the invocation @@ -25,15 +23,15 @@ public class AdminProvider extends InvocationProvider */ public static void init (InvocationManager invmgr) { - invmgr.registerProvider(MODULE_NAME, new AdminProvider()); + invmgr.registerDispatcher( + new AdminDispatcher(new AdminProvider()), true); } /** - * Processes a request from a client to obtain the configuration keys - * and oids for all registered configuration objects. + * Handles a request for the list of config objects. */ - public void handleGetConfigInfoRequest (BodyObject source, int invid) - throws ServiceFailedException + public void getConfigInfo ( + ClientObject caller, AdminService.ConfigInfoListener listener) { // we don't have to validate the request because the user can't do // anything with the keys or oids unless they're an admin (we put @@ -46,6 +44,6 @@ public class AdminProvider extends InvocationProvider for (int ii = 0; ii < keys.length; ii++) { oids[ii] = ConfObjRegistry.getObject(keys[ii]).getOid(); } - sendResponse(source, invid, CONFIG_INFO_RESPONSE, keys, oids); + listener.gotConfigInfo(keys, oids); } } diff --git a/src/java/com/threerings/crowd/chat/ChatMessageHandler.java b/src/java/com/threerings/crowd/chat/ChatMessageHandler.java deleted file mode 100644 index a62e7561b..000000000 --- a/src/java/com/threerings/crowd/chat/ChatMessageHandler.java +++ /dev/null @@ -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); - } -} diff --git a/src/java/com/threerings/crowd/chat/client/ChatDecoder.java b/src/java/com/threerings/crowd/chat/client/ChatDecoder.java new file mode 100644 index 000000000..af8b468d2 --- /dev/null +++ b/src/java/com/threerings/crowd/chat/client/ChatDecoder.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/chat/client/ChatDirector.java b/src/java/com/threerings/crowd/chat/client/ChatDirector.java index b06a2ab47..066c18ae5 100644 --- a/src/java/com/threerings/crowd/chat/client/ChatDirector.java +++ b/src/java/com/threerings/crowd/chat/client/ChatDirector.java @@ -1,5 +1,5 @@ // -// $Id: ChatDirector.java,v 1.32 2002/08/14 00:48:57 shaper Exp $ +// $Id: ChatDirector.java,v 1.33 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.chat; @@ -8,7 +8,6 @@ import java.util.Iterator; import java.util.LinkedList; import com.samskivert.util.HashIntMap; -import com.samskivert.util.Tuple; import com.threerings.presents.client.*; import com.threerings.presents.dobj.*; @@ -26,9 +25,8 @@ import com.threerings.crowd.util.CrowdContext; * services. It handles both place constrained chat as well as direct * messaging. */ -public class ChatDirector - implements LocationObserver, MessageListener, InvocationReceiver, - ChatCodes +public class ChatDirector extends BasicDirector + implements ChatCodes, LocationObserver, MessageListener, ChatReceiver { /** * An interface that can receive information about the {@link @@ -50,6 +48,8 @@ public class ChatDirector */ public ChatDirector (CrowdContext ctx, MessageManager msgmgr, String bundle) { + super(ctx); + // keep the context around _ctx = ctx; _msgmgr = msgmgr; @@ -57,7 +57,7 @@ public class ChatDirector // register for chat notifications _ctx.getClient().getInvocationDirector().registerReceiver( - MODULE_NAME, this); + new ChatDecoder(this)); // register ourselves as a location observer _ctx.getLocationDirector().addLocationObserver(this); @@ -218,41 +218,55 @@ public class ChatDirector } /** - * Requests that a speak message be generated and delivered to all - * users that occupy the place object that we currently occupy. - * - * @param message the contents of the speak message. - * - * @return an id which can be used to coordinate this speak request - * with the response that will be delivered to all active chat - * displays when it arrives, or -1 if we were unable to make the - * request because we are not currently in a place. + * Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the + * place object that we currently occupy. */ - public int requestSpeak (String message) + public void requestSpeak (String message) + { + requestSpeak(message, DEFAULT_MODE); + } + + /** + * Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the + * place object that we currently occupy. + */ + public void requestSpeak (String message, byte mode) { // make sure we're currently in a place if (_place == null) { - return -1; - } - - // make sure they can say what they want to say - for (Iterator iter = _validators.iterator(); iter.hasNext(); ) { - if (!((ChatValidator) iter.next()).validateSpeak(message)) { - return -1; - } + return; } // dispatch a speak request on the active place object - int reqid = - _ctx.getClient().getInvocationDirector().nextInvocationId(); - Object[] args = new Object[] { new Integer(reqid), message }; - MessageEvent mevt = new MessageEvent( - _place.getOid(), SPEAK_REQUEST, args); - _ctx.getDObjectManager().postEvent(mevt); - // TODO: when this gets changed such that we actually validate - // this on the server, we have to make sure that the - // user is not on a portal before we allow the 'shout' to go through - return reqid; + requestSpeak(_place.speakService, message, mode); + } + + /** + * Requests that a speak message with the specified mode be generated + * and delivered via the supplied speak service instance (which will + * be associated with a particular "speak object"). The message will + * first be validated by all registered {@link ChatValidator}s (and + * possibly vetoed) before being dispatched. + * + * @param speakService the speak service to use when generating the + * speak request. + * @param message the contents of the speak message. + * @param mode a speech mode that will be interpreted by the {@link + * ChatDisplay} implementations that eventually display this speak + * message. + */ + public void requestSpeak ( + SpeakService speakService, String message, byte mode) + { + // make sure they can say what they want to say + for (Iterator iter = _validators.iterator(); iter.hasNext(); ) { + if (!((ChatValidator) iter.next()).validateSpeak(message)) { + return; + } + } + + // dispatch a speak request using the supplied speak service + speakService.speak(_ctx.getClient(), message, mode); } /** @@ -262,26 +276,34 @@ public class ChatDirector * @param target the username of the user to which the tell message * should be delivered. * @param message the contents of the tell message. - * - * @return an id which can be used to coordinate this request with the - * tell response that will be delivered to all active chat displays - * when it arrives. */ - public int requestTell (String target, String message) + public void requestTell (final String target, final String message) { // make sure they can say what they want to say for (Iterator iter = _validators.iterator(); iter.hasNext(); ) { if (!((ChatValidator) iter.next()).validateTell( target, message)) { - return -1; + return; } } - int invid = ChatService.tell(_ctx.getClient(), target, message, this); - // cache the tell info for use when reporting success or failure - // to our various chat displays - _tells.put(invid, new Tuple(target, message)); - return invid; + // create a listener that will report success or failure + ChatService.TellListener listener = new ChatService.TellListener() { + public void tellSucceeded () { + String msg = MessageBundle.tcompose( + "m.told_format", target, message); + displayFeedbackMessage(_bundle, msg); + addChatter(target); + } + + public void requestFailed (String reason) { + String msg = + MessageBundle.compose("m.tell_failed", target, reason); + displayFeedbackMessage(_bundle, msg); + } + }; + + _cservice.tell(_ctx.getClient(), target, message, listener); } /** @@ -308,6 +330,13 @@ public class ChatDirector _auxes.remove(source.getOid()); } + // documentation inherited from interface + protected void fetchServices (Client client) + { + // get a handle on our chat service + _cservice = (ChatService)client.requireService(ChatService.class); + } + // documentation inherited public boolean locationMayChange (int placeId) { @@ -340,88 +369,33 @@ public class ChatDirector public void messageReceived (MessageEvent event) { String name = event.getName(); - if (name.equals(ChatService.SPEAK_NOTIFICATION)) { + if (name.equals(SPEAK_NOTIFICATION)) { handleSpeakMessage( getLocalType(event.getTargetOid()), event.getArgs()); - } else if (name.equals(ChatService.SYSTEM_NOTIFICATION)) { + } else if (name.equals(SYSTEM_NOTIFICATION)) { handleSystemMessage( getLocalType(event.getTargetOid()), event.getArgs()); } } - /** - * Called by the invocation director when another client has requested - * a tell message be delivered to this client. - */ - public void handleTellNotification (String source, String message) + // documentation inherited from interface + public void receivedTell (String speaker, String bundle, String message) { - // bail if the speaker is blocked - if (isBlocked(source)) { + // ignore messages from blocked users + if (isBlocked(speaker)) { return; } + // if the message need be translated, do so + if (bundle != null) { + message = xlate(bundle, message); + } + UserMessage um = new UserMessage( - message, ChatCodes.TELL_CHAT_TYPE, source, ChatCodes.DEFAULT_MODE); + message, ChatCodes.TELL_CHAT_TYPE, speaker, ChatCodes.DEFAULT_MODE); dispatchMessage(um); - addChatter(source); - } - - /** - * Called by the invocation director when an entity on the server has - * requested that we deliver a tell notification to this - * client. Because the server generated the notification, displays - * will want to translate the message itself using the supplied bundle - * identifier. - */ - public void handleTellNotification ( - String source, String bundle, String message) - { - handleTellNotification(source, xlate(bundle, message)); - } - - /** - * Called in response to a tell request that succeeded. - * - * @param invid the invocation id of the tell request. - */ - public void handleTellSucceeded (int invid) - { - // remove the tell info for the successful request - Tuple tup = (Tuple)_tells.remove(invid); - if (tup == null) { - Log.warning("Notified of successful tell request but no " + - "tell info available [invid=" + invid + "]."); - return; - } - - // pass this on to our chat displays - String target = (String)tup.left, message = (String)tup.right; - displayFeedbackMessage( - _bundle, MessageBundle.tcompose("m.told_format", target, message)); - addChatter(target); - } - - /** - * Called in response to a tell request that failed. - * - * @param invid the invocation id of the tell request. - * @param reason the code that describes the reason for failure. - */ - public void handleTellFailed (int invid, String reason) - { - // remove the tell info for the failed request - Tuple tup = (Tuple)_tells.remove(invid); - if (tup == null) { - Log.warning("Notified of failed tell request but no " + - "tell info available [invid=" + invid + "]."); - return; - } - - // pass this on to our chat displays - String target = (String)tup.left; - displayFeedbackMessage( - _bundle, MessageBundle.compose("m.tell_failed", target, reason)); + addChatter(speaker); } /** @@ -519,9 +493,20 @@ public class ChatDirector return (_muter != null) && _muter.isMuted(username); } + /** + * Used to assign unique ids to all speak requests. + */ + protected synchronized int nextRequestId () + { + return _requestId++; + } + /** Our active chat context. */ protected CrowdContext _ctx; + /** Provides access to chat-related server-side services. */ + protected ChatService _cservice; + /** The message manager. */ protected MessageManager _msgmgr; @@ -544,16 +529,15 @@ public class ChatDirector /** An optionally present mutelist director. */ protected MuteDirector _muter; - /** A cache of the target and message text associated with outstanding - * tell chat requests. */ - protected HashIntMap _tells = new HashIntMap(); - /** Usernames of users we've recently chatted with. */ protected LinkedList _chatters = new LinkedList(); /** Observers that are watching our chatters list. */ protected ArrayList _chatterObservers = new ArrayList(); + /** Used by {@link #nextRequestId}. */ + protected int _requestId; + /** The maximum number of chatter usernames to track. */ protected static final int MAX_CHATTERS = 6; } diff --git a/src/java/com/threerings/crowd/chat/client/ChatReceiver.java b/src/java/com/threerings/crowd/chat/client/ChatReceiver.java new file mode 100644 index 000000000..ded6e474c --- /dev/null +++ b/src/java/com/threerings/crowd/chat/client/ChatReceiver.java @@ -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); +} diff --git a/src/java/com/threerings/crowd/chat/client/ChatService.java b/src/java/com/threerings/crowd/chat/client/ChatService.java index 35d882c08..42f8da23a 100644 --- a/src/java/com/threerings/crowd/chat/client/ChatService.java +++ b/src/java/com/threerings/crowd/chat/client/ChatService.java @@ -1,22 +1,31 @@ // -// $Id: ChatService.java,v 1.7 2002/05/15 23:54:34 mdb Exp $ +// $Id: ChatService.java,v 1.8 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.chat; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; -import com.threerings.crowd.Log; +import com.threerings.presents.client.InvocationService; /** * The chat services provide a mechanism by which the client can broadcast * chat messages to all clients that are subscribed to a particular place * object or directly to a particular client. These services should not be - * used directly, but instead should be accessed via the chat director. - * - * @see ChatDirector + * used directly, but instead should be accessed via the {@link + * ChatDirector}. */ -public class ChatService implements ChatCodes +public interface ChatService extends InvocationService { + /** + * Used to communicate the response to a {@link #tell} request. + */ + public static interface TellListener extends InvocationListener + { + /** + * Communicates the response to a {@link #tell} request. + */ + public void tellSucceeded (); + } + /** * Requests that a tell message be delivered to the user with username * equal to target. @@ -25,18 +34,8 @@ public class ChatService implements ChatCodes * @param target the username of the user to which the tell message * should be delivered. * @param message the contents of the message. - * @param rsptarget the chat director reference that will receive the - * tell response. - * - * @return the invocation request id of the generated tell request. + * @param listener the reference that will receive the tell response. */ - public static int tell (Client client, String target, String message, - ChatDirector rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { target, message }; - Log.debug("Sending tell request [tgt=" + target + - ", msg=" + message + "]."); - return invdir.invoke(MODULE_NAME, TELL_REQUEST, args, rsptarget); - } + public void tell (Client client, String target, String message, + TellListener listener); } diff --git a/src/java/com/threerings/crowd/chat/client/SpeakService.java b/src/java/com/threerings/crowd/chat/client/SpeakService.java new file mode 100644 index 000000000..93aa30edf --- /dev/null +++ b/src/java/com/threerings/crowd/chat/client/SpeakService.java @@ -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); +} diff --git a/src/java/com/threerings/crowd/chat/data/ChatCodes.java b/src/java/com/threerings/crowd/chat/data/ChatCodes.java index 4a3d60f73..5d7474d61 100644 --- a/src/java/com/threerings/crowd/chat/data/ChatCodes.java +++ b/src/java/com/threerings/crowd/chat/data/ChatCodes.java @@ -1,5 +1,5 @@ // -// $Id: ChatCodes.java,v 1.10 2002/07/26 20:35:01 ray Exp $ +// $Id: ChatCodes.java,v 1.11 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.chat; @@ -10,19 +10,13 @@ import com.threerings.presents.data.InvocationCodes; */ public interface ChatCodes extends InvocationCodes { - /** The module name for the chat services. */ - public static final String MODULE_NAME = "chat"; - - /** The chat localtype code for chat messages delivered on the place object - * currently occupied by the client. This is the only type of chat - * message that will be delivered unless the chat director is + /** The chat localtype code for chat messages delivered on the place + * object currently occupied by the client. This is the only type of + * chat message that will be delivered unless the chat director is * explicitly provided with other chat message sources via {@link * ChatDirector#addAuxiliarySource}. */ public static final String PLACE_CHAT_TYPE = "placeChat"; - /** The message identifier for a speak request message. */ - public static final String SPEAK_REQUEST = "spkreq"; - /** The message identifier for a speak notification message. */ public static final String SPEAK_NOTIFICATION = "spknot"; @@ -32,31 +26,16 @@ public interface ChatCodes extends InvocationCodes /** The chat localtype for tells. */ public static final String TELL_CHAT_TYPE = "tellChat"; - /** The message identifier for a tell request. */ - public static final String TELL_REQUEST = "Tell"; - - /** The response identifier for a successful tell request. This is - * mapped by the invocation services to a call to {@link - * ChatDirector#handleTellSucceeded}. */ - public static final String TELL_SUCCEEDED_RESPONSE = "TellSucceeded"; - - /** The response identifier for a failed tell request. This is mapped - * by the invocation services to a call to {@link - * ChatDirector#handleTellFailed}. */ - public static final String TELL_FAILED_RESPONSE = "TellFailed"; - - /** The message identifier for a tell notification. */ - public static final String TELL_NOTIFICATION = "Tell"; - - /** The default mode used by speak requests. */ + /** The default mode used by {@link SpeakService#speak} requests. */ public static final byte DEFAULT_MODE = 0; - /** The think mode to indicate that the user is thinking - * what they're saying, or is it that they're saying what they're - * thinking? */ + /** A {@link SpeakService#speak} mode to indicate that the user is + * thinking what they're saying, or is it that they're saying what + * they're thinking? */ public static final byte THINK_MODE = 1; - /** The mode to indicate that a speak is actually an emote. */ + /** A {@link SpeakService#speak} mode to indicate that a speak is + * actually an emote. */ public static final byte EMOTE_MODE = 2; /** An error code delivered when the user targeted for a tell diff --git a/src/java/com/threerings/crowd/chat/data/ChatMarshaller.java b/src/java/com/threerings/crowd/chat/data/ChatMarshaller.java new file mode 100644 index 000000000..4b5c6a4ca --- /dev/null +++ b/src/java/com/threerings/crowd/chat/data/ChatMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/chat/data/SpeakMarshaller.java b/src/java/com/threerings/crowd/chat/data/SpeakMarshaller.java new file mode 100644 index 000000000..8e99c2355 --- /dev/null +++ b/src/java/com/threerings/crowd/chat/data/SpeakMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/chat/server/ChatDispatcher.java b/src/java/com/threerings/crowd/chat/server/ChatDispatcher.java new file mode 100644 index 000000000..4111bbc96 --- /dev/null +++ b/src/java/com/threerings/crowd/chat/server/ChatDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/crowd/chat/server/ChatProvider.java b/src/java/com/threerings/crowd/chat/server/ChatProvider.java index 8c5f914ed..808cf2616 100644 --- a/src/java/com/threerings/crowd/chat/server/ChatProvider.java +++ b/src/java/com/threerings/crowd/chat/server/ChatProvider.java @@ -1,50 +1,70 @@ // -// $Id: ChatProvider.java,v 1.12 2002/07/22 22:54:03 ray Exp $ +// $Id: ChatProvider.java,v 1.13 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.chat; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.MessageEvent; + +import com.threerings.presents.server.InvocationException; +import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationProvider; +import com.threerings.crowd.Log; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.server.CrowdServer; -import com.threerings.crowd.Log; +import com.threerings.crowd.chat.ChatService.TellListener; /** * The chat provider handles the server side of the chat-related * invocation services. */ public class ChatProvider - extends InvocationProvider implements ChatCodes + implements ChatCodes, InvocationProvider { + /** + * Initializes the chat services and registers a chat provider with + * the invocation manager. + */ + public static void init (InvocationManager invmgr, DObjectManager omgr) + { + _omgr = omgr; + + // register a chat provider with the invocation manager + invmgr.registerDispatcher(new ChatDispatcher( + new ChatProvider()), true); + } + /** * Processes a request from a client to deliver a tell message to * another client. */ - public void handleTellRequest ( - BodyObject source, int invid, String target, String message) + public void tell (ClientObject caller, String target, String message, + TellListener listener) + throws InvocationException { - // look up the target body object + // make sure the target user is online BodyObject tobj = CrowdServer.lookupBody(target); if (tobj == null) { - sendResponse(source, invid, TELL_FAILED_RESPONSE, - USER_NOT_ONLINE); - - } else { - // deliver a tell notification to the target player - sendTellMessage(tobj.getOid(), source.username, null, message); - // let the teller know it went ok - sendResponse(source, invid, TELL_SUCCEEDED_RESPONSE); + throw new InvocationException(USER_NOT_ONLINE); } + + // deliver a tell notification to the target player + BodyObject source = (BodyObject)caller; + sendTellMessage(tobj, source.username, null, message); + + // let the teller know it went ok + listener.tellSucceeded(); } /** * Delivers a tell notification to the specified target player, * originating with the specified speaker. * - * @param targetOid the body object id of the user that will receive - * the tell message. + * @param target the body object of the user that will receive the + * tell message. * @param speaker the username of the user that generated the message * (or some special speaker name for server messages). * @param bundle the bundle identifier that will be used by the client @@ -54,89 +74,11 @@ public class ChatProvider * @param message the text of the chat message. */ public static void sendTellMessage ( - int targetOid, String speaker, String bundle, String message) + BodyObject target, String speaker, String bundle, String message) { - Object[] args = null; - if (bundle == null) { - args = new Object[] { speaker, message }; - } else { - args = new Object[] { speaker, bundle, message }; - } - CrowdServer.invmgr.sendNotification( - targetOid, MODULE_NAME, TELL_NOTIFICATION, args); + ChatSender.sendTell(target, speaker, bundle, message); } - /** - * Sends a chat notification to the specified place object originating - * with the specified speaker (the speaker optionally being a server - * entity that wishes to fake a "speak" message) and with the supplied - * message content. - * - * @param placeOid the place to which to deliver the chat message. - * @param speaker the username of the user that generated the message - * (or some special speaker name for server messages). - * @param bundle null when the message originates from a real human, - * the bundle identifier that will be used by the client to translate - * the message text when the message originates from a server entity - * "faking" a chat message. - * @param message the text of the chat message. - */ - public static void sendChatMessage ( - int placeOid, String speaker, String bundle, String message) - { - sendChatMessage( - placeOid, speaker, bundle, message, ChatCodes.DEFAULT_MODE); - } - - /** - * Sends a chat notification to the specified place object originating - * with the specified speaker (the speaker optionally being a server - * entity that wishes to fake a "speak" message) and with the supplied - * message content. - * - * @param placeOid the place to which to deliver the chat message. - * @param speaker the username of the user that generated the message - * (or some special speaker name for server messages). - * @param bundle null when the message originates from a real human, - * the bundle identifier that will be used by the client to translate - * the message text when the message originates from a server entity - * "faking" a chat message. - * @param message the text of the chat message. - * @param mode the mode of the message, @see ChatCodes.DEFAULT_MODE - */ - public static void sendChatMessage ( - int placeOid, String speaker, String bundle, String message, - byte mode) - { - Object[] outargs = null; - if (bundle == null) { - outargs = new Object[] { speaker, message, new Byte(mode) }; - } else { - outargs = new Object[] { speaker, bundle, message, new Byte(mode) }; - } - MessageEvent nevt = new MessageEvent( - placeOid, ChatService.SPEAK_NOTIFICATION, outargs); - CrowdServer.omgr.postEvent(nevt); - } - - /** - * Sends a system message notification to the specified place object - * with the supplied message content. A system message is one that - * will be rendered where the chat messages are rendered, but in a way - * that makes it clear that it is a message from the server. - * - * @param placeOid the place to which to deliver the message. - * @param bundle the name of the localization bundle that should be - * used to translate this system message prior to displaying it to the - * client. - * @param message the text of the message. - */ - public static void sendSystemMessage ( - int placeOid, String bundle, String message) - { - Object[] outargs = new Object[] { bundle, message }; - MessageEvent nevt = new MessageEvent( - placeOid, ChatService.SYSTEM_NOTIFICATION, outargs); - CrowdServer.omgr.postEvent(nevt); - } + /** The distributed object manager used by the chat services. */ + protected static DObjectManager _omgr; } diff --git a/src/java/com/threerings/crowd/chat/server/ChatSender.java b/src/java/com/threerings/crowd/chat/server/ChatSender.java new file mode 100644 index 000000000..86c801cb0 --- /dev/null +++ b/src/java/com/threerings/crowd/chat/server/ChatSender.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/chat/server/SpeakDispatcher.java b/src/java/com/threerings/crowd/chat/server/SpeakDispatcher.java new file mode 100644 index 000000000..3e2ecad8d --- /dev/null +++ b/src/java/com/threerings/crowd/chat/server/SpeakDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/crowd/chat/server/SpeakProvider.java b/src/java/com/threerings/crowd/chat/server/SpeakProvider.java new file mode 100644 index 000000000..7917c5740 --- /dev/null +++ b/src/java/com/threerings/crowd/chat/server/SpeakProvider.java @@ -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; +} diff --git a/src/java/com/threerings/crowd/client/LocationDecoder.java b/src/java/com/threerings/crowd/client/LocationDecoder.java new file mode 100644 index 000000000..f8b5884f7 --- /dev/null +++ b/src/java/com/threerings/crowd/client/LocationDecoder.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/client/LocationDirector.java b/src/java/com/threerings/crowd/client/LocationDirector.java index da3183525..3ea38fe6e 100644 --- a/src/java/com/threerings/crowd/client/LocationDirector.java +++ b/src/java/com/threerings/crowd/client/LocationDirector.java @@ -1,5 +1,5 @@ // -// $Id: LocationDirector.java,v 1.24 2002/06/14 01:40:16 ray Exp $ +// $Id: LocationDirector.java,v 1.25 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.client; @@ -10,9 +10,9 @@ import com.samskivert.util.ObserverList; import com.samskivert.util.ObserverList.ObserverOp; import com.samskivert.util.ResultListener; +import com.threerings.presents.client.BasicDirector; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationReceiver; -import com.threerings.presents.client.SessionObserver; + import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.ObjectAccessException; @@ -32,8 +32,9 @@ import com.threerings.crowd.util.CrowdContext; * provides a mechanism for ratifying a request to move to a new place * before actually issuing the request. */ -public class LocationDirector - implements LocationCodes, SessionObserver, Subscriber, InvocationReceiver +public class LocationDirector extends BasicDirector + implements LocationCodes, Subscriber, LocationReceiver, + LocationService.MoveListener { /** * Used to recover from a moveTo request that was accepted but @@ -55,15 +56,14 @@ public class LocationDirector */ public LocationDirector (CrowdContext ctx) { + super(ctx); + // keep this around for later _ctx = ctx; - // register ourselves as a client observer - ctx.getClient().addClientObserver(this); - // register for location notifications _ctx.getClient().getInvocationDirector().registerReceiver( - MODULE_NAME, this); + new LocationDecoder(this)); } /** @@ -137,7 +137,7 @@ public class LocationDirector _pendingPlaceId = placeId; // issue a moveTo request - LocationService.moveTo(_ctx.getClient(), placeId, this); + _lservice.moveTo(_ctx.getClient(), placeId, this); return true; } @@ -314,7 +314,9 @@ public class LocationDirector // documentation inherited from interface public void clientDidLogon (Client client) { - // get a copy of our body object + super.clientDidLogon(client); + + // subscribe to our body object Subscriber sub = new Subscriber() { public void objectAvailable (DObject object) { @@ -332,6 +334,14 @@ public class LocationDirector client.getDObjectManager().subscribeToObject(cloid, sub); } + // documentation inherited + protected void fetchServices (Client client) + { + // obtain our service handle + _lservice = (LocationService) + client.requireService(LocationService.class); + } + protected void gotBodyObject (BodyObject clobj) { // check to see if we are already in a location, in which case @@ -341,17 +351,19 @@ public class LocationDirector // documentation inherited from interface public void clientDidLogoff (Client client) { + super.clientDidLogoff(client); + // clear ourselves out and inform observers of our departure didLeavePlace(); // let our observers know that we're no longer in a location _observers.apply(_didChangeOp); + + _lservice = null; } - /** - * Called in response to a successful moveTo request. - */ - public void handleMoveSucceeded (int invid, PlaceConfig config) + // documentation inherited from interface + public void moveSucceeded (PlaceConfig config) { // handle the successful move didMoveTo(_pendingPlaceId, config); @@ -360,10 +372,8 @@ public class LocationDirector _pendingPlaceId = -1; } - /** - * Called in response to a failed moveTo request. - */ - public void handleMoveFailed (int invid, String reason) + // documentation inherited from interface + public void requestFailed (String reason) { // clear out our pending request oid int placeId = _pendingPlaceId; @@ -376,13 +386,8 @@ public class LocationDirector notifyFailure(placeId, reason); } - /** - * Called when the server has decided to forcibly move us to another - * room. The server first ejects us from our previous room and then - * sends us a notification with our new location. We then turn around - * and issue a standard moveTo request. - */ - public void handleMoveNotification (int placeId) + // documentation inherited from interface + public void forcedMove (int placeId) { Log.info("Moving at request of server [placeId=" + placeId + "]."); @@ -493,6 +498,9 @@ public class LocationDirector /** The context through which we access needed services. */ protected CrowdContext _ctx; + /** Provides access to location services. */ + protected LocationService _lservice; + /** Our location observer list. */ protected ObserverList _observers = new ObserverList(ObserverList.SAFE_IN_ORDER_NOTIFY); diff --git a/src/java/com/threerings/crowd/client/LocationReceiver.java b/src/java/com/threerings/crowd/client/LocationReceiver.java new file mode 100644 index 000000000..9e3e754de --- /dev/null +++ b/src/java/com/threerings/crowd/client/LocationReceiver.java @@ -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); +} diff --git a/src/java/com/threerings/crowd/client/LocationService.java b/src/java/com/threerings/crowd/client/LocationService.java index 1564fd0a8..9f625f306 100644 --- a/src/java/com/threerings/crowd/client/LocationService.java +++ b/src/java/com/threerings/crowd/client/LocationService.java @@ -1,33 +1,43 @@ // -// $Id: LocationService.java,v 1.6 2002/05/15 23:54:34 mdb Exp $ +// $Id: LocationService.java,v 1.7 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; + import com.threerings.crowd.Log; -import com.threerings.crowd.data.LocationCodes; +import com.threerings.crowd.data.PlaceConfig; /** * The location services provide a mechanism by which the client can * request to move from place to place in the server. These services * should not be used directly, but instead should be accessed via the - * location director. - * - * @see LocationDirector + * {@link LocationDirector}. */ -public class LocationService implements LocationCodes +public interface LocationService extends InvocationService { /** - * Requests that that this client's body be moved to the specified - * location. + * Used to communicate responses to {@link #moveTo} requests. */ - public static void moveTo (Client client, int placeId, - LocationDirector rsptarget) + public static interface MoveListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(placeId) }; - invdir.invoke(MODULE_NAME, MOVE_TO_REQUEST, args, rsptarget); - Log.debug("Sent moveTo request [place=" + placeId + "]."); + /** + * Called in response to a successful {@link #moveTo} request. + */ + public void moveSucceeded (PlaceConfig config); } + + /** + * Requests that this client's body be moved to the specified + * location. + * + * @param client a reference to the client object that defines the + * context in which this invocation service should be executed. + * @param placeId the object id of the place object to which the body + * should be moved. + * @param listener the listener that will be informed of success or + * failure. + */ + public void moveTo (Client client, int placeId, MoveListener listener); } diff --git a/src/java/com/threerings/crowd/data/BodyObject.dobj b/src/java/com/threerings/crowd/data/BodyObject.dobj index f46f96e7b..0a325e367 100644 --- a/src/java/com/threerings/crowd/data/BodyObject.dobj +++ b/src/java/com/threerings/crowd/data/BodyObject.dobj @@ -1,5 +1,5 @@ // -// $Id: BodyObject.dobj,v 1.9 2002/06/20 22:37:32 mdb Exp $ +// $Id: BodyObject.dobj,v 1.10 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; @@ -18,9 +18,7 @@ public class BodyObject extends ClientObject */ public int location = -1; - /** - * Returns a short string identifying this body. - */ + // documentation inherited public String who () { return username + " (" + getOid() + ")"; diff --git a/src/java/com/threerings/crowd/data/BodyObject.java b/src/java/com/threerings/crowd/data/BodyObject.java index 69834b878..c9f121c24 100644 --- a/src/java/com/threerings/crowd/data/BodyObject.java +++ b/src/java/com/threerings/crowd/data/BodyObject.java @@ -1,5 +1,5 @@ // -// $Id: BodyObject.java,v 1.3 2002/06/20 22:37:32 mdb Exp $ +// $Id: BodyObject.java,v 1.4 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; @@ -24,9 +24,7 @@ public class BodyObject extends ClientObject */ public int location = -1; - /** - * Returns a short string identifying this body. - */ + // documentation inherited public String who () { return username + " (" + getOid() + ")"; diff --git a/src/java/com/threerings/crowd/data/LocationCodes.java b/src/java/com/threerings/crowd/data/LocationCodes.java index 1d0c8d7c9..5051cce42 100644 --- a/src/java/com/threerings/crowd/data/LocationCodes.java +++ b/src/java/com/threerings/crowd/data/LocationCodes.java @@ -1,35 +1,15 @@ // -// $Id: LocationCodes.java,v 1.3 2002/05/26 02:24:46 mdb Exp $ +// $Id: LocationCodes.java,v 1.4 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; import com.threerings.presents.data.InvocationCodes; -import com.threerings.crowd.client.LocationDirector; /** * Contains codes used by the location invocation services. */ public interface LocationCodes extends InvocationCodes { - /** The module name for the location services. */ - public static final String MODULE_NAME = "location"; - - /** The message identifier for a moveTo request. */ - public static final String MOVE_TO_REQUEST = "MoveTo"; - - /** The response identifier for a successful moveTo request. This is - * mapped by the invocation services to a call to {@link - * LocationDirector#handleMoveSucceeded}. */ - public static final String MOVE_SUCCEEDED_RESPONSE = "MoveSucceeded"; - - /** The response identifier for a failed moveTo request. This is - * mapped by the invocation services to a call to {@link - * LocationDirector#handleMoveFailed}. */ - public static final String MOVE_FAILED_RESPONSE = "MoveFailed"; - - /** The message identifier for a move notification. */ - public static final String MOVE_NOTIFICATION = "Move"; - /** An error code indicating that a place identified by a particular * place id does not exist. Usually generated by a failed moveTo * request. */ diff --git a/src/java/com/threerings/crowd/data/LocationMarshaller.java b/src/java/com/threerings/crowd/data/LocationMarshaller.java new file mode 100644 index 000000000..30f611a06 --- /dev/null +++ b/src/java/com/threerings/crowd/data/LocationMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/data/OccupantInfo.java b/src/java/com/threerings/crowd/data/OccupantInfo.java index c6c6badeb..0145cf7c6 100644 --- a/src/java/com/threerings/crowd/data/OccupantInfo.java +++ b/src/java/com/threerings/crowd/data/OccupantInfo.java @@ -1,5 +1,5 @@ // -// $Id: OccupantInfo.java,v 1.8 2002/07/23 05:54:52 mdb Exp $ +// $Id: OccupantInfo.java,v 1.9 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; @@ -40,7 +40,7 @@ public class OccupantInfo extends SimpleStreamableObject } // documentation inherited - public Object getKey () + public Comparable getKey () { return bodyOid; } diff --git a/src/java/com/threerings/crowd/data/PlaceObject.dobj b/src/java/com/threerings/crowd/data/PlaceObject.dobj index 36451fee3..6ef3aaa09 100644 --- a/src/java/com/threerings/crowd/data/PlaceObject.dobj +++ b/src/java/com/threerings/crowd/data/PlaceObject.dobj @@ -1,9 +1,13 @@ // -// $Id: PlaceObject.dobj,v 1.9 2002/04/15 14:30:49 shaper Exp $ +// $Id: PlaceObject.dobj,v 1.10 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; -import com.threerings.presents.dobj.*; +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.DSet; +import com.threerings.presents.dobj.OidList; + +import com.threerings.crowd.chat.SpeakMarshaller; public class PlaceObject extends DObject { @@ -19,4 +23,7 @@ public class PlaceObject extends DObject * to be known by everyone in the place. */ public DSet occupantInfo = new DSet(); + + /** Used to generate speak requests on this place object. */ + public SpeakMarshaller speakService; } diff --git a/src/java/com/threerings/crowd/data/PlaceObject.java b/src/java/com/threerings/crowd/data/PlaceObject.java index 760cd8123..181a07d70 100644 --- a/src/java/com/threerings/crowd/data/PlaceObject.java +++ b/src/java/com/threerings/crowd/data/PlaceObject.java @@ -1,9 +1,13 @@ // -// $Id: PlaceObject.java,v 1.6 2002/04/15 14:35:31 shaper Exp $ +// $Id: PlaceObject.java,v 1.7 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.data; -import com.threerings.presents.dobj.*; +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.DSet; +import com.threerings.presents.dobj.OidList; + +import com.threerings.crowd.chat.SpeakMarshaller; public class PlaceObject extends DObject { @@ -13,6 +17,9 @@ public class PlaceObject extends DObject /** The field name of the occupantInfo field. */ public static final String OCCUPANT_INFO = "occupantInfo"; + /** The field name of the speakService field. */ + public static final String SPEAK_SERVICE = "speakService"; + /** * Tracks the oid of the body objects of all of the occupants of this * place. @@ -26,6 +33,9 @@ public class PlaceObject extends DObject */ public DSet occupantInfo = new DSet(); + /** Used to generate speak requests on this place object. */ + public SpeakMarshaller speakService; + /** * Requests that the specified oid be added to the * occupants oid list. The list will not change until the @@ -91,4 +101,18 @@ public class PlaceObject extends DObject this.occupantInfo = occupantInfo; requestAttributeChange(OCCUPANT_INFO, occupantInfo); } + + /** + * Requests that the speakService 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); + } } diff --git a/src/java/com/threerings/crowd/server/CrowdServer.java b/src/java/com/threerings/crowd/server/CrowdServer.java index 66b21bd6b..c141082ea 100644 --- a/src/java/com/threerings/crowd/server/CrowdServer.java +++ b/src/java/com/threerings/crowd/server/CrowdServer.java @@ -1,5 +1,5 @@ // -// $Id: CrowdServer.java,v 1.12 2002/04/18 00:44:50 shaper Exp $ +// $Id: CrowdServer.java,v 1.13 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.server; @@ -8,6 +8,7 @@ import java.util.Iterator; import com.threerings.presents.server.PresentsServer; import com.threerings.crowd.Log; +import com.threerings.crowd.chat.ChatProvider; import com.threerings.crowd.data.BodyObject; /** @@ -40,10 +41,13 @@ public class CrowdServer extends PresentsServer // create our place registry plreg = new PlaceRegistry(invmgr, omgr); - // register our invocation service providers - String[] providers = null; - providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers); - registerProviders(providers); + // initialize the chat services + ChatProvider.init(invmgr, omgr); + +// // register our invocation service providers +// String[] providers = null; +// providers = CrowdConfig.config.getValue(PROVIDERS_KEY, providers); +// registerProviders(providers); Log.info("Crowd server initialized."); } diff --git a/src/java/com/threerings/crowd/server/LocationDispatcher.java b/src/java/com/threerings/crowd/server/LocationDispatcher.java new file mode 100644 index 000000000..28c1eb917 --- /dev/null +++ b/src/java/com/threerings/crowd/server/LocationDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/crowd/server/LocationProvider.java b/src/java/com/threerings/crowd/server/LocationProvider.java index 93ced9f62..be6005240 100644 --- a/src/java/com/threerings/crowd/server/LocationProvider.java +++ b/src/java/com/threerings/crowd/server/LocationProvider.java @@ -1,16 +1,19 @@ // -// $Id: LocationProvider.java,v 1.15 2002/06/20 22:38:58 mdb Exp $ +// $Id: LocationProvider.java,v 1.16 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.server; -import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.RootDObjectManager; +import com.threerings.presents.data.ClientObject; + +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.Log; +import com.threerings.crowd.client.LocationService; + import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.LocationCodes; import com.threerings.crowd.data.OccupantInfo; @@ -20,42 +23,38 @@ import com.threerings.crowd.data.PlaceObject; /** * This class provides the server end of the location services. */ -public class LocationProvider extends InvocationProvider - implements LocationCodes +public class LocationProvider + implements LocationCodes, InvocationProvider { /** - * Constructs a location provider and registers it with the invocation - * manager to handle location services. This is done automatically by - * the {@link PlaceRegistry}. + * Creates a location provider and prepares it for operation. */ - public static void init ( - InvocationManager invmgr, RootDObjectManager omgr, PlaceRegistry plreg) + public LocationProvider (InvocationManager invmgr, RootDObjectManager omgr, + PlaceRegistry plreg) { // we'll need these later _invmgr = invmgr; _omgr = omgr; _plreg = plreg; - - // register a location provider instance - invmgr.registerProvider(MODULE_NAME, new LocationProvider()); } /** - * Processes a request from a client to move to a new place. + * Requests that this client's body be moved to the specified + * location. + * + * @param caller the client object of the client that invoked this + * remotely callable method. + * @param placeId the object id of the place object to which the body + * should be moved. + * @param listener the listener that will be informed of success or + * failure. */ - public void handleMoveToRequest ( - BodyObject source, int invid, int placeId) + public void moveTo (ClientObject caller, int placeId, + LocationService.MoveListener listener) + throws InvocationException { - try { - // do the move - PlaceConfig config = moveTo(source, placeId); - // and send the response - sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, config); - - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, - sfe.getMessage()); - } + // do the move and send the response + listener.moveSucceeded(moveTo((BodyObject)caller, placeId)); } /** @@ -68,8 +67,8 @@ public class LocationProvider extends InvocationProvider * successful for some reason (which will be communicated as an error * code in the exception's message data). */ - public static PlaceConfig moveTo (BodyObject source, int placeId) - throws ServiceFailedException + public PlaceConfig moveTo (BodyObject source, int placeId) + throws InvocationException { int bodoid = source.getOid(); @@ -78,7 +77,7 @@ public class LocationProvider extends InvocationProvider if (pmgr == null) { Log.info("Requested to move to non-existent place " + "[source=" + source + ", place=" + placeId + "]."); - throw new ServiceFailedException(NO_SUCH_PLACE); + throw new InvocationException(NO_SUCH_PLACE); } // if they're already in the location they're asking to move to, @@ -97,26 +96,17 @@ public class LocationProvider extends InvocationProvider if (!source.acquireLock("moveToLock")) { // if we're still locked, a previous moveTo request hasn't // been fully processed - throw new ServiceFailedException(MOVE_IN_PROGRESS); + throw new InvocationException(MOVE_IN_PROGRESS); } PlaceObject place = pmgr.getPlaceObject(); try { + place.startTransaction(); source.startTransaction(); // remove them from any previous location leaveOccupiedPlace(source); - // set the body's new location - source.setLocation(place.getOid()); - - } finally { - source.commitTransaction(); - } - - try { - place.startTransaction(); - // generate a new occupant info record and add it to the // target location OccupantInfo info = pmgr.buildOccupantInfo(source); @@ -124,11 +114,15 @@ public class LocationProvider extends InvocationProvider place.addToOccupantInfo(info); } + // set the body's new location + source.setLocation(place.getOid()); + // add the body oid to the place object's occupant list place.addToOccupants(bodoid); } finally { place.commitTransaction(); + source.commitTransaction(); } } finally { @@ -144,7 +138,7 @@ public class LocationProvider extends InvocationProvider * Removes the specified body from the place object they currently * occupy. Does nothing if the body is not currently in a place. */ - public static void leaveOccupiedPlace (BodyObject source) + public void leaveOccupiedPlace (BodyObject source) { int oldloc = source.location; int bodoid = source.getOid(); @@ -200,18 +194,16 @@ public class LocationProvider extends InvocationProvider // first remove them from their old place leaveOccupiedPlace(source); - // then send a move notification - _invmgr.sendNotification( - source.getOid(), MODULE_NAME, MOVE_NOTIFICATION, - new Object[] { new Integer(placeId) }); + // then send a forced move notification + LocationSender.forcedMove(source, placeId); } /** The invocation manager with which we interoperate. */ - protected static InvocationManager _invmgr; + protected InvocationManager _invmgr; /** The distributed object manager with which we interoperate. */ - protected static RootDObjectManager _omgr; + protected RootDObjectManager _omgr; /** The place registry with which we interoperate. */ - protected static PlaceRegistry _plreg; + protected PlaceRegistry _plreg; } diff --git a/src/java/com/threerings/crowd/server/LocationSender.java b/src/java/com/threerings/crowd/server/LocationSender.java new file mode 100644 index 000000000..97e94d719 --- /dev/null +++ b/src/java/com/threerings/crowd/server/LocationSender.java @@ -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. +} diff --git a/src/java/com/threerings/crowd/server/PlaceManager.java b/src/java/com/threerings/crowd/server/PlaceManager.java index ca17ff463..75fb599f0 100644 --- a/src/java/com/threerings/crowd/server/PlaceManager.java +++ b/src/java/com/threerings/crowd/server/PlaceManager.java @@ -1,5 +1,5 @@ // -// $Id: PlaceManager.java,v 1.31 2002/06/20 22:12:22 mdb Exp $ +// $Id: PlaceManager.java,v 1.32 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.server; @@ -7,6 +7,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Properties; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationManager; + +import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.MessageEvent; import com.threerings.presents.dobj.MessageListener; @@ -22,6 +26,10 @@ import com.threerings.crowd.data.OccupantInfo; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; +import com.threerings.crowd.chat.SpeakDispatcher; +import com.threerings.crowd.chat.SpeakMarshaller; +import com.threerings.crowd.chat.SpeakProvider; + /** * The place manager is the server-side entity that handles all * place-related interaction. It subscribes to the place object and reacts @@ -42,7 +50,8 @@ import com.threerings.crowd.data.PlaceObject; * listeners. */ public class PlaceManager - implements MessageListener, OidListListener, ObjectDeathListener + implements MessageListener, OidListListener, ObjectDeathListener, + SpeakProvider.SpeakerValidator { /** * An interface used to allow the registration of standard message @@ -99,11 +108,13 @@ public class PlaceManager * Called by the place registry after creating this place manager. */ public void init ( - PlaceRegistry registry, PlaceConfig config, DObjectManager omgr) + PlaceRegistry registry, InvocationManager invmgr, + DObjectManager omgr, PlaceConfig config) { _registry = registry; - _config = config; + _invmgr = invmgr; _omgr = omgr; + _config = config; // let derived classes do initialization stuff didInit(); @@ -135,6 +146,13 @@ public class PlaceManager // keep track of this _plobj = plobj; + // create and register a speaker service instance that clients can + // use to speak in this place + SpeakMarshaller speakService = + (SpeakMarshaller)_invmgr.registerDispatcher( + new SpeakDispatcher(new SpeakProvider(_plobj, this)), false); + plobj.setSpeakService(speakService); + // we'll need to hear about place object events plobj.addListener(this); @@ -379,6 +397,13 @@ public class PlaceManager didShutdown(); } + // documentation inherited from interface + public boolean isValidSpeaker (DObject speakObj, ClientObject speaker) + { + // only allow people in the room to speak + return _plobj.occupants.contains(speaker.getOid()); + } + /** * Generates a string representation of this manager. Does so in a way * that makes it easier for derived classes to add to the string @@ -438,6 +463,13 @@ public class PlaceManager } } + /** A reference to the place registry with which we're registered. */ + protected PlaceRegistry _registry; + + /** The invocation manager with whom we register our game invocation + * services. */ + protected InvocationManager _invmgr; + /** A distributed object manager for doing dobj stuff. */ protected DObjectManager _omgr; @@ -447,9 +479,6 @@ public class PlaceManager /** A reference to the configuration for our place. */ protected PlaceConfig _config; - /** A reference to the place registry with which we're registered. */ - protected PlaceRegistry _registry; - /** Message handlers are used to process message events. */ protected HashMap _msghandlers; diff --git a/src/java/com/threerings/crowd/server/PlaceRegistry.java b/src/java/com/threerings/crowd/server/PlaceRegistry.java index ef8268d25..e6e814567 100644 --- a/src/java/com/threerings/crowd/server/PlaceRegistry.java +++ b/src/java/com/threerings/crowd/server/PlaceRegistry.java @@ -1,5 +1,5 @@ // -// $Id: PlaceRegistry.java,v 1.20 2002/04/15 16:28:01 shaper Exp $ +// $Id: PlaceRegistry.java,v 1.21 2002/08/14 19:07:49 mdb Exp $ package com.threerings.crowd.server; @@ -43,17 +43,23 @@ public class PlaceRegistry public void placeCreated (PlaceObject place, PlaceManager pmgr); } + /** The location provider used by the place registry to provide + * location-related invocation services. */ + public LocationProvider locprov; + /** * Creates and initializes the place registry; called by the server * during its initialization phase. */ public PlaceRegistry (InvocationManager invmgr, RootDObjectManager omgr) { - // we'll need this later - _omgr = omgr; + // create and register our location provider + locprov = new LocationProvider(invmgr, omgr, this); + invmgr.registerDispatcher(new LocationDispatcher(locprov), true); - // register the location provider - LocationProvider.init(invmgr, omgr, this); + // we'll need these later + _omgr = omgr; + _invmgr = invmgr; } /** @@ -86,7 +92,7 @@ public class PlaceRegistry // create a place manager for this place PlaceManager pmgr = (PlaceManager)pmgrClass.newInstance(); // let the pmgr know about us and its configuration - pmgr.init(this, config, _omgr); + pmgr.init(this, _invmgr, _omgr, config); // stick the manager on the creation queue because we know // we'll get our calls to objectAvailable()/requestFailed() in @@ -221,6 +227,9 @@ public class PlaceRegistry } } + /** The invocation manager with which we operate. */ + protected InvocationManager _invmgr; + /** The distributed object manager with which we operate. */ protected RootDObjectManager _omgr; diff --git a/src/java/com/threerings/micasa/client/MiCasaApplet.java b/src/java/com/threerings/micasa/client/MiCasaApplet.java index 2e74b380a..eeacf8e61 100644 --- a/src/java/com/threerings/micasa/client/MiCasaApplet.java +++ b/src/java/com/threerings/micasa/client/MiCasaApplet.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaApplet.java,v 1.6 2002/02/09 20:47:11 mdb Exp $ +// $Id: MiCasaApplet.java,v 1.7 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.client; @@ -93,7 +93,7 @@ public class MiCasaApplet extends Applet // hide the frame and log off _frame.setVisible(false); Client client = _client.getContext().getClient(); - if (client.loggedOn()) { + if (client.isLoggedOn()) { client.logoff(false); } } diff --git a/src/java/com/threerings/micasa/client/MiCasaClient.java b/src/java/com/threerings/micasa/client/MiCasaClient.java index 71eaff2e3..769cdfe3d 100644 --- a/src/java/com/threerings/micasa/client/MiCasaClient.java +++ b/src/java/com/threerings/micasa/client/MiCasaClient.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaClient.java,v 1.14 2002/07/12 17:01:28 mdb Exp $ +// $Id: MiCasaClient.java,v 1.15 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.client; @@ -55,7 +55,7 @@ public class MiCasaClient _frame.addWindowListener(new WindowAdapter() { public void windowClosing (WindowEvent evt) { // if we're logged on, log off - if (_client.loggedOn()) { + if (_client.isLoggedOn()) { _client.logoff(true); } else { // otherwise get the heck out diff --git a/src/java/com/threerings/micasa/lobby/LobbyCodes.java b/src/java/com/threerings/micasa/lobby/LobbyCodes.java deleted file mode 100644 index 19c2d0a84..000000000 --- a/src/java/com/threerings/micasa/lobby/LobbyCodes.java +++ /dev/null @@ -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 - * handleGotCategories. */ - 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 - * handleGotLobbies. */ - 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 - * handleRequestFailed. */ - public static final String REQUEST_FAILED_RESPONSE = "RequestFailed"; -} diff --git a/src/java/com/threerings/micasa/lobby/LobbyController.java b/src/java/com/threerings/micasa/lobby/LobbyController.java index 188772138..a391b94da 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyController.java +++ b/src/java/com/threerings/micasa/lobby/LobbyController.java @@ -1,5 +1,5 @@ // -// $Id: LobbyController.java,v 1.8 2001/10/25 23:42:33 mdb Exp $ +// $Id: LobbyController.java,v 1.9 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; @@ -15,8 +15,7 @@ import com.threerings.parlor.game.GameConfig; import com.threerings.micasa.Log; import com.threerings.micasa.util.MiCasaContext; -public class LobbyController - extends PlaceController +public class LobbyController extends PlaceController implements InvitationHandler, InvitationResponseObserver { public void init (CrowdContext ctx, PlaceConfig config) @@ -61,35 +60,38 @@ public class LobbyController } } - public void invitationReceived (int inviteId, String inviter, - GameConfig config) + // documentation inherited from interface + public void invitationReceived (Invitation invite) { - Log.info("Invitation received [inviteId=" + inviteId + - ", inviter=" + inviter + ", config=" + config + "]."); + Log.info("Invitation received [invite=" + invite + "]."); // accept the invitation. we're game... - _ctx.getParlorDirector().accept(inviteId); + invite.accept(); } - public void invitationCancelled (int inviteId) + // documentation inherited from interface + public void invitationCancelled (Invitation invite) { - Log.info("Invitation cancelled [inviteId=" + inviteId + "]."); + Log.info("Invitation cancelled " + invite + "."); } - public void invitationAccepted (int inviteId) + // documentation inherited from interface + public void invitationAccepted (Invitation invite) { - Log.info("Invitation accepted [inviteId=" + inviteId + "]."); + Log.info("Invitation accepted " + invite + "."); } - public void invitationRefused (int inviteId, String message) + // documentation inherited from interface + public void invitationRefused (Invitation invite, String message) { - Log.info("Invitation refused [inviteId=" + inviteId + + Log.info("Invitation refused [invite=" + invite + ", message=" + message + "]."); } - public void invitationCountered (int inviteId, GameConfig config) + // documentation inherited from interface + public void invitationCountered (Invitation invite, GameConfig config) { - Log.info("Invitation countered [inviteId=" + inviteId + + Log.info("Invitation countered [invite=" + invite + ", config=" + config + "]."); } diff --git a/src/java/com/threerings/micasa/lobby/LobbyDispatcher.java b/src/java/com/threerings/micasa/lobby/LobbyDispatcher.java new file mode 100644 index 000000000..27c07a3ce --- /dev/null +++ b/src/java/com/threerings/micasa/lobby/LobbyDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/micasa/lobby/LobbyManager.java b/src/java/com/threerings/micasa/lobby/LobbyManager.java index be3cafce7..617cd476f 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyManager.java +++ b/src/java/com/threerings/micasa/lobby/LobbyManager.java @@ -1,13 +1,12 @@ // -// $Id: LobbyManager.java,v 1.4 2001/10/11 04:13:33 mdb Exp $ +// $Id: LobbyManager.java,v 1.5 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; import java.util.Properties; import com.samskivert.util.StringUtil; -import com.threerings.crowd.chat.ChatService; -import com.threerings.crowd.chat.ChatMessageHandler; +import com.threerings.crowd.chat.ChatCodes; import com.threerings.crowd.server.PlaceManager; import com.threerings.micasa.Log; @@ -61,11 +60,6 @@ public class LobbyManager extends PlaceManager // let the lobby registry know that we're up and running _lobreg.lobbyReady(_plobj.getOid(), _gameIdent, _name); - - // register a chat message handler because we want to support - // chatting - MessageHandler handler = new ChatMessageHandler(); - registerMessageHandler(ChatService.SPEAK_REQUEST, handler); } /** The universal game identifier for the game matchmade by this diff --git a/src/java/com/threerings/micasa/lobby/LobbyMarshaller.java b/src/java/com/threerings/micasa/lobby/LobbyMarshaller.java new file mode 100644 index 000000000..36e7eef6a --- /dev/null +++ b/src/java/com/threerings/micasa/lobby/LobbyMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/micasa/lobby/LobbyProvider.java b/src/java/com/threerings/micasa/lobby/LobbyProvider.java index 85afe6ad8..4f125e54e 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyProvider.java +++ b/src/java/com/threerings/micasa/lobby/LobbyProvider.java @@ -1,57 +1,31 @@ // -// $Id: LobbyProvider.java,v 1.4 2002/07/23 05:54:52 mdb Exp $ +// $Id: LobbyProvider.java,v 1.5 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; -import com.threerings.crowd.data.BodyObject; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.InvocationProvider; -import com.threerings.util.StreamableArrayList; + +import com.threerings.micasa.lobby.LobbyService.CategoriesListener; +import com.threerings.micasa.lobby.LobbyService.LobbiesListener; /** - * Handles server side of lobby-related invocation services. + * Provides access to the server-side implementation of the lobby + * services. */ -public class LobbyProvider - extends InvocationProvider implements LobbyCodes +public interface LobbyProvider extends InvocationProvider { - /** - * Constructs a lobby provider instance which will be used to handle - * all lobby-related invocation service requests. This is - * automatically taken care of by the lobby registry, so no other - * entity need instantiate and register a lobby provider. - * - * @param lobreq a reference to the lobby registry active in this - * server. - */ - public LobbyProvider (LobbyRegistry lobreg) - { - _lobreg = lobreg; - } - /** * Processes a request by the client to obtain a list of the lobby * categories available on this server. */ - public void handleGetCategoriesRequest ( - BodyObject source, int invid) - { - String[] cats = _lobreg.getCategories(source); - // we have to cast the array to an object to avoid having the - // compiler pick the version of sendResponse that takes Object[] - sendResponse(source, invid, GOT_CATEGORIES_RESPONSE, (Object)cats); - } + public void getCategories (ClientObject caller, + CategoriesListener listener); /** * Processes a request by the client to obtain a list of lobbies * matching the supplied category string. */ - public void handleGetLobbiesRequest ( - BodyObject source, int invid, String category) - { - StreamableArrayList list = new StreamableArrayList(); - _lobreg.getLobbies(source, category, list); - sendResponse(source, invid, GOT_LOBBIES_RESPONSE, list); - } - - /** A reference to the lobby registry. */ - protected LobbyRegistry _lobreg; + public void getLobbies (ClientObject caller, String category, + LobbiesListener listener); } diff --git a/src/java/com/threerings/micasa/lobby/LobbyRegistry.java b/src/java/com/threerings/micasa/lobby/LobbyRegistry.java index 62bb56fd1..91a2016c0 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyRegistry.java +++ b/src/java/com/threerings/micasa/lobby/LobbyRegistry.java @@ -1,15 +1,20 @@ // -// $Id: LobbyRegistry.java,v 1.9 2002/05/21 04:46:44 mdb Exp $ +// $Id: LobbyRegistry.java,v 1.10 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; import java.util.*; import com.samskivert.util.*; +import com.threerings.util.StreamableArrayList; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.InvocationManager; + import com.threerings.crowd.data.BodyObject; import com.threerings.micasa.Log; +import com.threerings.micasa.lobby.LobbyService.CategoriesListener; +import com.threerings.micasa.lobby.LobbyService.LobbiesListener; import com.threerings.micasa.server.MiCasaConfig; import com.threerings.micasa.server.MiCasaServer; @@ -64,7 +69,8 @@ import com.threerings.micasa.server.MiCasaServer; * sense, the client will be able to use them to search for lobbies * containing games of similar types using the provided facilities. */ -public class LobbyRegistry implements LobbyCodes +public class LobbyRegistry + implements LobbyProvider { /** * Initializes the registry. It will use the supplied configuration @@ -75,9 +81,8 @@ public class LobbyRegistry implements LobbyCodes */ public void init (InvocationManager invmgr) { - // register our invocation service handler - LobbyProvider provider = new LobbyProvider(this); - invmgr.registerProvider(MODULE_NAME, provider); + // register ourselves as an invocation service handler + invmgr.registerDispatcher(new LobbyDispatcher(this), true); // create our lobby managers String[] lmgrs = null; @@ -178,6 +183,30 @@ public class LobbyRegistry implements LobbyCodes return cats; } + /** + * Processes a request by the client to obtain a list of the lobby + * categories available on this server. + */ + public void getCategories (ClientObject caller, CategoriesListener listener) + { + listener.gotCategories(getCategories((BodyObject)caller)); + } + + /** + * Processes a request by the client to obtain a list of lobbies + * matching the supplied category string. + */ + public void getLobbies (ClientObject caller, String category, + LobbiesListener listener) + { + StreamableArrayList target = new StreamableArrayList(); + ArrayList list = (ArrayList)_lobbies.get(category); + if (list != null) { + target.addAll(list); + } + listener.gotLobbies(target); + } + /** * Called by our lobby managers once they have started up and are * ready to do their lobby duties. diff --git a/src/java/com/threerings/micasa/lobby/LobbySelector.java b/src/java/com/threerings/micasa/lobby/LobbySelector.java index 7a0f3be1b..95a2ee33c 100644 --- a/src/java/com/threerings/micasa/lobby/LobbySelector.java +++ b/src/java/com/threerings/micasa/lobby/LobbySelector.java @@ -1,5 +1,5 @@ // -// $Id: LobbySelector.java,v 1.4 2001/12/14 16:07:30 shaper Exp $ +// $Id: LobbySelector.java,v 1.5 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; @@ -18,8 +18,6 @@ import com.samskivert.util.StringUtil; import com.threerings.crowd.data.PlaceObject; import com.threerings.micasa.Log; -import com.threerings.micasa.lobby.Lobby; -import com.threerings.micasa.lobby.LobbyService; import com.threerings.micasa.util.MiCasaContext; /** @@ -28,8 +26,9 @@ import com.threerings.micasa.util.MiCasaContext; * displays a list of the lobbies that are available in that category. If * a lobby is double-clicked, it moves the client into that lobby room. */ -public class LobbySelector - extends JPanel implements ActionListener +public class LobbySelector extends JPanel + implements ActionListener, LobbyService.CategoriesListener, + LobbyService.LobbiesListener { /** * Constructs a new lobby selector component. It will wait until it is @@ -64,16 +63,18 @@ public class LobbySelector }; _loblist.addMouseListener(ml); add(_loblist, BorderLayout.CENTER); + } - // we'll need to know when we are made visible because we'll want - // to issue a request for our categories when that happens - addAncestorListener(new AncestorAdapter () { - public void ancestorAdded (AncestorEvent evt) { - // issue a get categories request to get the category list - LobbyService.getCategories( - _ctx.getClient(), LobbySelector.this); - } - }); + // documentation inherited + public void addNotify () + { + super.addNotify(); + + // get a handle on our lobby service instance + _lservice = (LobbyService) + _ctx.getClient().requireService(LobbyService.class); + // and use them to look up the lobby categories + _lservice.getCategories(_ctx.getClient(), this); } /** @@ -90,11 +91,8 @@ public class LobbySelector } } - /** - * Called in response to a {@link LobbyService#getCategories} - * invocation service request. - */ - public void handleGotCategories (int invid, String[] categories) + // documentation inherited from interface + public void gotCategories (String[] categories) { // append these to our "unselected" item for (int i = 0; i < categories.length; i++) { @@ -102,11 +100,8 @@ public class LobbySelector } } - /** - * Called in response to a {@link LobbyService#getLobbies} invocation - * service request. - */ - public void handleGotLobbies (int invid, List lobbies) + // documentation inherited from interface + public void gotLobbies (List lobbies) { // create a list model for this category DefaultListModel model = new DefaultListModel(); @@ -130,16 +125,13 @@ public class LobbySelector _pendingCategory = null; } - /** - * Called in response to a lobby invocation request when the request - * failed. - */ - public void handleRequestFailed (int invid, String reason) + // documentation inherited from interface + public void requestFailed (String reason) { - Log.info("Request failed [invid=" + invid + - ", reason=" + reason + "]."); + Log.info("Request failed [reason=" + reason + "]."); - // clear out our pending category indicator + // clear out our pending category indicator in case this was a + // failed getLobbies() request _pendingCategory = null; } @@ -158,7 +150,7 @@ public class LobbySelector // make a note that we're loading up this category _pendingCategory = category; // issue a request to load up the lobbies in this category - LobbyService.getLobbies(_ctx.getClient(), category, this); + _lservice.getLobbies(_ctx.getClient(), category, this); } else { Log.info("Ignoring category select request because " + @@ -201,6 +193,8 @@ public class LobbySelector } protected MiCasaContext _ctx; + protected LobbyService _lservice; + protected JComboBox _combo; protected JList _loblist; diff --git a/src/java/com/threerings/micasa/lobby/LobbyService.java b/src/java/com/threerings/micasa/lobby/LobbyService.java index 2e28e61e7..46a0871e6 100644 --- a/src/java/com/threerings/micasa/lobby/LobbyService.java +++ b/src/java/com/threerings/micasa/lobby/LobbyService.java @@ -1,40 +1,54 @@ // -// $Id: LobbyService.java,v 1.3 2002/05/15 23:54:34 mdb Exp $ +// $Id: LobbyService.java,v 1.4 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.lobby; -import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import java.util.List; -import com.threerings.micasa.Log; +import com.threerings.presents.client.Client; +import com.threerings.presents.client.InvocationService; /** - * This class provides an interface to the various parlor services that - * are directly invokable by the client (by means of the invocation - * services). + * Provides an interface to the various parlor services that are directly + * invokable by the client (by means of the invocation services). */ -public class LobbyService - implements LobbyCodes +public interface LobbyService extends InvocationService { + /** + * Used to communicate the results of a {@link #getCategories} + * request. + */ + public static interface CategoriesListener extends InvocationListener + { + /** + * Supplies the listener with the results of a {@link + * #getCategories} request. + */ + public void gotCategories (String[] categories); + } + + /** + * Used to communicate the results of a {@link #getLobbies} + * request. + */ + public static interface LobbiesListener extends InvocationListener + { + /** + * Supplies the listener with the results of a {@link + * #getLobbies} request. + */ + public void gotLobbies (List lobbies); + } + /** * Requests the list of lobby cateogories that are available on this * server. * * @param client a connected, operational client instance. - * @param rsptarget the object reference that will receive and process - * the response. The response will come in the form of a method call - * to handleGotCategories or - * handleRequestFailed. - * - * @return the invocation request id of the generated request. + * @param listener the listener that will receive and process the + * response. */ - public static int getCategories (Client client, Object rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Log.debug("Sending get categories."); - return invdir.invoke( - MODULE_NAME, GET_CATEGORIES_REQUEST, null, rsptarget); - } + public void getCategories (Client client, CategoriesListener listener); /** * Requests information on all active lobbies that match the specified @@ -43,20 +57,9 @@ public class LobbyService * @param client a connected, operational client instance. * @param category the category of game for which a list of lobbies is * desired. - * @param rsptarget the object reference that will receive and process - * the response. The response will come in the form of a method call - * to handleLobbyList or - * handleRequestFailed. - * - * @return the invocation request id of the generated request. + * @param listener the listener that will receive and process the + * response. */ - public static int getLobbies ( - Client client, String category, Object rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { category }; - Log.debug("Sending get lobbies [category=" + category + "]."); - return invdir.invoke( - MODULE_NAME, GET_LOBBIES_REQUEST, args, rsptarget); - } + public void getLobbies (Client client, String category, + LobbiesListener listener); } diff --git a/src/java/com/threerings/micasa/server/MiCasaServer.java b/src/java/com/threerings/micasa/server/MiCasaServer.java index 839ce8db4..561e0fe21 100644 --- a/src/java/com/threerings/micasa/server/MiCasaServer.java +++ b/src/java/com/threerings/micasa/server/MiCasaServer.java @@ -1,5 +1,5 @@ // -// $Id: MiCasaServer.java,v 1.5 2002/03/28 22:32:32 mdb Exp $ +// $Id: MiCasaServer.java,v 1.6 2002/08/14 19:07:49 mdb Exp $ package com.threerings.micasa.server; @@ -40,7 +40,7 @@ public class MiCasaServer extends CrowdServer clmgr.setClientClass(MiCasaClient.class); // initialize our parlor manager - parmgr.init(invmgr); + parmgr.init(invmgr, plreg); // initialize the lobby registry lobreg.init(invmgr); diff --git a/src/java/com/threerings/micasa/simulator/client/ClientController.java b/src/java/com/threerings/micasa/simulator/client/ClientController.java index 7749d88ac..8dc3ac50d 100644 --- a/src/java/com/threerings/micasa/simulator/client/ClientController.java +++ b/src/java/com/threerings/micasa/simulator/client/ClientController.java @@ -1,5 +1,5 @@ // -// $Id: ClientController.java,v 1.6 2002/05/17 21:22:46 shaper Exp $ +// $Id: ClientController.java,v 1.7 2002/08/14 19:07:50 mdb Exp $ package com.threerings.micasa.simulator.client; @@ -73,8 +73,11 @@ public class ClientController extends Controller config = (GameConfig) Class.forName(_info.gameConfigClass).newInstance(); - // send the game creation request - SimulatorDirector.createGame( + // get the simulator service and use it to request that our + // game be created + SimulatorService sservice = (SimulatorService) + client.requireService(SimulatorService.class); + sservice.createGame( client, config, _info.simClass, _info.playerCount); // our work here is done, as the location manager will move us diff --git a/src/java/com/threerings/micasa/simulator/client/SimpleClient.java b/src/java/com/threerings/micasa/simulator/client/SimpleClient.java index 3299173bf..a0bed4ba9 100644 --- a/src/java/com/threerings/micasa/simulator/client/SimpleClient.java +++ b/src/java/com/threerings/micasa/simulator/client/SimpleClient.java @@ -1,5 +1,5 @@ // -// $Id: SimpleClient.java,v 1.6 2002/07/15 03:09:29 mdb Exp $ +// $Id: SimpleClient.java,v 1.7 2002/08/14 19:07:50 mdb Exp $ package com.threerings.micasa.simulator.client; @@ -53,7 +53,7 @@ public class SimpleClient _frame.getFrame().addWindowListener(new WindowAdapter() { public void windowClosing (WindowEvent evt) { // if we're logged on, log off - if (_client.loggedOn()) { + if (_client.isLoggedOn()) { _client.logoff(true); } } diff --git a/src/java/com/threerings/micasa/simulator/client/SimulatorDirector.java b/src/java/com/threerings/micasa/simulator/client/SimulatorDirector.java deleted file mode 100644 index 729b1ae8a..000000000 --- a/src/java/com/threerings/micasa/simulator/client/SimulatorDirector.java +++ /dev/null @@ -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); - } -} diff --git a/src/java/com/threerings/micasa/simulator/client/SimulatorService.java b/src/java/com/threerings/micasa/simulator/client/SimulatorService.java new file mode 100644 index 000000000..c2b1a29dd --- /dev/null +++ b/src/java/com/threerings/micasa/simulator/client/SimulatorService.java @@ -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); +} diff --git a/src/java/com/threerings/micasa/simulator/data/SimulatorCodes.java b/src/java/com/threerings/micasa/simulator/data/SimulatorCodes.java deleted file mode 100644 index b3db42c87..000000000 --- a/src/java/com/threerings/micasa/simulator/data/SimulatorCodes.java +++ /dev/null @@ -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"; -} - diff --git a/src/java/com/threerings/micasa/simulator/data/SimulatorMarshaller.java b/src/java/com/threerings/micasa/simulator/data/SimulatorMarshaller.java new file mode 100644 index 000000000..7ac28dfc5 --- /dev/null +++ b/src/java/com/threerings/micasa/simulator/data/SimulatorMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/micasa/simulator/server/Simulant.java b/src/java/com/threerings/micasa/simulator/server/Simulant.java index 0db4e7d5a..34668dc4d 100644 --- a/src/java/com/threerings/micasa/simulator/server/Simulant.java +++ b/src/java/com/threerings/micasa/simulator/server/Simulant.java @@ -1,7 +1,7 @@ // -// $Id: Simulant.java,v 1.3 2002/02/05 22:11:51 mdb Exp $ +// $Id: Simulant.java,v 1.4 2002/08/14 19:07:51 mdb Exp $ -package com.threerings.micasa.simulator.client; +package com.threerings.micasa.simulator.server; import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.MessageEvent; @@ -9,19 +9,21 @@ import com.threerings.presents.dobj.MessageEvent; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceObject; -import com.threerings.parlor.game.GameCodes; import com.threerings.parlor.game.GameConfig; +import com.threerings.parlor.game.GameManager; -public abstract class Simulant implements GameCodes +public abstract class Simulant { /** * Initializes the simulant with a body object and the game config for * the game they'll be engaged in. */ - public void init (BodyObject self, GameConfig config, DObjectManager omgr) + public void init (BodyObject self, GameConfig config, + GameManager gmgr, DObjectManager omgr) { _self = self; _config = config; + _gmgr = gmgr; _omgr = omgr; } @@ -37,9 +39,7 @@ public abstract class Simulant implements GameCodes public void willEnterPlace (PlaceObject plobj) { // let the game manager know that the simulant's ready - MessageEvent mevt = new MessageEvent( - plobj.getOid(), PLAYER_READY_NOTIFICATION, null); - postEvent(mevt); + _gmgr.playerReady(_self); } /** @@ -59,6 +59,9 @@ public abstract class Simulant implements GameCodes /** The game config object. */ protected GameConfig _config; + /** The game manager for the game we're playing. */ + protected GameManager _gmgr; + /** Our body object. */ protected BodyObject _self; diff --git a/src/java/com/threerings/micasa/simulator/server/SimulatorDispatcher.java b/src/java/com/threerings/micasa/simulator/server/SimulatorDispatcher.java new file mode 100644 index 000000000..2d88d44aa --- /dev/null +++ b/src/java/com/threerings/micasa/simulator/server/SimulatorDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/micasa/simulator/server/SimulatorManager.java b/src/java/com/threerings/micasa/simulator/server/SimulatorManager.java index 06071999e..bbfeaa2d7 100644 --- a/src/java/com/threerings/micasa/simulator/server/SimulatorManager.java +++ b/src/java/com/threerings/micasa/simulator/server/SimulatorManager.java @@ -1,5 +1,5 @@ // -// $Id: SimulatorManager.java,v 1.11 2002/07/02 21:14:23 shaper Exp $ +// $Id: SimulatorManager.java,v 1.12 2002/08/14 19:07:51 mdb Exp $ package com.threerings.micasa.simulator.server; @@ -14,25 +14,21 @@ import com.threerings.presents.server.InvocationProvider; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceObject; -import com.threerings.crowd.server.LocationProvider; import com.threerings.crowd.server.PlaceManager; -import com.threerings.crowd.server.PlaceRegistry; import com.threerings.crowd.server.PlaceRegistry.CreationObserver; +import com.threerings.crowd.server.PlaceRegistry; import com.threerings.parlor.game.GameConfig; import com.threerings.parlor.game.GameManager; import com.threerings.parlor.game.GameObject; import com.threerings.micasa.Log; -import com.threerings.micasa.simulator.client.Simulant; -import com.threerings.micasa.simulator.data.SimulatorCodes; /** * The simulator manager is responsible for handling the simulator * services on the server side. */ public class SimulatorManager - implements SimulatorCodes { /** * Initializes the simulator manager manager. This should be called by @@ -48,7 +44,7 @@ public class SimulatorManager { // register our simulator provider SimulatorProvider sprov = new SimulatorProvider(this); - invmgr.registerProvider(MODULE_NAME, sprov); + invmgr.registerDispatcher(new SimulatorDispatcher(sprov), true); // keep these for later _plreg = plreg; @@ -172,7 +168,7 @@ public class SimulatorManager // give the simulant its body BodyObject bobj = (BodyObject)_sims.get(ii - 1); - sim.init(bobj, _config, _omgr); + sim.init(bobj, _config, _gmgr, _omgr); // give the simulant a chance to engage in place antics sim.willEnterPlace(_gobj); @@ -180,7 +176,7 @@ public class SimulatorManager // move the simulant into the game room since they have no // location director to move them automagically try { - LocationProvider.moveTo(bobj, _gobj.getOid()); + _plreg.locprov.moveTo(bobj, _gobj.getOid()); } catch (Exception e) { Log.warning("Failed to move simulant into room " + "[e=" + e + "]."); diff --git a/src/java/com/threerings/micasa/simulator/server/SimulatorProvider.java b/src/java/com/threerings/micasa/simulator/server/SimulatorProvider.java index 806b8eab4..0f81aa9dc 100644 --- a/src/java/com/threerings/micasa/simulator/server/SimulatorProvider.java +++ b/src/java/com/threerings/micasa/simulator/server/SimulatorProvider.java @@ -1,9 +1,11 @@ // -// $Id: SimulatorProvider.java,v 1.1 2001/12/19 09:32:02 shaper Exp $ +// $Id: SimulatorProvider.java,v 1.2 2002/08/14 19:07:51 mdb Exp $ package com.threerings.micasa.simulator.server; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.server.InvocationProvider; + import com.threerings.crowd.data.BodyObject; import com.threerings.parlor.game.GameConfig; @@ -14,7 +16,7 @@ import com.threerings.micasa.Log; * side, passing them off to the {@link SimulatorManager}. */ public class SimulatorProvider - extends InvocationProvider + implements InvocationProvider { /** * Constructs a simulator provider. @@ -27,15 +29,14 @@ public class SimulatorProvider /** * Processes a request from the client to create a new game. */ - public void handleCreateGameRequest ( - BodyObject source, int invid, GameConfig config, - String simClass, int playerCount) + public void createGame (ClientObject caller, GameConfig config, + String simClass, int playerCount) { - Log.info("handleCreateGameRequest [source=" + source + + Log.info("handleCreateGameRequest [caller=" + caller + ", config=" + config + ", simClass=" + simClass + ", playerCount=" + playerCount + "]."); - _simmgr.createGame(source, config, simClass, playerCount); + _simmgr.createGame((BodyObject)caller, config, simClass, playerCount); } /** The simulator manager. */ diff --git a/src/java/com/threerings/parlor/client/Invitation.java b/src/java/com/threerings/parlor/client/Invitation.java new file mode 100644 index 000000000..087ab731e --- /dev/null +++ b/src/java/com/threerings/parlor/client/Invitation.java @@ -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; +} diff --git a/src/java/com/threerings/parlor/client/InvitationHandler.java b/src/java/com/threerings/parlor/client/InvitationHandler.java index 14e3fcaa6..ee83340a1 100644 --- a/src/java/com/threerings/parlor/client/InvitationHandler.java +++ b/src/java/com/threerings/parlor/client/InvitationHandler.java @@ -1,5 +1,5 @@ // -// $Id: InvitationHandler.java,v 1.2 2001/10/11 21:08:21 mdb Exp $ +// $Id: InvitationHandler.java,v 1.3 2002/08/14 19:07:52 mdb Exp $ package com.threerings.parlor.client; @@ -16,18 +16,14 @@ public interface InvitationHandler /** * Called when an invitation is received from another player. * - * @param inviteId this invitation's unique id. - * @param inviter the username of the user that sent the invitation. - * @param config the configuration of the game to which we are being - * invited. + * @param invite the received invitation. */ - public void invitationReceived (int inviteId, String inviter, - GameConfig config); + public void invitationReceived (Invitation invite); /** * Called when an invitation is cancelled by the inviting player. * - * @param inviteId this invitation's unique id. + * @param invite the cancelled invitation. */ - public void invitationCancelled (int inviteId); + public void invitationCancelled (Invitation invite); } diff --git a/src/java/com/threerings/parlor/client/InvitationResponseObserver.java b/src/java/com/threerings/parlor/client/InvitationResponseObserver.java index 8f8df9d65..683314c9f 100644 --- a/src/java/com/threerings/parlor/client/InvitationResponseObserver.java +++ b/src/java/com/threerings/parlor/client/InvitationResponseObserver.java @@ -1,5 +1,5 @@ // -// $Id: InvitationResponseObserver.java,v 1.5 2001/10/11 21:08:21 mdb Exp $ +// $Id: InvitationResponseObserver.java,v 1.6 2002/08/14 19:07:52 mdb Exp $ package com.threerings.parlor.client; @@ -17,30 +17,27 @@ public interface InvitationResponseObserver /** * Called if the invitation was accepted. * - * @param inviteId the unique id of the invitation for which we - * received a response. + * @param invite the invitation for which we received a response. */ - public void invitationAccepted (int inviteId); + public void invitationAccepted (Invitation invite); /** * Called if the invitation was refused. * - * @param inviteId the unique id of the invitation for which we - * received a response. + * @param invite the invitation for which we received a response. * @param message a message provided by the invited user explaining * the reason for their refusal, or the empty string if no message was * provided. */ - public void invitationRefused (int inviteId, String message); + public void invitationRefused (Invitation invite, String message); /** * Called if the invitation was countered with an alternate game * configuration. * - * @param inviteId the unique id of the invitation for which we - * received a response. + * @param invite the invitation for which we received a response. * @param config the game configuration proposed by the invited * player. */ - public void invitationCountered (int inviteId, GameConfig config); + public void invitationCountered (Invitation invite, GameConfig config); } diff --git a/src/java/com/threerings/parlor/client/ParlorDecoder.java b/src/java/com/threerings/parlor/client/ParlorDecoder.java new file mode 100644 index 000000000..b7ad98c01 --- /dev/null +++ b/src/java/com/threerings/parlor/client/ParlorDecoder.java @@ -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. +} diff --git a/src/java/com/threerings/parlor/client/ParlorDirector.java b/src/java/com/threerings/parlor/client/ParlorDirector.java index 04932763f..e40c0a1a6 100644 --- a/src/java/com/threerings/parlor/client/ParlorDirector.java +++ b/src/java/com/threerings/parlor/client/ParlorDirector.java @@ -1,5 +1,5 @@ // -// $Id: ParlorDirector.java,v 1.15 2002/04/15 16:28:02 shaper Exp $ +// $Id: ParlorDirector.java,v 1.16 2002/08/14 19:07:52 mdb Exp $ package com.threerings.parlor.client; @@ -7,7 +7,8 @@ import java.util.ArrayList; import java.util.Iterator; import com.samskivert.util.HashIntMap; -import com.threerings.presents.client.InvocationReceiver; +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; import com.threerings.parlor.Log; import com.threerings.parlor.data.ParlorCodes; @@ -21,8 +22,8 @@ import com.threerings.parlor.util.ParlorContext; * that will actually create and display the user interface for the game * that started. */ -public class ParlorDirector - implements ParlorCodes, InvocationReceiver +public class ParlorDirector extends BasicDirector + implements ParlorCodes, ParlorReceiver { /** * Constructs a parlor director and provides it with the parlor @@ -35,12 +36,16 @@ public class ParlorDirector */ public ParlorDirector (ParlorContext ctx) { + super(ctx); _ctx = ctx; - // register ourselves with the invocation director as handling - // parlor notifications - _ctx.getClient().getInvocationDirector(). - registerReceiver(MODULE_NAME, this); + // register as a session observer + _ctx.getClient().addClientObserver(this); + + // register ourselves with the invocation director as a parlor + // notification receiver + _ctx.getClient().getInvocationDirector().registerReceiver( + new ParlorDecoder(this)); } /** @@ -83,135 +88,30 @@ public class ParlorDirector * @param observer the entity that will be notified if this invitation * is accepted, refused or countered. * - * @return a unique id associated with this invitation that can be - * used to discern between various outstanding invitations by the - * invitation observer. + * @return an invitation object that can be used to manage the + * outstanding invitation. */ - public int invite (String invitee, GameConfig config, - InvitationResponseObserver observer) + public Invitation invite (String invitee, GameConfig config, + InvitationResponseObserver observer) { - // generate the invocation service request - int invid = ParlorService.invite( - _ctx.getClient(), invitee, config, this); - - // create an invitation record and put it in the submitted table - Invitation invite = new Invitation(invitee, config, observer); - _submittedInvites.put(invid, invite); - return invite.inviteId; + // create the invitation record + Invitation invite = new Invitation( + _ctx, _pservice, invitee, config, observer); + // submit the invitation request to the server + _pservice.invite(_ctx.getClient(), invitee, config, invite); + // and return the invitation to the caller + return invite; } - /** - * Accept an invitation. - * - * @param inviteId the id of the invitation to accept. - */ - public void accept (int inviteId) + // documentation inherited + protected void fetchServices (Client client) { - Invitation invite = getInviteByLocalId(inviteId); - if (invite == null) { - // complain if we didn't find a matching invitation - Log.warning("Received request to accept non-existent " + - "invitation [inviteId=" + inviteId + "]."); - return; - } - - // generate the invocation service request - ParlorService.respond(_ctx.getClient(), invite.remoteId, - INVITATION_ACCEPTED, null, this); + // get a handle on our parlor services + _pservice = (ParlorService)client.requireService(ParlorService.class); } - /** - * Refuse an invitation. - * - * @param inviteId the id of the invitation to accept. - * @param message the message to deliver to the inviting user - * explaining the reason for the refusal or null if no message is to - * be provided. - */ - public void refuse (int inviteId, String message) - { - Invitation invite = getInviteByLocalId(inviteId); - if (invite == null) { - // complain if we didn't find a matching invitation - Log.warning("Received request to accept non-existent " + - "invitation [inviteId=" + inviteId + "]."); - return; - } - - // generate the invocation service request - ParlorService.respond(_ctx.getClient(), invite.remoteId, - INVITATION_REFUSED, message, this); - } - - /** - * Counters a received invitation with an invitation with different - * game configuration parameters. - * - * @param inviteId the id of the received invitation. - * @param config the updated game configuration. - * @param observer the entity that will be notified if this - * counter-invitation is accepted, refused or countered. - */ - public void counter (int inviteId, GameConfig config, - InvitationResponseObserver observer) - { - Invitation invite = getInviteByLocalId(inviteId); - if (invite == null) { - // complain if we didn't find a matching invitation - Log.warning("Received request to counter non-existent " + - "invitation [inviteId=" + inviteId + - ", config=" + config + "]."); - return; - } - - // update the invitation record with the observer (who will - // eventually be hearing back from the other client about their - // counter-invitation) - invite.observer = observer; - - // generate the invocation service request - ParlorService.respond(_ctx.getClient(), invite.remoteId, - INVITATION_COUNTERED, config, this); - } - - /** - * Issues a request to cancel an outstanding invitation. - * - * @param inviteId the id of the invitation to cancel. - */ - public void cancel (int inviteId) - { - // look up the invitation record (oh the two separate key spaces - // humanity) - Invitation invite = getInviteByLocalId(inviteId); - if (invite == null) { - // complain if we didn't find a matching invitation - Log.warning("Received request to cancel non-existent " + - "invitation [inviteId=" + inviteId + "]."); - return; - } - - // if the invitation has not yet been acknowleged by the server, - // we make a note that it should be cancelled when we do receive - // the acknowlegement - if (invite.remoteId == -1) { - invite.cancelled = true; - - } else { - // otherwise, generate the invocation service request - ParlorService.cancel(_ctx.getClient(), invite.remoteId, this); - // and remove it from the pending table - _pendingInvites.remove(invite.remoteId); - } - } - - /** - * Called by the invocation services when a game in which we are a - * player is ready to begin. - * - * @param gameOid the object id of the game object. - */ - public void handleGameReadyNotification (int gameOid) + // documentation inherited from interface + public void gameIsReady (int gameOid) { Log.info("Handling game ready [goid=" + gameOid + "]."); @@ -229,55 +129,31 @@ public class ParlorDirector } } - /** - * Called by the invocation services when another user has invited us - * to play a game. - * - * @param remoteId the unique indentifier for this invitation (used - * when countering or responding). - * @param inviter the username of the inviting user. - * @param config the configuration information for the game to which - * we've been invited. - */ - public void handleInviteNotification ( + // documentation inherited from interface + public void receivedInvite ( int remoteId, String inviter, GameConfig config) { // create an invitation record for this invitation - Invitation invite = new Invitation(inviter, config, null); - invite.remoteId = remoteId; + Invitation invite = new Invitation( + _ctx, _pservice, inviter, config, null); + invite.inviteId = remoteId; // put it in the pending invitations table _pendingInvites.put(remoteId, invite); try { // notify the invitation handler of the incoming invitation - _handler.invitationReceived(invite.inviteId, inviter, config); + _handler.invitationReceived(invite); } catch (Exception e) { Log.warning("Invitation handler choked on invite " + - "notification [inviteId=" + invite.inviteId + - ", inviter=" + inviter + - ", config=" + config + "]."); + "notification " + invite + "."); Log.logStackTrace(e); } } - /** - * Called by the invocation services when another user has responded - * to our invitation by either accepting, refusing or countering it. - * - * @param remoteId the unique indentifier for the invitation. - * @param code the response code, either {@link - * ParlorCodes#INVITATION_ACCEPTED} or {@link - * ParlorCodes#INVITATION_REFUSED} or {@link - * ParlorCodes#INVITATION_COUNTERED}. - * @param arg in the case of a refused invitation, a string - * containing a message provided by the invited user explaining the - * reason for refusal (the empty string if no explanation was - * provided). In the case of a countered invitation, a new game config - * object with the modified game configuration. - */ - public void handleRespondInviteNotification ( + // documentation inherited from interface + public void receivedInviteResponse ( int remoteId, int code, Object arg) { // look up the invitation record for this invitation @@ -286,226 +162,50 @@ public class ParlorDirector Log.warning("Have no record of invitation for which we " + "received a response?! [remoteId=" + remoteId + ", code=" + code + ", arg=" + arg + "]."); - return; - } - // make sure we have an observer to notify - if (invite.observer == null) { - Log.warning("No observer registered for invitation " + - invite + "."); - return; - } - - // notify the observer - try { - switch (code) { - case INVITATION_ACCEPTED: - invite.observer.invitationAccepted(invite.inviteId); - break; - - case INVITATION_REFUSED: - invite.observer.invitationRefused( - invite.inviteId, (String)arg); - break; - - case INVITATION_COUNTERED: - invite.observer.invitationCountered( - invite.inviteId, (GameConfig)arg); - break; - } - - } catch (Exception e) { - Log.warning("Invitation response observer choked on response " + - "[code=" + code + ", arg=" + arg + - ", invite=" + invite + "]."); - Log.logStackTrace(e); - } - - // unless the invitation was countered, we can remove it from the - // pending table because it's resolved - if (code != INVITATION_COUNTERED) { - _pendingInvites.remove(remoteId); + } else { + invite.receivedResponse(code, arg); } } - /** - * Called by the invocation services when an outstanding invitation - * has been cancelled by the inviting user. - * - * @param remoteId the unique indentifier for the invitation. - */ - public void handleCancelInviteNotification (int remoteId) + // documentation inherited from interface + public void receivedInviteCancellation (int remoteId) { // TBD } /** - * Called by the invocation services when an invitation request was - * received by the server and delivered to the intended invitee. - * - * @param invid the invocation id of the invitation request. + * Register a new invitation in our pending invitations table. The + * invitation will call this when it knows its invitation id. */ - public void handleInviteReceived (int invid, int remoteId) + protected void registerInvitation (Invitation invite) { - // remove the invitation record from the submitted table and put - // it in the pending table - Invitation invite = (Invitation)_submittedInvites.remove(invid); - if (invite == null) { - Log.warning("Received accepted notification for non-existent " + - "invitation request!? [invid=" + invid + - ", remoteId=" + remoteId + "]."); - return; - } - - // now that we know the invitation's unique id, keep track of it - invite.remoteId = remoteId; - - // if the invitation was cancelled before we heard back about it, - // we need to send off a cancellation request now - if (invite.cancelled) { - // generate the invocation service request to cancel it - ParlorService.cancel(_ctx.getClient(), invite.remoteId, this); - - } else { - // otherwise, put it in the new table - _pendingInvites.put(remoteId, invite); - } + _pendingInvites.put(invite.inviteId, invite); } /** - * Called by the invocation services when an invitation request failed - * or was rejected for some reason (this is different than an - * invitation being refused by the invitee which is handled by {@link - * #handleRespondInviteNotification}). - * - * @param invid the invocation id of the invitation request. - * @param reason a reason code explaining the failure. + * Called by an invitation when it knows it is no longer and can be + * cleared from the pending invitations table. */ - public void handleInviteFailed (int invid, String reason) + protected void clearInvitation (Invitation invite) { - Log.info("Handling invite failed [invid=" + invid + - ", reason=" + reason + "]."); - - // remove the invitation record from the submitted table and let - // the observer know that we're hosed - Invitation invite = (Invitation)_submittedInvites.remove(invid); - if (invite == null) { - Log.warning("Received failed notification for non-existent " + - "invitation request!? [invid=" + invid + - ", reason=" + reason + "]."); - return; - } - - // let the observer know what's up - try { - invite.observer.invitationRefused(invite.inviteId, reason); - } catch (Exception e) { - Log.warning("Invite observer choked on refusal notification " + - "[invite=" + invite + ", reason=" + reason + "]."); - Log.logStackTrace(e); - } - } - - /** - * The invitation class is used to track information related to - * outstanding invitations generated by or targeted to this client. - */ - protected static class Invitation - { - /** A unique id for this invitation assigned on the client which - * is only used to allow the invitation observers to discriminate - * between multiple outstanding invitations. We'd use the remote - * id here except that that is not known until we receive the - * acknowlegedment from the server and we need to provide the - * caller with some sort of unique id at the time the request is - * generated. */ - public int inviteId = _localInvitationId++; - - /** The unique id for this invitation (as assigned by the - * server). This is -1 until we receive an acknowledgement from - * the server that our invitation was delivered. */ - public int remoteId = -1; - - /** The name of the other user involved in this invitation. */ - public String opponent; - - /** The configuration of the game to be created. */ - public GameConfig config; - - /** The entity to notify when we receive a response for this - * invitation. */ - public InvitationResponseObserver observer; - - /** A flag indicating that we were requested to cancel this - * invitation before we even heard back with an acknowledgement - * that it was received by the server. */ - public boolean cancelled = false; - - /** Constructs a new invitation record. */ - public Invitation (String opponent, GameConfig config, - InvitationResponseObserver observer) - { - this.opponent = opponent; - this.config = config; - this.observer = observer; - } - - /** Returns a string representation of this invitation record. */ - public String toString () - { - return "[inviteId=" + inviteId + ", remoteId=" + remoteId + - ", opponent=" + opponent + ", config=" + config + - ", observer=" + observer + ", cancelled=" + cancelled + "]"; - } - } - - /** - * Looks up an invitation by its local unique identifier. Oh the dual - * uid space humanity! - */ - protected Invitation getInviteByLocalId (int inviteId) - { - // first search the pending invites which is where we're most - // likely to find it - Iterator iter = _pendingInvites.values().iterator(); - while (iter.hasNext()) { - Invitation match = (Invitation)iter.next(); - if (match.inviteId == inviteId) { - return match; - } - } - - // if that didn't work, look in the submitted invites - iter = _submittedInvites.values().iterator(); - while (iter.hasNext()) { - Invitation match = (Invitation)iter.next(); - if (match.inviteId == inviteId) { - return match; - } - } - - // il n'existe pas - return null; + _pendingInvites.remove(invite.inviteId); } /** An active parlor context. */ protected ParlorContext _ctx; + /** Provides access to parlor server side services. */ + protected ParlorService _pservice; + /** The entity that has registered itself to handle incoming * invitation notifications. */ protected InvitationHandler _handler; - /** A table of submitted (but not acknowledged) invitation requests, - * keyed on invocation request id. */ - protected HashIntMap _submittedInvites = new HashIntMap(); - /** A table of acknowledged (but not yet accepted or refused) * invitation requests, keyed on invitation id. */ protected HashIntMap _pendingInvites = new HashIntMap(); - /** A counter used to assign a unique id to every invitation. */ - protected static int _localInvitationId = 0; - /** We notify the entities on this list when we get a game ready * notification. */ protected ArrayList _grobs = new ArrayList(); diff --git a/src/java/com/threerings/parlor/client/ParlorReceiver.java b/src/java/com/threerings/parlor/client/ParlorReceiver.java new file mode 100644 index 000000000..4bfdfed04 --- /dev/null +++ b/src/java/com/threerings/parlor/client/ParlorReceiver.java @@ -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); +} diff --git a/src/java/com/threerings/parlor/client/ParlorService.java b/src/java/com/threerings/parlor/client/ParlorService.java index b32441e18..81e47a9bb 100644 --- a/src/java/com/threerings/parlor/client/ParlorService.java +++ b/src/java/com/threerings/parlor/client/ParlorService.java @@ -1,27 +1,33 @@ // -// $Id: ParlorService.java,v 1.12 2002/04/15 16:28:02 shaper Exp $ +// $Id: ParlorService.java,v 1.13 2002/08/14 19:07:52 mdb Exp $ package com.threerings.parlor.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; -import com.threerings.parlor.Log; -import com.threerings.parlor.data.ParlorCodes; import com.threerings.parlor.game.GameConfig; /** - * This class provides an interface to the various parlor services that - * are directly invokable by the client (by means of the invocation - * services). Presently these services are limited to the various - * matchmaking mechanisms. It is unlikely that client code will want to - * make direct use of this class, instead they would make use of the - * programmatic interface provided by the {@link ParlorDirector}. - * - * @see ParlorDirector + * Provides an interface to the various parlor invocation services. + * Presently these services are limited to the various matchmaking + * mechanisms. It is unlikely that client code will want to make direct + * use of this class, instead they would make use of the programmatic + * interface provided by the {@link ParlorDirector}. */ -public class ParlorService implements ParlorCodes +public interface ParlorService extends InvocationService { + /** + * Used to communicate responses to {@link #invite} requests. + */ + public static interface InviteListener extends InvocationListener + { + /** + * Called in response to a successful {@link #invite} request. + */ + public void inviteReceived (int inviteId); + } + /** * You probably don't want to call this directly, but want to generate * your invitation request via {@link ParlorDirector#invite}. Requests @@ -33,18 +39,10 @@ public class ParlorService implements ParlorCodes * @param invitee the username of the user to be invited. * @param config a game config object detailing the type and * configuration of the game to be created. - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int invite (Client client, String invitee, - GameConfig config, Object rsptgt) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { invitee, config }; - return invdir.invoke(MODULE_NAME, INVITE_ID, args, rsptgt); - } + public void invite (Client client, String invitee, GameConfig config, + InviteListener listener); /** * You probably don't want to call this directly, but want to call one @@ -62,21 +60,10 @@ public class ParlorService implements ParlorCodes * the case of an invitation refusal or an updated game configuration * object in the case of a counter-invitation, or null in the case of * an accepted invitation). - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int respond (Client client, int inviteId, int code, - Object arg, Object rsptgt) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { - new Integer(inviteId), new Integer(code), null }; - // we can't have a null argument so we use the empty string - args[2] = (arg == null) ? "" : arg; - return invdir.invoke(MODULE_NAME, RESPOND_INVITE_ID, args, rsptgt); - } + public void respond (Client client, int inviteId, int code, Object arg, + InvocationListener listener); /** * You probably don't want to call this directly, but want to call @@ -86,16 +73,18 @@ public class ParlorService implements ParlorCodes * @param client a connected, operational client instance. * @param inviteId the unique id previously assigned by the server to * this invitation. - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int cancel (Client client, int inviteId, Object rsptgt) + public void cancel (Client client, int inviteId, + InvocationListener listener); + + /** + * Used to communicate responses to {@link #createTable}, {@link + * #joinTable}, and {@link #leaveTable} requests. + */ + public static interface TableListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(inviteId) }; - return invdir.invoke(MODULE_NAME, CANCEL_INVITE_ID, args, rsptgt); + public void tableCreated (int tableId); } /** @@ -108,18 +97,10 @@ public class ParlorService implements ParlorCodes * created table. * @param config the game config for the game to be matchmade by the * table. - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int createTable ( - Client client, int lobbyOid, GameConfig config, Object rsptgt) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(lobbyOid), config }; - return invdir.invoke(MODULE_NAME, CREATE_TABLE_REQUEST, args, rsptgt); - } + public void createTable (Client client, int lobbyOid, GameConfig config, + TableListener listener); /** * You probably don't want to call this directly, but want to call @@ -132,41 +113,22 @@ public class ParlorService implements ParlorCodes * to be added. * @param position the position at the table to which this user desires * to be added. - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int joinTable (Client client, int lobbyOid, int tableId, - int position, Object rsptgt) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(lobbyOid), - new Integer(tableId), - new Integer(position) }; - return invdir.invoke(MODULE_NAME, JOIN_TABLE_REQUEST, args, rsptgt); - } + public void joinTable (Client client, int lobbyOid, int tableId, + int position, InvocationListener listener); /** * You probably don't want to call this directly, but want to call - * {@link TableDirector#leaveTable}. Requests that the current user - * be removed from the specified table. + * {@link TableDirector#leaveTable}. Requests that the current user be + * removed from the specified table. * * @param client a connected, operational client instance. * @param lobbyOid the oid of the lobby that contains the table. * @param tableId the unique id of the table from which this user * wishes to be removed. - * @param rsptgt the object reference that will receive and process - * the response. - * - * @return the invocation request id of the generated request. + * @param listener will receive and process the response. */ - public static int leaveTable ( - Client client, int lobbyOid, int tableId, Object rsptgt) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(lobbyOid), - new Integer(tableId) }; - return invdir.invoke(MODULE_NAME, LEAVE_TABLE_REQUEST, args, rsptgt); - } + public void leaveTable (Client client, int lobbyOid, int tableId, + InvocationListener listener); } diff --git a/src/java/com/threerings/parlor/client/TableDirector.java b/src/java/com/threerings/parlor/client/TableDirector.java index 47b5b03e1..e7fd1e45f 100644 --- a/src/java/com/threerings/parlor/client/TableDirector.java +++ b/src/java/com/threerings/parlor/client/TableDirector.java @@ -1,13 +1,16 @@ // -// $Id: TableDirector.java,v 1.7 2002/04/17 18:26:29 mdb Exp $ +// $Id: TableDirector.java,v 1.8 2002/08/14 19:07:52 mdb Exp $ package com.threerings.parlor.client; import java.util.ArrayList; +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; + import com.threerings.presents.dobj.EntryAddedEvent; -import com.threerings.presents.dobj.EntryUpdatedEvent; import com.threerings.presents.dobj.EntryRemovedEvent; +import com.threerings.presents.dobj.EntryUpdatedEvent; import com.threerings.presents.dobj.SetListener; import com.threerings.crowd.data.BodyObject; @@ -37,8 +40,8 @@ import com.threerings.parlor.util.ParlorContext; * lobby in which the table matchmaking takes place implements the {@link * TableLobbyObject} interface. */ -public class TableDirector - implements SetListener +public class TableDirector extends BasicDirector + implements SetListener, ParlorService.TableListener { /** * Creates a new table director to manage tables with the specified @@ -54,6 +57,8 @@ public class TableDirector public TableDirector ( ParlorContext ctx, String tableField, TableObserver observer) { + super(ctx); + // keep track of this stuff _ctx = ctx; _tableField = tableField; @@ -140,8 +145,7 @@ public class TableDirector } // go ahead and issue the create request - ParlorService.createTable( - _ctx.getClient(), _lobby.getOid(), config, this); + _pservice.createTable(_ctx.getClient(), _lobby.getOid(), config, this); } /** @@ -166,8 +170,8 @@ public class TableDirector } // issue the join request - ParlorService.joinTable( - _ctx.getClient(), _lobby.getOid(), tableId, position, this); + _pservice.joinTable(_ctx.getClient(), _lobby.getOid(), tableId, + position, this); } /** @@ -185,8 +189,14 @@ public class TableDirector } // issue the leave request - ParlorService.leaveTable( - _ctx.getClient(), _lobby.getOid(), tableId, this); + _pservice.leaveTable(_ctx.getClient(), _lobby.getOid(), tableId, this); + } + + // documentation inherited + protected void fetchServices (Client client) + { + // get a handle on our parlor services + _pservice = (ParlorService)client.requireService(ParlorService.class); } // documentation inherited @@ -234,54 +244,19 @@ public class TableDirector } } - /** - * Called by the invocation services when a table creation request was - * received by the server and the table was successfully created. - * - * @param invid the invocation id of the invitation request. - */ - public void handleTableCreated (int invid, int tableId) + // documentation inherited from interface + public void tableCreated (int tableId) { // nothing much to do here Log.info("Table creation succeeded [tableId=" + tableId + "]."); } - /** - * Called by the invocation services when a table creation request - * failed or was rejected for some reason. - * - * @param invid the invocation id of the creation request. - * @param reason a reason code explaining the failure. - */ - public void handleCreateTableFailed (int invid, String reason) + // documentation inherited from interface + public void requestFailed (String reason) { Log.warning("Table creation failed [reason=" + reason + "]."); } - /** - * Called by the invocation services when a join table request failed - * or was rejected for some reason. - * - * @param invid the invocation id of the join request. - * @param reason a reason code explaining the failure. - */ - public void handleJoinTableFailed (int invid, String reason) - { - Log.warning("Join table failed [reason=" + reason + "]."); - } - - /** - * Called by the invocation services when a leave table request failed - * or was rejected for some reason. - * - * @param invid the invocation id of the leave request. - * @param reason a reason code explaining the failure. - */ - public void handleLeaveTableFailed (int invid, String reason) - { - Log.warning("Leave table failed [reason=" + reason + "]."); - } - /** * Checks to see if we're a member of this table and notes it as our * table, if so. @@ -338,6 +313,9 @@ public class TableDirector /** A context by which we can access necessary client services. */ protected ParlorContext _ctx; + /** Parlor server-side services. */ + protected ParlorService _pservice; + /** The place object in which we're currently managing tables. */ protected PlaceObject _lobby; diff --git a/src/java/com/threerings/parlor/data/ParlorCodes.java b/src/java/com/threerings/parlor/data/ParlorCodes.java index 74b425f8f..76d46483a 100644 --- a/src/java/com/threerings/parlor/data/ParlorCodes.java +++ b/src/java/com/threerings/parlor/data/ParlorCodes.java @@ -1,48 +1,15 @@ // -// $Id: ParlorCodes.java,v 1.3 2002/04/17 18:26:29 mdb Exp $ +// $Id: ParlorCodes.java,v 1.4 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.data; import com.threerings.presents.data.InvocationCodes; -import com.threerings.parlor.client.ParlorDirector; -import com.threerings.parlor.client.TableDirector; - /** * Contains codes used by the parlor invocation services. */ public interface ParlorCodes extends InvocationCodes { - /** The module name for the parlor services. */ - public static final String MODULE_NAME = "parlor"; - - /** The message identifier for a game ready notification. This is - * mapped by the invocation services to a call to {@link - * ParlorDirector#handleGameReadyNotification}. */ - public static final String GAME_READY_NOTIFICATION = "GameReady"; - - /** The message identifier for an invitation creation request or - * notification. The notification is mapped by the invocation services - * to a call to {@link ParlorDirector#handleInviteNotification}. */ - public static final String INVITE_ID = "Invite"; - - /** The response identifier for an accepted invite request. This is - * mapped by the invocation services to a call to {@link - * ParlorDirector#handleInviteReceived}. */ - public static final String INVITE_RECEIVED_RESPONSE = "InviteReceived"; - - /** The message identifier for an invitation cancellation request or - * notification. The notification is mapped by the invocation services - * to a call to {@link - * ParlorDirector#handleCancelInviteNotification}. */ - public static final String CANCEL_INVITE_ID = "CancelInvite"; - - /** The message identifier for an invitation response request or - * notification. The notification is mapped by the invocation services - * to a call to {@link - * ParlorDirector#handleRespondInviteNotification}. */ - public static final String RESPOND_INVITE_ID = "RespondInvite"; - /** The response code for an accepted invitation. */ public static final int INVITATION_ACCEPTED = 0; @@ -57,20 +24,6 @@ public interface ParlorCodes extends InvocationCodes * received. */ public static final String INVITEE_NOT_ONLINE = "m.invitee_not_online"; - /** The message identifier for a create table request. */ - public static final String CREATE_TABLE_REQUEST = "CreateTable"; - - /** The response identifier for a table created response. This is - * mapped by the invocation services to a call to {@link - * TableDirector#handleTableCreated}. */ - public static final String TABLE_CREATED_RESPONSE = "TableCreated"; - - /** The message identifier for a join table request. */ - public static final String JOIN_TABLE_REQUEST = "JoinTable"; - - /** The message identifier for a leave table request. */ - public static final String LEAVE_TABLE_REQUEST = "LeaveTable"; - /** An error code returned when a user requests to join a table that * doesn't exist. */ public static final String NO_SUCH_TABLE = "m.no_such_table"; diff --git a/src/java/com/threerings/parlor/data/ParlorMarshaller.java b/src/java/com/threerings/parlor/data/ParlorMarshaller.java new file mode 100644 index 000000000..abd34f393 --- /dev/null +++ b/src/java/com/threerings/parlor/data/ParlorMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/parlor/data/Table.java b/src/java/com/threerings/parlor/data/Table.java index 47ce91c42..0da49995f 100644 --- a/src/java/com/threerings/parlor/data/Table.java +++ b/src/java/com/threerings/parlor/data/Table.java @@ -1,5 +1,5 @@ // -// $Id: Table.java,v 1.10 2002/07/23 05:54:52 mdb Exp $ +// $Id: Table.java,v 1.11 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.data; @@ -237,7 +237,7 @@ public class Table } // documentation inherited - public Object getKey () + public Comparable getKey () { return tableId; } diff --git a/src/java/com/threerings/parlor/game/GameCodes.java b/src/java/com/threerings/parlor/game/GameCodes.java deleted file mode 100644 index e5ca1cde4..000000000 --- a/src/java/com/threerings/parlor/game/GameCodes.java +++ /dev/null @@ -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"; -} diff --git a/src/java/com/threerings/parlor/game/GameController.java b/src/java/com/threerings/parlor/game/GameController.java index 7e1e227ba..5daf19b5c 100644 --- a/src/java/com/threerings/parlor/game/GameController.java +++ b/src/java/com/threerings/parlor/game/GameController.java @@ -1,5 +1,5 @@ // -// $Id: GameController.java,v 1.17 2002/06/18 02:35:10 shaper Exp $ +// $Id: GameController.java,v 1.18 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.game; @@ -31,7 +31,7 @@ import com.threerings.parlor.util.ParlorContext; * distributed object events. */ public abstract class GameController extends PlaceController - implements AttributeChangeListener, GameCodes + implements AttributeChangeListener { /** * Initializes this game controller with the game configuration that @@ -70,10 +70,16 @@ public abstract class GameController extends PlaceController // and add ourselves as a listener _gobj.addListener(this); - // finally let the game manager know that we're ready to roll - MessageEvent mevt = new MessageEvent( - _gobj.getOid(), PLAYER_READY_NOTIFICATION, null); - _ctx.getDObjectManager().postEvent(mevt); + // we don't want to claim to be finished until any derived classes + // that overrode this method have executed, so we'll queue up a + // runnable here that will let the game manager know that we're + // ready on the next pass through the distributed event loop + _ctx.getClient().getInvoker().invokeLater(new Runnable() { + public void run () { + // finally let the game manager know that we're ready to roll + _gobj.service.playerReady(_ctx.getClient()); + } + }); } /** diff --git a/src/java/com/threerings/parlor/game/GameDispatcher.java b/src/java/com/threerings/parlor/game/GameDispatcher.java new file mode 100644 index 000000000..e8761ad22 --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/parlor/game/GameManager.java b/src/java/com/threerings/parlor/game/GameManager.java index 88b7aa524..966f95ade 100644 --- a/src/java/com/threerings/parlor/game/GameManager.java +++ b/src/java/com/threerings/parlor/game/GameManager.java @@ -1,5 +1,5 @@ // -// $Id: GameManager.java,v 1.37 2002/06/19 23:41:25 shaper Exp $ +// $Id: GameManager.java,v 1.38 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.game; @@ -7,11 +7,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.dobj.AttributeChangeListener; import com.threerings.presents.dobj.AttributeChangedEvent; import com.threerings.presents.dobj.MessageEvent; -import com.threerings.crowd.chat.ChatProvider; +import com.threerings.crowd.chat.SpeakProvider; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceObject; @@ -23,6 +24,7 @@ import com.threerings.crowd.server.PlaceManagerDelegate; import com.threerings.parlor.Log; import com.threerings.parlor.data.ParlorCodes; +import com.threerings.parlor.server.ParlorSender; /** * The game manager handles the server side management of a game. It @@ -34,34 +36,15 @@ import com.threerings.parlor.data.ParlorCodes; * bodies in that location. */ public class GameManager extends PlaceManager - implements ParlorCodes, GameCodes, AttributeChangeListener + implements ParlorCodes, GameProvider, AttributeChangeListener { - // documentation inherited - protected Class getPlaceObjectClass () - { - return GameObject.class; - } - - // documentation inherited - protected void didInit () - { - super.didInit(); - - // cast our configuration object (do we need to do this?) - _gconfig = (GameConfig)_config; - - // register our message handlers - MessageHandler handler = new PlayerReadyHandler(); - registerMessageHandler(PLAYER_READY_NOTIFICATION, handler); - } - /** - * Initializes the game manager with the supplied game configuration - * object. This happens before startup and before the game object has - * been created. + * Provides the game manager with a list of the usernames of all + * players in the game. This happens before startup and before the + * game object has been created. * * @param players the usernames of all of the players in this game or - * null if the game has no specific set of players. + * a zero-length array if the game has no specific set of players. */ public void setPlayers (String[] players) { @@ -167,8 +150,9 @@ public class GameManager extends PlaceManager public void systemMessage ( String msgbundle, String msg, boolean waitForStart) { - if (waitForStart && ((_gameobj == null) || - (_gameobj.state == GameObject.AWAITING_PLAYERS))) { + if (waitForStart && + ((_gameobj == null) || + (_gameobj.state == GameObject.AWAITING_PLAYERS))) { // queue up the message. if (_startmsgs == null) { _startmsgs = new ArrayList(); @@ -179,7 +163,13 @@ public class GameManager extends PlaceManager } // otherwise, just deliver the message - ChatProvider.sendSystemMessage(_gameobj.getOid(), msgbundle, msg); + SpeakProvider.sendSystemSpeak(_gameobj, msgbundle, msg); + } + + // documentation inherited + protected Class getPlaceObjectClass () + { + return GameObject.class; } // documentation inherited @@ -193,24 +183,27 @@ public class GameManager extends PlaceManager // stick the players into the game object _gameobj.setPlayers(_players); + // create and fill in our game service object + GameMarshaller service = (GameMarshaller) + _invmgr.registerDispatcher(new GameDispatcher(this), false); + _gameobj.setService(service); + // let the players of this game know that we're ready to roll (if // we have a specific set of players) if (_players != null) { - Object[] args = new Object[] { - new Integer(_gameobj.getOid()) }; + int gameOid = _gameobj.getOid(); for (int i = 0; i < _players.length; i++) { BodyObject bobj = CrowdServer.lookupBody(_players[i]); if (bobj == null) { Log.warning("Unable to deliver game ready to " + "non-existent player " + - "[gameOid=" + _gameobj.getOid() + + "[gameOid=" + gameOid + ", player=" + _players[i] + "]."); continue; } // deliver a game ready notification to the player - CrowdServer.invmgr.sendNotification( - bobj.getOid(), MODULE_NAME, GAME_READY_NOTIFICATION, args); + ParlorSender.gameIsReady(bobj, gameOid); } } } @@ -448,6 +441,35 @@ public class GameManager extends PlaceManager }); } + // documentation inherited from interface + public void playerReady (ClientObject caller) + { + BodyObject plobj = (BodyObject)caller; + + // make a note of this player's oid + int pidx = _gameobj.getPlayerIndex(plobj.username); + if (pidx == -1) { + Log.warning("Received playerReady() from non-player? " + + "[caller=" + caller + "]."); + return; + } + _playerOids[pidx] = plobj.getOid(); + + // and check to see if we're all set + boolean allSet = true; + for (int ii = 0; ii < _players.length; ii++) { + if ((_playerOids[ii] == 0) && + ((_AIs == null) || (_AIs[ii] == -1))) { + allSet = false; + } + } + + // if everyone is now ready to go, make a note of it + if (allSet) { + playersAllHere(); + } + } + // documentation inherited public void attributeChanged (AttributeChangedEvent event) { @@ -463,39 +485,6 @@ public class GameManager extends PlaceManager } } - /** Handles player ready notifications. */ - protected class PlayerReadyHandler implements MessageHandler - { - public void handleEvent (MessageEvent event, PlaceManager pmgr) - { - int cloid = event.getSourceOid(); - BodyObject body = (BodyObject)CrowdServer.omgr.getObject(cloid); - if (body == null) { - Log.warning("Player sent am ready notification and then " + - "disappeared [event=" + event + "]."); - return; - } - - // make a note of this player's oid and check to see if we're - // all set at the same time - boolean allSet = true; - for (int i = 0; i < _players.length; i++) { - if (_players[i].equals(body.username)) { - _playerOids[i] = body.getOid(); - } - if ((_playerOids[i] == 0) && - ((_AIs == null) || (_AIs[i] == -1))) { - allSet = false; - } - } - - // if everyone is now ready to go, make a note of it - if (allSet) { - playersAllHere(); - } - } - } - /** * A helper operation to distribute AI ticks to our delegates. */ @@ -515,9 +504,6 @@ public class GameManager extends PlaceManager protected byte _level; } - /** A reference to our game configuration. */ - protected GameConfig _gconfig; - /** A reference to our game object. */ protected GameObject _gameobj; diff --git a/src/java/com/threerings/parlor/game/GameMarshaller.java b/src/java/com/threerings/parlor/game/GameMarshaller.java new file mode 100644 index 000000000..7be82bf7b --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameMarshaller.java @@ -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. +} diff --git a/src/java/com/threerings/parlor/game/GameObject.dobj b/src/java/com/threerings/parlor/game/GameObject.dobj index 4e35b7bf1..126ba652e 100644 --- a/src/java/com/threerings/parlor/game/GameObject.dobj +++ b/src/java/com/threerings/parlor/game/GameObject.dobj @@ -1,5 +1,5 @@ // -// $Id: GameObject.dobj,v 1.10 2002/08/09 23:34:10 shaper Exp $ +// $Id: GameObject.dobj,v 1.11 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.game; @@ -33,6 +33,9 @@ public class GameObject extends PlaceObject /** A game state constant indicating that the game was cancelled. */ public static final int CANCELLED = GAME_OVER+3; + /** Provides general game invocation services. */ + public GameMarshaller service; + /** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY}, * {@link #GAME_OVER}, or {@link #CANCELLED}. */ public int state; diff --git a/src/java/com/threerings/parlor/game/GameObject.java b/src/java/com/threerings/parlor/game/GameObject.java index 75d35a74e..700b93e78 100644 --- a/src/java/com/threerings/parlor/game/GameObject.java +++ b/src/java/com/threerings/parlor/game/GameObject.java @@ -1,5 +1,5 @@ // -// $Id: GameObject.java,v 1.5 2002/08/09 23:34:10 shaper Exp $ +// $Id: GameObject.java,v 1.6 2002/08/14 19:07:53 mdb Exp $ package com.threerings.parlor.game; @@ -20,6 +20,9 @@ import com.threerings.crowd.data.PlaceObject; */ public class GameObject extends PlaceObject { + /** The field name of the service field. */ + public static final String SERVICE = "service"; + /** The field name of the state field. */ public static final String STATE = "state"; @@ -45,6 +48,9 @@ public class GameObject extends PlaceObject /** A game state constant indicating that the game was cancelled. */ public static final int CANCELLED = GAME_OVER+3; + /** Provides general game invocation services. */ + public GameMarshaller service; + /** The game state, one of {@link #AWAITING_PLAYERS}, {@link #IN_PLAY}, * {@link #GAME_OVER}, or {@link #CANCELLED}. */ public int state; @@ -68,6 +74,20 @@ public class GameObject extends PlaceObject ListUtil.indexOfEqual(players, username); } + /** + * Requests that the service 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 state field be set to the specified * value. The local value will be updated immediately and an event diff --git a/src/java/com/threerings/parlor/game/GameProvider.java b/src/java/com/threerings/parlor/game/GameProvider.java new file mode 100644 index 000000000..c4d635293 --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameProvider.java @@ -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); +} diff --git a/src/java/com/threerings/parlor/game/GameService.java b/src/java/com/threerings/parlor/game/GameService.java new file mode 100644 index 000000000..f51938055 --- /dev/null +++ b/src/java/com/threerings/parlor/game/GameService.java @@ -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); +} diff --git a/src/java/com/threerings/parlor/server/ParlorDispatcher.java b/src/java/com/threerings/parlor/server/ParlorDispatcher.java new file mode 100644 index 000000000..f402083bf --- /dev/null +++ b/src/java/com/threerings/parlor/server/ParlorDispatcher.java @@ -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); + } + } +} diff --git a/src/java/com/threerings/parlor/server/ParlorManager.java b/src/java/com/threerings/parlor/server/ParlorManager.java index 5c0f90dee..58fad26ce 100644 --- a/src/java/com/threerings/parlor/server/ParlorManager.java +++ b/src/java/com/threerings/parlor/server/ParlorManager.java @@ -1,15 +1,15 @@ // -// $Id: ParlorManager.java,v 1.18 2002/04/15 16:28:02 shaper Exp $ +// $Id: ParlorManager.java,v 1.19 2002/08/14 19:07:54 mdb Exp $ package com.threerings.parlor.server; import com.samskivert.util.HashIntMap; +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; -import com.threerings.crowd.server.CrowdServer; +import com.threerings.crowd.server.PlaceRegistry; import com.threerings.parlor.Log; import com.threerings.parlor.data.ParlorCodes; @@ -26,6 +26,9 @@ import com.threerings.parlor.game.GameManager; public class ParlorManager implements ParlorCodes { + /** Provides the server-side implementation of the parlor services. */ + public ParlorProvider parprov; + /** * Initializes the parlor manager. This should be called by the server * that is making use of the parlor services on the single instance of @@ -33,15 +36,17 @@ public class ParlorManager * * @param invmgr a reference to the invocation manager in use by this * server. + * @param plreg a reference to the place registry to be used by the + * parlor manager when creating game places. */ - public void init (InvocationManager invmgr) + public void init (InvocationManager invmgr, PlaceRegistry plreg) { - // register our invocation provider - ParlorProvider pprov = new ParlorProvider(this); - invmgr.registerProvider(MODULE_NAME, pprov); + // create and register our invocation provider + parprov = new ParlorProvider(this); + invmgr.registerDispatcher(new ParlorDispatcher(parprov), true); - // keep this around for later - _invmgr = invmgr; + // keep this for later + _plreg = plreg; } /** @@ -56,14 +61,14 @@ public class ParlorManager * @return the invitation identifier for the newly created invitation * record. * - * @exception ServiceFailedException thrown if the invitation was not + * @exception InvocationException thrown if the invitation was not * able to be processed for some reason (like the invited player has * requested not to be disturbed). The explanation will be provided in * the message data of the exception. */ public int invite (BodyObject inviter, BodyObject invitee, GameConfig config) - throws ServiceFailedException + throws InvocationException { // Log.info("Received invitation request [inviter=" + inviter + // ", invitee=" + invitee + ", config=" + config + "]."); @@ -79,10 +84,8 @@ public class ParlorManager _invites.put(invite.inviteId, invite); // deliver an invite notification to the invitee - Object[] args = new Object[] { - new Integer(invite.inviteId), inviter.username, config }; - _invmgr.sendNotification( - invitee.getOid(), MODULE_NAME, INVITE_ID, args); + ParlorSender.sendInvite(invitee, invite.inviteId, + inviter.username, config); // and let the caller know the invite id we assigned return invite.inviteId; @@ -126,10 +129,8 @@ public class ParlorManager // let the other user know that a response was made to this // invitation - Object[] args = new Object[] { - new Integer(invite.inviteId), new Integer(code), arg }; - _invmgr.sendNotification( - invite.inviter.getOid(), MODULE_NAME, RESPOND_INVITE_ID, args); + ParlorSender.sendInviteResponse( + invite.inviter, invite.inviteId, code, arg); switch (code) { case INVITATION_ACCEPTED: @@ -187,7 +188,7 @@ public class ParlorManager // started up (which is done by the place registry once the // game object creation has completed) GameManager gmgr = (GameManager) - CrowdServer.plreg.createPlace(invite.config, null); + _plreg.createPlace(invite.config, null); // provide the game manager with the player list gmgr.setPlayers(new String[] { @@ -242,8 +243,8 @@ public class ParlorManager } } - /** A reference to the invocation manager in operation on this server. */ - protected InvocationManager _invmgr; + /** The place registry with which we operate. */ + protected PlaceRegistry _plreg; /** The table of pending invitations. */ protected HashIntMap _invites = new HashIntMap(); diff --git a/src/java/com/threerings/parlor/server/ParlorProvider.java b/src/java/com/threerings/parlor/server/ParlorProvider.java index 7daea308e..a580b25e8 100644 --- a/src/java/com/threerings/parlor/server/ParlorProvider.java +++ b/src/java/com/threerings/parlor/server/ParlorProvider.java @@ -1,16 +1,20 @@ // -// $Id: ParlorProvider.java,v 1.12 2002/04/17 18:26:30 mdb Exp $ +// $Id: ParlorProvider.java,v 1.13 2002/08/14 19:07:54 mdb Exp $ package com.threerings.parlor.server; +import com.threerings.presents.client.InvocationService.InvocationListener; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.server.CrowdServer; import com.threerings.crowd.server.PlaceManager; import com.threerings.parlor.Log; +import com.threerings.parlor.client.ParlorService.InviteListener; +import com.threerings.parlor.client.ParlorService.TableListener; import com.threerings.parlor.data.ParlorCodes; import com.threerings.parlor.game.GameConfig; @@ -20,7 +24,7 @@ import com.threerings.parlor.game.GameConfig; * Primarily these are the matchmaking mechanisms. */ public class ParlorProvider - extends InvocationProvider implements ParlorCodes + implements InvocationProvider, ParlorCodes { /** * Constructs a parlor provider instance which will be used to handle @@ -40,82 +44,79 @@ public class ParlorProvider * Processes a request from the client to invite another user to play * a game. */ - public void handleInviteRequest ( - BodyObject source, int invid, String invitee, GameConfig config) - throws ServiceFailedException + public void invite (ClientObject caller, String invitee, + GameConfig config, InviteListener listener) + throws InvocationException { // Log.info("Handling invite request [source=" + source + -// ", invid=" + invid + ", invitee=" + invitee + -// ", config=" + config + "]."); +// ", invitee=" + invitee + ", config=" + config + "]."); + BodyObject source = (BodyObject)caller; String rsp = null; // ensure that the invitee is online at present BodyObject target = CrowdServer.lookupBody(invitee); if (target == null) { - throw new ServiceFailedException(INVITEE_NOT_ONLINE); + throw new InvocationException(INVITEE_NOT_ONLINE); } // submit the invite request to the parlor manager int inviteId = _pmgr.invite(source, target, config); - sendResponse(source, invid, INVITE_RECEIVED_RESPONSE, - new Integer(inviteId)); + listener.inviteReceived(inviteId); } /** * Processes a request from the client to respond to an outstanding * invitation by accepting, refusing, or countering it. */ - public void handleRespondInviteRequest ( - BodyObject source, int invid, int inviteId, int code, Object arg) + public void respond (ClientObject caller, int inviteId, int code, + Object arg, InvocationListener listener) { // pass this on to the parlor manager - _pmgr.respondToInvite(source, inviteId, code, arg); + _pmgr.respondToInvite((BodyObject)caller, inviteId, code, arg); } /** * Processes a request from the client to cancel an outstanding * invitation. */ - public void handleCancelInviteRequest ( - BodyObject source, int invid, int inviteId) + public void cancel (ClientObject caller, int inviteId, + InvocationListener listener) { // pass this on to the parlor manager - _pmgr.cancelInvite(source, inviteId); + _pmgr.cancelInvite((BodyObject)caller, inviteId); } /** * Processes a request from the client to create a new table. */ - public void handleCreateTableRequest ( - BodyObject source, int invid, int lobbyOid, GameConfig config) - throws ServiceFailedException + public void createTable (ClientObject caller, int lobbyOid, + GameConfig config, TableListener listener) + throws InvocationException { - Log.info("Handling create table request [source=" + source + - ", invid=" + invid + ", lobbyOid=" + lobbyOid + - ", config=" + config + "]."); + Log.info("Handling create table request [caller=" + caller + + ", lobbyOid=" + lobbyOid + ", config=" + config + "]."); // pass the creation request on to the table manager TableManager tmgr = getTableManager(lobbyOid); - int tableId = tmgr.createTable(source, config); - sendResponse(source, invid, TABLE_CREATED_RESPONSE, - new Integer(tableId)); + int tableId = tmgr.createTable((BodyObject)caller, config); + listener.tableCreated(tableId); } /** * Processes a request from the client to join an existing table. */ - public void handleJoinTableRequest ( - BodyObject source, int invid, int lobbyOid, int tableId, int position) - throws ServiceFailedException + public void joinTable (ClientObject caller, int lobbyOid, int tableId, + int position, InvocationListener listener) + throws InvocationException { - Log.info("Handling join table request [source=" + source + - ", invid=" + invid + ", lobbyOid=" + lobbyOid + - ", tableId=" + tableId + ", position=" + position + "]."); + Log.info("Handling join table request [caller=" + caller + + ", lobbyOid=" + lobbyOid + ", tableId=" + tableId + + ", position=" + position + "]."); // pass the join request on to the table manager TableManager tmgr = getTableManager(lobbyOid); - tmgr.joinTable(source, tableId, position); + tmgr.joinTable((BodyObject)caller, tableId, position); // there is normally no success response. the client will see // themselves show up in the table that they joined @@ -124,17 +125,16 @@ public class ParlorProvider /** * Processes a request from the client to leave an existing table. */ - public void handleLeaveTableRequest ( - BodyObject source, int invid, int lobbyOid, int tableId) - throws ServiceFailedException + public void leaveTable (ClientObject caller, int lobbyOid, int tableId, + InvocationListener listener) + throws InvocationException { - Log.info("Handling leave table request [source=" + source + - ", invid=" + invid + ", lobbyOid=" + lobbyOid + - ", tableId=" + tableId + "]."); + Log.info("Handling leave table request [caller=" + caller + + ", lobbyOid=" + lobbyOid + ", tableId=" + tableId + "]."); // pass the join request on to the table manager TableManager tmgr = getTableManager(lobbyOid); - tmgr.leaveTable(source, tableId); + tmgr.leaveTable((BodyObject)caller, tableId); // there is normally no success response. the client will see // themselves removed from the table they just left @@ -145,25 +145,25 @@ public class ParlorProvider * casts it to a table lobby manager and obtains the associated table * manager reference. * - * @exception ServiceFailedException thrown if something goes wrong + * @exception InvocationException thrown if something goes wrong * along the way like no place manager exists or the place manager * that does exist doesn't implement table lobby manager. */ protected TableManager getTableManager (int lobbyOid) - throws ServiceFailedException + throws InvocationException { PlaceManager plmgr = CrowdServer.plreg.getPlaceManager(lobbyOid); if (plmgr == null) { Log.warning("No place manager exists from which to obtain " + "table manager reference [ploid=" + lobbyOid + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); + throw new InvocationException(INTERNAL_ERROR); } // sanity check if (!(plmgr instanceof TableManagerProvider)) { Log.warning("Place manager not a table lobby manager " + "[plmgr=" + plmgr + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); + throw new InvocationException(INTERNAL_ERROR); } return ((TableManagerProvider)plmgr).getTableManager(); diff --git a/src/java/com/threerings/parlor/server/ParlorSender.java b/src/java/com/threerings/parlor/server/ParlorSender.java new file mode 100644 index 000000000..0cea7fa9a --- /dev/null +++ b/src/java/com/threerings/parlor/server/ParlorSender.java @@ -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. +} diff --git a/src/java/com/threerings/parlor/server/TableManager.java b/src/java/com/threerings/parlor/server/TableManager.java index 0fa8bb721..5640468a7 100644 --- a/src/java/com/threerings/parlor/server/TableManager.java +++ b/src/java/com/threerings/parlor/server/TableManager.java @@ -1,5 +1,5 @@ // -// $Id: TableManager.java,v 1.6 2002/07/23 05:54:52 mdb Exp $ +// $Id: TableManager.java,v 1.7 2002/08/14 19:07:54 mdb Exp $ package com.threerings.parlor.server; @@ -14,7 +14,7 @@ import com.threerings.presents.dobj.ObjectDeathListener; import com.threerings.presents.dobj.ObjectDestroyedEvent; import com.threerings.presents.dobj.ObjectRemovedEvent; import com.threerings.presents.dobj.OidListListener; -import com.threerings.presents.server.ServiceFailedException; +import com.threerings.presents.server.InvocationException; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceObject; @@ -69,12 +69,12 @@ public class TableManager * * @return the id of the newly created table. * - * @exception ServiceFailedException thrown if the table creation was + * @exception InvocationException thrown if the table creation was * not able processed for some reason. The explanation will be * provided in the message data of the exception. */ public int createTable (BodyObject creator, GameConfig config) - throws ServiceFailedException + throws InvocationException { // make sure the game config implements TableConfig if (!(config instanceof TableConfig)) { @@ -82,7 +82,7 @@ public class TableManager "using the table services [creator=" + creator + ", lobby=" + _plobj.getOid() + ", config=" + config + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); + throw new InvocationException(INTERNAL_ERROR); } // make sure the creator is an occupant of the lobby in which @@ -91,7 +91,7 @@ public class TableManager Log.warning("Requested to create a table in a lobby not " + "occupied by the creator [creator=" + creator + ", loboid=" + _plobj.getOid() + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); + throw new InvocationException(INTERNAL_ERROR); } // create a brand spanking new table @@ -105,7 +105,7 @@ public class TableManager "table!? [table=" + table + ", creator=" + creator + ", error=" + error + "]."); // bail out now and abort the table creation process - throw new ServiceFailedException(error); + throw new InvocationException(error); } // stick the table into the table lobby object @@ -126,7 +126,7 @@ public class TableManager * the specified position. If the user successfully joins the table, * the function will return normally. If they are not able to join for * some reason (the slot is already full, etc.), a {@link - * ServiceFailedException} will be thrown with a message code that + * InvocationException} will be thrown with a message code that * describes the reason for failure. If the user does successfully * join, they will be added to the table entry in the tables set in * the place object that is hosting the table. @@ -136,17 +136,17 @@ public class TableManager * @param tableId the id of the table to be joined. * @param position the position at which to join the table. * - * @exception ServiceFailedException thrown if the joining was not - * able processed for some reason. The explanation will be provided in - * the message data of the exception. + * @exception InvocationException thrown if the joining was not able + * processed for some reason. The explanation will be provided in the + * message data of the exception. */ public void joinTable (BodyObject joiner, int tableId, int position) - throws ServiceFailedException + throws InvocationException { // look the table up Table table = (Table)_tables.get(tableId); if (table == null) { - throw new ServiceFailedException(NO_SUCH_TABLE); + throw new InvocationException(NO_SUCH_TABLE); } // request that the user be added to the table at that position @@ -154,7 +154,7 @@ public class TableManager table.setOccupant(position, joiner.username, joiner.getOid()); // if that failed, report the error if (error != null) { - throw new ServiceFailedException(error); + throw new InvocationException(error); } // determine whether or not it's time to start the game @@ -188,29 +188,29 @@ public class TableManager * table. If the user successfully leaves the table, the function will * return normally. If they are not able to leave for some reason * (they aren't sitting at the table, etc.), a {@link - * ServiceFailedException} will be thrown with a message code that + * InvocationException} will be thrown with a message code that * describes the reason for failure. * * @param leaver the body object of the user that wishes to leave the * table. * @param tableId the id of the table to be left. * - * @exception ServiceFailedException thrown if the leaving was not - * able processed for some reason. The explanation will be provided in - * the message data of the exception. + * @exception InvocationException thrown if the leaving was not able + * processed for some reason. The explanation will be provided in the + * message data of the exception. */ public void leaveTable (BodyObject leaver, int tableId) - throws ServiceFailedException + throws InvocationException { // look the table up Table table = (Table)_tables.get(tableId); if (table == null) { - throw new ServiceFailedException(NO_SUCH_TABLE); + throw new InvocationException(NO_SUCH_TABLE); } // request that the user be removed from the table if (!table.clearOccupant(leaver.username)) { - throw new ServiceFailedException(NOT_AT_TABLE); + throw new InvocationException(NOT_AT_TABLE); } // remove the mapping from this user to the table diff --git a/src/java/com/threerings/presents/client/BasicDirector.java b/src/java/com/threerings/presents/client/BasicDirector.java new file mode 100644 index 000000000..7885b394d --- /dev/null +++ b/src/java/com/threerings/presents/client/BasicDirector.java @@ -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) + { + } +} diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index b3206900f..865a45499 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -1,5 +1,5 @@ // -// $Id: Client.java,v 1.27 2002/06/04 16:34:24 mdb Exp $ +// $Id: Client.java,v 1.28 2002/08/14 19:07:54 mdb Exp $ package com.threerings.presents.client; @@ -196,6 +196,40 @@ public class Client return _invdir; } + /** + * Returns the first bootstrap service that could be located that + * implements the supplied {@link InvocationService} derivation. + * null is returned if no such service could be found. + */ + public InvocationService getService (Class sclass) + { + int scount = _bstrap.services.size(); + for (int ii = 0; ii < scount; ii++) { + InvocationService service = (InvocationService) + _bstrap.services.get(ii); + if (sclass.isInstance(service)) { + return service; + } + } + return null; + } + + /** + * Like {@link #getService} except that a {@link RuntimeException} is + * thrown if the service is not available. Useful to avoid redundant + * error checking when you know that the shit will hit the fan if a + * particular invocation service is not available. + */ + public InvocationService requireService (Class sclass) + { + InvocationService isvc = getService(sclass); + if (isvc == null) { + throw new RuntimeException(sclass.getName() + " isn't available. " + + "I can't bear to go on."); + } + return isvc; + } + /** * Returns a reference to the bootstrap data provided to this client * at logon time. @@ -219,7 +253,7 @@ public class Client /** * Returns true if we are logged on, false if we're not. */ - public synchronized boolean loggedOn () + public synchronized boolean isLoggedOn () { // if we have a communicator, we're logged on return (_comm != null); @@ -312,7 +346,7 @@ public class Client notifyObservers(Client.CLIENT_FAILED_TO_LOGON, cause); } }; - _invdir.init(_comm.getDObjectManager(), _cloid, _bstrap.invOid, rl); + _invdir.init(_comm.getDObjectManager(), _cloid, rl); // we can't quite call initialization completed at this point // because we need for the invocation director to fully initialize @@ -356,6 +390,8 @@ public class Client public void run () { // clear out our communicator reference _comm = null; + // and let our invocation director know we're logged off + _invdir.cleanup(); } }); } diff --git a/src/java/com/threerings/presents/client/InvocationDecoder.java b/src/java/com/threerings/presents/client/InvocationDecoder.java new file mode 100644 index 000000000..c73dc56f6 --- /dev/null +++ b/src/java/com/threerings/presents/client/InvocationDecoder.java @@ -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) + "]."); + } +} diff --git a/src/java/com/threerings/presents/client/InvocationDirector.java b/src/java/com/threerings/presents/client/InvocationDirector.java index c9473623b..1fdabe07c 100644 --- a/src/java/com/threerings/presents/client/InvocationDirector.java +++ b/src/java/com/threerings/presents/client/InvocationDirector.java @@ -1,70 +1,69 @@ // -// $Id: InvocationDirector.java,v 1.20 2002/04/16 21:37:23 mdb Exp $ +// $Id: InvocationDirector.java,v 1.21 2002/08/14 19:07:54 mdb Exp $ package com.threerings.presents.client; -import java.lang.reflect.Method; -import java.util.HashMap; +import java.util.ArrayList; +import java.util.Iterator; import com.samskivert.util.HashIntMap; -import com.samskivert.util.StringUtil; import com.samskivert.util.ResultListener; +import com.samskivert.util.StringUtil; import com.threerings.presents.Log; -import com.threerings.presents.data.*; -import com.threerings.presents.dobj.*; -import com.threerings.presents.util.ClassUtil; +import com.threerings.presents.client.InvocationReceiver.Registration; + +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationMarshaller.ListenerMarshaller; +import com.threerings.presents.data.InvocationMarshaller; + +import com.threerings.presents.dobj.DEvent; +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.DObjectManager; +import com.threerings.presents.dobj.EventListener; +import com.threerings.presents.dobj.InvocationNotificationEvent; +import com.threerings.presents.dobj.InvocationRequestEvent; +import com.threerings.presents.dobj.InvocationResponseEvent; +import com.threerings.presents.dobj.ObjectAccessException; +import com.threerings.presents.dobj.Subscriber; /** - * The invocation services provide client to server invocations (service - * requests) and server to client invocations (responses and - * notifications). Via this mechanism, the client can make requests of the - * server, be notified of its response and the server can asynchronously - * invoke code on the client. - * - *

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). - * - *

The client invocation director delivers invocation requests to the - * server invocation manager and maps the responses back to the proper - * response target objects when they arrive. It also maintains the mapping - * of invocation receivers that can receive asynchronous invocation - * notifications at any time from the server. + * Handles the client side management of the invocation services. */ public class InvocationDirector - implements MessageListener + implements EventListener { /** - * Initializes the invocation director. + * Initializes the invocation director. This is called when the client + * establishes a connection with the server. * * @param omgr the distributed object manager via which the invocation * manager will send and receive events. * @param cloid the oid of the object on which invocation * notifications as well as invocation responses will be received. - * @param imoid the oid of the object on the server to which to - * deliver invocation requests. * @param initListener a result listener that will be notified when * the invocation director is up and running (meaning it has * subscribed successfully to the client object and is ready to * process invocation requests); or when initialization has failed. */ - public void init (DObjectManager omgr, final int cloid, int imoid, + public void init (DObjectManager omgr, final int cloid, final ResultListener initListener) { + // keep this for later _omgr = omgr; - _imoid = imoid; - _cloid = cloid; // add ourselves as a subscriber to the client object _omgr.subscribeToObject(cloid, new Subscriber() { public void objectAvailable (DObject object) { + // keep a handle on this bad boy + _clobj = (ClientObject)object; + // add ourselves as a message listener - object.addListener(InvocationDirector.this); + _clobj.addListener(InvocationDirector.this); + + // assign a mapping to already registered receivers + assignReceiverIds(); // let the client know that we're ready to go now that // we've got our subscription to the client object @@ -83,224 +82,243 @@ public class InvocationDirector } /** - * Sends an invocation request to the server. If a response target is - * supplied, the response will be delivered to that object by calling - * a member function on it whose name is defined in the response - * generated by the invocation implementation. In general, this is a - * derivative of the invocation procedure name. For example, if the - * caller invoked a procedure named Switch, the response - * may be delivered via a call to the handleSwitchSuccess - * method on the response target object. The signature of that method - * would be defined by the arguments provided in the response message - * with the addition of a first argument which is the invocation - * identifier for this particular request (that is also returned by - * this function). - * - * @param module the name of the invocation module to use. - * @param procedure the name of the procedure within that module. - * @param args the arguments of the invocation. - * @param rsptarget the object that will receive the response, or null - * if no response is desired. - * - * @return a unique identifier associated with this invocation - * request. This identifier will be passed as the first argument to - * the response function. + * Clears out our session information. This is called when the client + * ends its session with the server. */ - public int invoke (String module, String procedure, Object[] args, - Object rsptarget) + public void cleanup () { - int invid = nextInvocationId(); + // wipe our client object, receiver mappings and listener mappings + _clobj = null; + _receivers.clear(); + _listeners.clear(); - // if null arguments were supplied, assume zero arguments - if (args == null) { - args = new Object[0]; + // also reset our counters + _requestId = 0; + _receiverId = 0; + } + + /** + * Registers an invocation notification receiver by way of its + * notification event decoder. + */ + public void registerReceiver (InvocationDecoder decoder) + { + // add the receiver to the list + _reclist.add(decoder); + + // if we're already online, assign a receiver id to this decoder + if (_clobj != null) { + assignReceiverId(decoder); + } + } + + /** + * Removes a receiver registration. + */ + public void unregisterReceiver (String receiverCode) + { + // remove the receiver from the list + for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) { + InvocationDecoder decoder = (InvocationDecoder)iter.next(); + if (decoder.getReceiverCode().equals(receiverCode)) { + iter.remove(); + } } - // we need an args array for a message that can contain the - // invocation names, an invocation id and the invocation arguments - Object[] iargs = new Object[args.length+3]; - iargs[0] = module; - iargs[1] = procedure; - iargs[2] = new Integer(invid); - System.arraycopy(args, 0, iargs, 3, args.length); - - // create a message event on the invocation manager object - MessageEvent event = new MessageEvent( - _imoid, InvocationObject.REQUEST_NAME, iargs); - - // if we have a response target, register that for later receipt - // of the response - if (rsptarget != null) { - _targets.put(invid, rsptarget); + // if we're logged on, clear out any receiver id mapping + if (_clobj != null) { + _clobj.removeFromReceivers(receiverCode); } + } + + /** + * Assigns a receiver id to this decoder and publishes it in the + * {@link ClientObject#receivers} field. + */ + protected void assignReceiverId (InvocationDecoder decoder) + { + Registration reg = new Registration( + decoder.getReceiverCode(), nextReceiverId()); + // stick the mapping into the client object + _clobj.addToReceivers(reg); + // and map the receiver in our receivers table + _receivers.put(reg.receiverId, decoder); + } + + /** + * Called when we log on; generates mappings for all receivers + * registered prior to logon. + */ + protected void assignReceiverIds () + { + // pack all the set add events into a single transaction + _clobj.startTransaction(); + try { + for (Iterator iter = _reclist.iterator(); iter.hasNext(); ) { + assignReceiverId((InvocationDecoder)iter.next()); + } + } finally { + _clobj.commitTransaction(); + } + } + + /** + * Requests that the specified invocation request be packaged up and + * sent to the supplied invocation oid. + */ + public void sendRequest ( + int invOid, int invCode, int methodId, Object[] args) + { + // configure any invocation listener marshallers among the + // arguments + int acount = args.length; + for (int ii = 0; ii < acount; ii++) { + Object arg = args[ii]; + if (arg instanceof ListenerMarshaller) { + ListenerMarshaller lm = (ListenerMarshaller)arg; + lm.callerOid = _clobj.getOid(); + lm.requestId = nextRequestId(); + // create a mapping for this marshaller so that we can + // properly dispatch responses sent to it + _listeners.put(lm.requestId, lm); + } + } + + // create an invocation request event + InvocationRequestEvent event = + new InvocationRequestEvent(invOid, invCode, methodId, args); // because invocation directors are used on the server, we set the // source oid here so that invocation requests are properly // attributed to the right client object when created by // server-side entities only sort of pretending to be a client - event.setSourceOid(_cloid); + event.setSourceOid(_clobj.getOid()); - // and finally ship off the invocation message +// Log.info("Sending invocation request " + event + "."); + + // now dispatch the event _omgr.postEvent(event); - - return invid; } /** - * Registers the supplied invocation receiver instance as the handler - * for all invocation notifications for the specified module. + * Process notification and response events arriving on user object. */ - public void registerReceiver (String module, InvocationReceiver receiver) + public void eventReceived (DEvent event) { - if (_receivers == null) { - _receivers = new HashMap(); - } - _receivers.put(module, receiver); - } + if (event instanceof InvocationResponseEvent) { + InvocationResponseEvent ire = (InvocationResponseEvent)event; + handleInvocationResponse( + ire.getRequestId(), ire.getMethodId(), ire.getArgs()); - /** - * Removes the registration for the supplied invocation receiver - * instance as the handler for invocation notifications for the - * specified module. - */ - public void unregisterReceiver (String module) - { - if (_receivers != null) { - _receivers.remove(module); + } else if (event instanceof InvocationNotificationEvent) { + InvocationNotificationEvent ine = + (InvocationNotificationEvent)event; + handleInvocationNotification( + ine.getReceiverId(), ine.getMethodId(), ine.getArgs()); } } /** - * Process incoming message requests on user object. + * Dispatches an invocation response. */ - public void messageReceived (MessageEvent event) + protected void handleInvocationResponse ( + int reqId, int methodId, Object[] args) { - String name = event.getName(); - if (name.equals(InvocationObject.RESPONSE_NAME)) { - handleInvocationResponse(event.getArgs()); - } else if (name.equals(InvocationObject.NOTIFICATION_NAME)) { - handleInvocationNotification(event.getArgs()); - } - } - - /** - * Processes an invocation response message. - */ - protected void handleInvocationResponse (Object[] args) - { - String name = (String)args[0]; - int invid = ((Integer)args[1]).intValue(); - - Object rsptarg = _targets.get(invid); - if (rsptarg == null) { - Log.warning("No target for invocation response " + - "[args=" + StringUtil.toString(args) + "]."); + // look up the invocation marshaller registered for that response + ListenerMarshaller listener = (ListenerMarshaller) + _listeners.remove(reqId); + if (listener == null) { + Log.warning("Received invocation response for which we have " + + "no registered listener [reqId=" + reqId + + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); return; } - // prune the invocation id and method arguments from the full - // message arguments - Object[] rargs = new Object[args.length-1]; - System.arraycopy(args, 1, rargs, 0, rargs.length); +// Log.info("Dispatching invocation response " + +// "[listener=" + listener + ", methId=" + methodId + +// ", args=" + StringUtil.toString(args) + "]."); - // and invoke the response method; we'd cache these but the key - // for the method would have to include all of the class names of - // all of the arguments and would probably be more expensive to - // create than just reflecting the method (we should really test - // this because that's half intuition and half wild-ass guess, but - // we're going with it for now) - String mname = "handle" + name; - Method rspmeth = ClassUtil.getMethod(mname, rsptarg, rargs); - if (rspmeth == null) { - Log.warning("Unable to resolve response method " + - "[target=" + rsptarg.getClass().getName() + - ", method=" + mname + - ", args=" + StringUtil.toString(rargs) + "]."); - return; - } - - // and invoke it + // dispatch the response try { -// Log.info("Invoking method [meth=" + -// rsptarg.getClass().getName() + "." + rspmeth.getName() + -// ", args=" + StringUtil.toString(rargs) + "]."); - rspmeth.invoke(rsptarg, rargs); - } catch (Exception e) { - Log.warning("Error invoking response target method " + - "[target=" + rsptarg + ", method=" + rspmeth + "]."); - Log.logStackTrace(e); + listener.dispatchResponse(methodId, args); + } catch (Throwable t) { + Log.warning("Invocation response listener choked " + + "[listener=" + listener + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + Log.logStackTrace(t); } } /** - * Processes an invocation notification message. + * Dispatches an invocation notification. */ - protected void handleInvocationNotification (Object[] args) + protected void handleInvocationNotification ( + int receiverId, int methodId, Object[] args) { - String module = (String)args[0]; - String proc = (String)args[1]; - - InvocationReceiver receiver = null; - if (_receivers != null) { - receiver = (InvocationReceiver)_receivers.get(module); - } - if (receiver == null) { - Log.warning("No receiver registered for notification " + - "[args=" + StringUtil.toString(args) + "]."); + // look up the decoder registered for this receiver + InvocationDecoder decoder = (InvocationDecoder) + _receivers.get(receiverId); + if (decoder == null) { + Log.warning("Received notification for which we have no " + + "registered receiver [recvId=" + receiverId + + ", methodId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); return; } - // prune the method arguments from the full message arguments - Object[] nargs = new Object[args.length-2]; - System.arraycopy(args, 2, nargs, 0, nargs.length); +// Log.info("Dispatching invocation notification " + +// "[receiver=" + decoder.receiver + ", methodId=" + methodId + +// ", args=" + StringUtil.toString(args) + "]."); - // and invoke the receiver method; we'd cache these but the key - // for the method would have to include all of the class names of - // all of the arguments and would probably be more expensive to - // create than just reflecting the method (we should really test - // this because that's half intuition and half wild-ass guess, but - // we're going with it for now) - String mname = "handle" + proc + "Notification"; - Method rspmeth = ClassUtil.getMethod(mname, receiver, nargs); - if (rspmeth == null) { - Log.warning("Unable to resolve receiver method " + - "[target=" + receiver.getClass().getName() + - ", method=" + mname + - ", args=" + StringUtil.toString(nargs) + "]."); - return; - } - - // and invoke it try { - rspmeth.invoke(receiver, nargs); - } catch (Exception e) { - Log.warning("Error invoking receiver method " + - "[receiver=" + receiver + ", method=" + rspmeth + "]."); - Log.logStackTrace(e); + decoder.dispatchNotification(methodId, args); + } catch (Throwable t) { + Log.warning("Invocation notification receiver choked " + + "[receiver=" + decoder.receiver + + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + Log.logStackTrace(t); } } - public synchronized int nextInvocationId () + /** + * Used to generate monotonically increasing invocation request ids. + */ + protected synchronized short nextRequestId () { - return _invocationId++; + return _requestId++; } - protected static class Response + /** + * Used to generate monotonically increasing invocation receiver ids. + */ + protected synchronized short nextReceiverId () { - public String name; - public Object target; - public Response (String name, Object target) - { - this.name = name; - this.target = target; - } + return _receiverId++; } + /** The distributed object manager with which we interact. */ protected DObjectManager _omgr; - protected int _imoid; - protected int _cloid; - protected int _invocationId; - protected HashIntMap _targets = new HashIntMap(); - protected HashMap _receivers = new HashMap(); + /** Our client object; invocation responses and notifications are + * received on this object. */ + protected ClientObject _clobj; + + /** Used to generate monotonically increasing request ids. */ + protected short _requestId; + + /** Used to generate monotonically increasing receiver ids. */ + protected short _receiverId; + + /** Used to keep track of invocation service listeners which will + * receive responses from invocation service requests. */ + protected HashIntMap _listeners = new HashIntMap(); + + /** Used to keep track of invocation notification receivers. */ + protected HashIntMap _receivers = new HashIntMap(); + + /** All registered receivers are maintained in a list so that we can + * assign receiver ids to them when we go online. */ + protected ArrayList _reclist = new ArrayList(); } diff --git a/src/java/com/threerings/presents/client/InvocationReceiver.java b/src/java/com/threerings/presents/client/InvocationReceiver.java index 3270f62cc..0a3e1c473 100644 --- a/src/java/com/threerings/presents/client/InvocationReceiver.java +++ b/src/java/com/threerings/presents/client/InvocationReceiver.java @@ -1,36 +1,70 @@ // -// $Id: InvocationReceiver.java,v 1.4 2001/10/11 04:07:52 mdb Exp $ +// $Id: InvocationReceiver.java,v 1.5 2002/08/14 19:07:54 mdb Exp $ package com.threerings.presents.client; +import com.threerings.presents.dobj.DSet; + /** - * Classes registered to process invocation notifications should implement - * the invocation receiver interface and register themselves with the - * invocation director. Because the invocation notification procedures are - * looked up using reflection, there are no methods to implement in the - * receiver interface, but it serves as a useful point for documentation - * and as a useful indicator that the class in question is serving as an - * invocation receiver. + * Invocation notification receipt interfaces should be defined as + * extending this interface. Actual notification receivers will implement + * the requisite receiver interface definition and register themselves + * with the {@link InvocationDirector} using the generated {@link + * InvocationDispatcher} class specific to the notification receiver + * interface in question. For example: * - *

Invocation notifications are identified by a module name and a - * procedure name. The module name identifies which invocation receiver - * instance will receive the notification. Receivers are registered with - * the invocation director as handling all notification procedures for a - * particular module. The notification procedure name is used to construct - * a method name which is then reflected and invoked. - * - *

The name construction is as follows: a notification message - * requesting the invocation of a procedure named Tell will - * result in a method named handleTellNotification being - * invoked on the invocation receiver instance. The signature of that - * method is defined by the arguments supplied with the invocation - * notification message. These arguments must always be of the same type - * and must exactly match the signature of the implementing method (with - * the standard reflection argument type conversion process taken into - * account). + *

+ * public class FooDirector implements FooReceiver
+ * {
+ *     public FooDirector (PresentsContext ctx)
+ *     {
+ *         InvocationDirector idir = ctx.getClient().getInvocationDirector();
+ *         idir.registerReceiver(new FooDispatcher(this));
+ *     }
+ * }
+ * 
* * @see InvocationDirector#registerReceiver */ public interface InvocationReceiver { + /** + * Used to maintain a registry of invocation receivers that can be + * used to convert (large) hash codes into (small) registration + * numbers. + */ + public static class Registration implements DSet.Entry + { + /** The unique hash code associated with this invocation receiver + * class. */ + public String receiverCode; + + /** The unique id assigned to this invocation receiver class at + * registration time. */ + public short receiverId; + + /** Creates and initializes a registration instance. */ + public Registration (String receiverCode, short receiverId) + { + this.receiverCode = receiverCode; + this.receiverId = receiverId; + } + + /** Creates a blank instance suitable for unserialization. */ + public Registration () + { + } + + // documentation inherited from interface + public Comparable getKey () + { + return receiverCode; + } + + /** Generates a string representation of this instance. */ + public String toString () + { + return "[" + receiverCode + " => " + receiverId + "]"; + } + } } diff --git a/src/java/com/threerings/presents/client/InvocationService.java b/src/java/com/threerings/presents/client/InvocationService.java new file mode 100644 index 000000000..cc8e3961c --- /dev/null +++ b/src/java/com/threerings/presents/client/InvocationService.java @@ -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: + * + *
+ * 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);
+ * }
+ * 
+ * + * From this interface, a LocationProvider 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: + * + *
+ * 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;
+ * }
+ * 
+ */ +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: + * + *
+     * // 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);
+     * }
+     * 
+ */ + 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); + } +} diff --git a/src/java/com/threerings/presents/client/LoggingListener.java b/src/java/com/threerings/presents/client/LoggingListener.java new file mode 100644 index 000000000..112c37e55 --- /dev/null +++ b/src/java/com/threerings/presents/client/LoggingListener.java @@ -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; +} diff --git a/src/java/com/threerings/presents/client/TimeBaseService.java b/src/java/com/threerings/presents/client/TimeBaseService.java index 7d4d082a7..7728484d3 100644 --- a/src/java/com/threerings/presents/client/TimeBaseService.java +++ b/src/java/com/threerings/presents/client/TimeBaseService.java @@ -1,36 +1,32 @@ // -// $Id: TimeBaseService.java,v 1.2 2002/05/29 18:44:36 mdb Exp $ +// $Id: TimeBaseService.java,v 1.3 2002/08/14 19:07:54 mdb Exp $ package com.threerings.presents.client; -import com.threerings.presents.Log; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; -import com.threerings.presents.data.TimeBaseCodes; +import com.threerings.presents.client.InvocationService; /** * Provides a means by which to obtain access to a time base object which * can be used to convert delta times into absolute times. */ -public class TimeBaseService - implements TimeBaseCodes +public interface TimeBaseService extends InvocationService { /** - * Requests the oid of the specified time base object be returned. The - * supplied response target must provide two methods to handle the - * responses generated by this request: - * - *
-     *   public void handleTimeOidResponse (int invid, int timeOid);
-     *   public void handleGetTimeOidFailed (int invid, String reason);
-     * 
+ * Used to communicated the result of a {@link #getTimeOid} request. */ - public static void getTimeOid ( - Client client, String timeBase, Object rsptarget) + public static interface GotTimeBaseListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - invdir.invoke(MODULE_NAME, GET_TIME_OID_REQUEST, - new Object[] { timeBase }, rsptarget); - Log.debug("Sent getTimeOid request [timeBase=" + timeBase + "]."); + /** + * Communicates the result of a successful {@link #getTimeOid} + * request. + */ + public void gotTimeOid (int timeOid); } + + /** + * Requests the oid of the specified time base object be fetched. + */ + public void getTimeOid ( + Client client, String timeBase, GotTimeBaseListener listener); } diff --git a/src/java/com/threerings/presents/data/ClientObject.dobj b/src/java/com/threerings/presents/data/ClientObject.dobj new file mode 100644 index 000000000..b124e5252 --- /dev/null +++ b/src/java/com/threerings/presents/data/ClientObject.dobj @@ -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() + ")"; + } +} diff --git a/src/java/com/threerings/presents/data/ClientObject.java b/src/java/com/threerings/presents/data/ClientObject.java index 824a0a461..366bfde18 100644 --- a/src/java/com/threerings/presents/data/ClientObject.java +++ b/src/java/com/threerings/presents/data/ClientObject.java @@ -1,9 +1,10 @@ // -// $Id: ClientObject.java,v 1.2 2001/10/11 04:07:52 mdb Exp $ +// $Id: ClientObject.java,v 1.3 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 @@ -13,4 +14,64 @@ import com.threerings.presents.dobj.DObject; */ public class ClientObject extends DObject { + /** The field name of the receivers field. */ + public static final String RECEIVERS = "receivers"; + + /** 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() + ")"; + } + + /** + * Requests that the specified entry be added to the + * receivers set. The set will not change until the event is + * actually propagated through the system. + */ + public void addToReceivers (DSet.Entry elem) + { + requestEntryAdd(RECEIVERS, elem); + } + + /** + * Requests that the entry matching the supplied key be removed from + * the receivers set. The set will not change until the + * event is actually propagated through the system. + */ + public void removeFromReceivers (Object key) + { + requestEntryRemove(RECEIVERS, key); + } + + /** + * Requests that the specified entry be updated in the + * receivers set. The set will not change until the event is + * actually propagated through the system. + */ + public void updateReceivers (DSet.Entry elem) + { + requestEntryUpdate(RECEIVERS, elem); + } + + /** + * Requests that the receivers field be set to the + * specified value. Generally one only adds, updates and removes + * entries of a distributed set, but certain situations call for a + * complete replacement of the set value. The local value will be + * updated immediately and an event will be propagated through the + * system to notify all listeners that the attribute did + * change. Proxied copies of this object (on clients) will apply the + * value change when they received the attribute changed notification. + */ + public void setReceivers (DSet receivers) + { + this.receivers = receivers; + requestAttributeChange(RECEIVERS, receivers); + } } diff --git a/src/java/com/threerings/presents/data/InvocationMarshaller.java b/src/java/com/threerings/presents/data/InvocationMarshaller.java new file mode 100644 index 000000000..b2b140c3e --- /dev/null +++ b/src/java/com/threerings/presents/data/InvocationMarshaller.java @@ -0,0 +1,156 @@ +// +// $Id: InvocationMarshaller.java,v 1.1 2002/08/14 19:07:55 mdb Exp $ + +package com.threerings.presents.data; + +import java.io.IOException; + +import com.samskivert.util.StringUtil; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; + +import com.threerings.presents.Log; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.InvocationService; + +import com.threerings.presents.dobj.DObjectManager; +import com.threerings.presents.dobj.InvocationResponseEvent; + +/** + * Provides a base from which all invocation service marshallers extend. + * Handles functionality common to all marshallers. + */ +public class InvocationMarshaller + implements Streamable, InvocationService +{ + /** + * Provides a base from which invocation listener marshallers extend. + */ + public static class ListenerMarshaller + implements Streamable, InvocationListener + { + /** The method id used to dispatch a {@link #requestFailed} + * response. */ + public static final int REQUEST_FAILED_RSPID = 0; + + /** The oid of the invocation service requester. */ + public int callerOid; + + /** The request id associated with this listener. */ + public short requestId; + + /** The actual invocation listener associated with this + * marshalling listener. This is only valid on the client. */ + public transient InvocationListener listener; + + /** The distributed object manager to use when dispatching proxied + * responses. This is only valid on the server. */ + public transient DObjectManager omgr; + + // documentation inherited from interface + public void requestFailed (String cause) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, REQUEST_FAILED_RSPID, + new Object[] { cause })); + } + + /** + * Called to dispatch an invocation response to our target + * listener. + */ + public void dispatchResponse (int methodId, Object[] args) + { + if (methodId == REQUEST_FAILED_RSPID) { + listener.requestFailed((String)args[0]); + + } else { + Log.warning("Requested to dispatch unknown invocation " + + "response [listener=" + listener + + ", methodId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + } + } + + /** + * Generates a string representation of this instance. + */ + public String toString () + { + return "[callerOid=" + callerOid + ", reqId=" + requestId + + ", type=" + getClass().getName() + "]"; + } + } + + /** + * Initializes this invocation marshaller instance with the requisite + * information to allow it to operate in the wide world. This is + * called by the invocation manager when an invocation provider is + * registered and should not be called otherwise. + */ + public void init (int invOid, int invCode) + { + _invOid = invOid; + _invCode = invCode; + } + + /** + * Sets the invocation oid to which this marshaller should send its + * invocation service requests. This is called by the invocation + * manager in certain initialization circumstances. + */ + public void setInvocationOid (int invOid) + { + _invOid = invOid; + } + + /** + * Called by generated invocation marshaller code; packages up and + * sends the specified invocation service request. + */ + protected void sendRequest (Client client, int methodId, Object[] args) + { + client.getInvocationDirector().sendRequest( + _invOid, _invCode, methodId, args); + } + + /** + * Writes this instance to the supplied output stream. + */ + public void writeObject (ObjectOutputStream out) + throws IOException + { + out.writeInt(_invOid); + out.writeShort(_invCode); + } + + /** + * Reads this instance from the supplied input stream. + */ + public void readObject (ObjectInputStream in) + throws IOException + { + _invOid = in.readInt(); + _invCode = in.readShort(); + } + + /** + * Generates a string representation of this instance. + */ + public String toString () + { + return "[invOid=" + _invOid + ", code=" + _invCode + + ", type=" + getClass().getName() + "]"; + } + + /** The oid of the invocation object, where invocation service + * requests are sent. */ + protected int _invOid; + + /** The invocation service code assigned to this service when it was + * registered on the server. */ + protected int _invCode; +} diff --git a/src/java/com/threerings/presents/data/InvocationObject.java b/src/java/com/threerings/presents/data/InvocationObject.java index 2f9af6d5d..c01e2e060 100644 --- a/src/java/com/threerings/presents/data/InvocationObject.java +++ b/src/java/com/threerings/presents/data/InvocationObject.java @@ -1,5 +1,5 @@ // -// $Id: InvocationObject.java,v 1.3 2001/10/11 04:07:52 mdb Exp $ +// $Id: InvocationObject.java,v 1.4 2002/08/14 19:07:55 mdb Exp $ package com.threerings.presents.data; @@ -13,21 +13,4 @@ import com.threerings.presents.dobj.DObject; */ public class InvocationObject extends DObject { - /** - * This constant is used to identify invocation requests sent to the - * server. - */ - public static final String REQUEST_NAME = "invreq"; - - /** - * This constant is used to identify invocation responses sent to the - * client. - */ - public static final String RESPONSE_NAME = "invrsp"; - - /** - * This constant is used to identify invocation notifications sent to - * the client. - */ - public static final String NOTIFICATION_NAME = "invnot"; } diff --git a/src/java/com/threerings/presents/data/TimeBaseCodes.java b/src/java/com/threerings/presents/data/TimeBaseCodes.java index 3f48a6d1f..2c6d54935 100644 --- a/src/java/com/threerings/presents/data/TimeBaseCodes.java +++ b/src/java/com/threerings/presents/data/TimeBaseCodes.java @@ -1,23 +1,13 @@ // -// $Id: TimeBaseCodes.java,v 1.1 2002/05/28 23:14:06 mdb Exp $ +// $Id: TimeBaseCodes.java,v 1.2 2002/08/14 19:07:55 mdb Exp $ package com.threerings.presents.data; /** * Codes and constants relating to the Presents time base services. */ -public interface TimeBaseCodes +public interface TimeBaseCodes extends InvocationCodes { - /** The module name for the time services. */ - public static final String MODULE_NAME = "time"; - - /** The message identifier for a request to obtain a particular time - * object. */ - public static final String GET_TIME_OID_REQUEST = "GetTimeOid"; - - /** A response generated for a successful getTimeOid request. */ - public static final String TIME_OID_RESPONSE = "TimeOid"; - /** An error response generated for GetTimeOid requests. */ public static final String NO_SUCH_TIME_BASE = "m.no_such_time_base"; } diff --git a/src/java/com/threerings/presents/data/TimeBaseMarshaller.java b/src/java/com/threerings/presents/data/TimeBaseMarshaller.java new file mode 100644 index 000000000..6f55fdfbf --- /dev/null +++ b/src/java/com/threerings/presents/data/TimeBaseMarshaller.java @@ -0,0 +1,67 @@ +// +// $Id: TimeBaseMarshaller.java,v 1.1 2002/08/14 19:07:55 mdb Exp $ + +package com.threerings.presents.data; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.TimeBaseService; +import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; + +/** + * Provides the implementation of the {@link TimeBaseService} 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 TimeBaseMarshaller extends InvocationMarshaller + implements TimeBaseService +{ + // documentation inherited + public static class GotTimeBaseMarshaller extends ListenerMarshaller + implements GotTimeBaseListener + { + /** The method id used to dispatch {@link #gotTimeOid} + * responses. */ + public static final int GOT_TIME_OID = 0; + + // documentation inherited from interface + public void gotTimeOid (int arg1) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, GOT_TIME_OID, + new Object[] { new Integer(arg1) })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case GOT_TIME_OID: + ((GotTimeBaseListener)listener).gotTimeOid( + ((Integer)args[0]).intValue()); + return; + + default: + super.dispatchResponse(methodId, args); + } + } + } + + /** The method id used to dispatch {@link #getTimeOid} requests. */ + public static final int GET_TIME_OID = 1; + + // documentation inherited from interface + public void getTimeOid (Client arg1, String arg2, GotTimeBaseListener arg3) + { + GotTimeBaseMarshaller listener3 = new GotTimeBaseMarshaller(); + listener3.listener = arg3; + sendRequest(arg1, GET_TIME_OID, new Object[] { + arg2, listener3 + }); + } + + // Class file generated on 00:26:01 08/11/02. +} diff --git a/src/java/com/threerings/presents/dobj/DObject.java b/src/java/com/threerings/presents/dobj/DObject.java index 99068e1b1..bec562d65 100644 --- a/src/java/com/threerings/presents/dobj/DObject.java +++ b/src/java/com/threerings/presents/dobj/DObject.java @@ -1,5 +1,5 @@ // -// $Id: DObject.java,v 1.46 2002/07/23 05:52:48 mdb Exp $ +// $Id: DObject.java,v 1.47 2002/08/14 19:07:55 mdb Exp $ package com.threerings.presents.dobj; @@ -473,6 +473,14 @@ public class DObject implements Streamable _oid = oid; } + /** + * Generates a concise string representation of this object. + */ + public String which () + { + return "[" + getClass().getName() + " " + getOid() + "]"; + } + /** * Generates a string representation of this object. */ diff --git a/src/java/com/threerings/presents/dobj/DSet.java b/src/java/com/threerings/presents/dobj/DSet.java index a2c3270c5..8767e8ddb 100644 --- a/src/java/com/threerings/presents/dobj/DSet.java +++ b/src/java/com/threerings/presents/dobj/DSet.java @@ -1,11 +1,13 @@ // -// $Id: DSet.java,v 1.17 2002/07/23 05:52:48 mdb Exp $ +// $Id: DSet.java,v 1.18 2002/08/14 19:07:55 mdb Exp $ package com.threerings.presents.dobj; import java.io.IOException; +import java.util.Comparator; import java.util.Iterator; +import com.samskivert.util.ArrayUtil; import com.samskivert.util.StringUtil; import com.threerings.io.ObjectInputStream; @@ -45,7 +47,7 @@ public class DSet * its uniqueness in the set. See the {@link DSet} class * documentation for further information. */ - public Object getKey (); + public Comparable getKey (); } /** @@ -61,17 +63,8 @@ public class DSet */ public DSet (Iterator source) { - for (int index = 0; source.hasNext(); index++) { - Entry elem = (Entry)source.next(); - - // expand the array if necessary - if (index >= _entries.length) { - expand(index); - } - - // insert the item - _entries[index] = elem; - _size++; + while (source.hasNext()) { + add((Entry)source.next()); } } @@ -140,37 +133,16 @@ public class DSet public Iterator entries () { return new Iterator() { - public boolean hasNext () - { - // we need to scan to the next entry the first time - if (_index < 0) { - scanToNext(); - } - return (_index < _entries.length); + public boolean hasNext () { + return (_index < _size); } - - public Object next () - { - Object val = _entries[_index]; - scanToNext(); - return val; + public Object next () { + return _entries[_index++]; } - - public void remove () - { + public void remove () { throw new UnsupportedOperationException(); } - - protected void scanToNext () - { - for (_index++; _index < _entries.length; _index++) { - if (_entries[_index] != null) { - return; - } - } - } - - int _index = -1; + protected int _index = 0; }; } @@ -185,32 +157,44 @@ public class DSet */ protected boolean add (Entry elem) { - Object key = elem.getKey(); + // determine where we'll be adding the new element + int eidx = ArrayUtil.binarySearch( + _entries, 0, _size, elem, ENTRY_COMP); + + // if the element is already in the set, bail now + if (eidx >= 0) { + return false; + } + + // convert the index into happy positive land + eidx = (eidx+1)*-1; + + // expand our entries array if necessary int elength = _entries.length; - int index = elength; - - // scan the array looking for a slot and/or the entry already in - // the set - for (int i = 0; i < elength; i++) { - Entry el = _entries[i]; - // the array may be sparse - if (el == null) { - if (index == elength) { - index = i; - } - } else if (el.getKey().equals(key)) { - return false; + if (_size == elength-1) { + // sanity check + if (elength > 2048) { + Log.warning("Requested to expand to questionably large size " + + "[length=" + elength + "]."); + Thread.dumpStack(); } + + // create a new array and copy our data into it + Entry[] elems = new Entry[elength*2]; + System.arraycopy(_entries, 0, elems, 0, elength); + _entries = elems; } - // expand the array if necessary - if (index >= _entries.length) { - expand(index); + // if the entry doesn't go at the end, shift the elements down to + // accomodate it + if (eidx < _size) { + System.arraycopy(_entries, eidx, _entries, eidx+1, _size-eidx); } - // insert the item - _entries[index] = elem; + // stuff the entry into the array and note that we're bigger + _entries[eidx] = elem; _size++; + return true; } @@ -239,17 +223,20 @@ public class DSet */ protected boolean removeKey (Object key) { - // scan the array looking for a matching entry - int elength = _entries.length; - for (int i = 0; i < elength; i++) { - Entry el = _entries[i]; - if (el != null && el.getKey().equals(key)) { - _entries[i] = null; - _size--; - return true; - } + // look up this entry's position in our set + int eidx = ArrayUtil.binarySearch( + _entries, 0, _size, key, ENTRY_COMP); + + // if we found it, remove it + if (eidx >= 0) { + // shift the remaining elements down + System.arraycopy(_entries, eidx+1, _entries, eidx, _size-eidx-1); + _entries[--_size] = null; + return true; + + } else { + return false; } - return false; } /** @@ -264,19 +251,17 @@ public class DSet */ protected boolean update (Entry elem) { - Object key = elem.getKey(); + // look up this entry's position in our set + int eidx = ArrayUtil.binarySearch( + _entries, 0, _size, elem, ENTRY_COMP); - // scan the array looking for a matching entry - int elength = _entries.length; - for (int i = 0; i < elength; i++) { - Entry el = _entries[i]; - if (el != null && el.getKey().equals(key)) { - _entries[i] = elem; - return true; - } + // if we found it, update it + if (eidx >= 0) { + _entries[eidx] = elem; + return true; + } else { + return false; } - - return false; } /** @@ -340,35 +325,6 @@ public class DSet return buf.toString(); } - protected void expand (int index) - { - // sanity check - if (index < 0 || index > Short.MAX_VALUE) { - Log.warning("Requested to expand to accomodate bogus index! " + - "[index=" + index + "]."); - Thread.dumpStack(); - index = 0; - } - - // increase our length in powers of two until we're big enough - int tlength = _entries.length; - while (index >= tlength) { - tlength *= 2; - } - - // further sanity checks - if (tlength > 4096) { - Log.warning("Requested to expand to questionably large size " + - "[index=" + index + ", tlength=" + tlength + "]."); - Thread.dumpStack(); - } - - // create a new array and copy our data into it - Entry[] elems = new Entry[tlength]; - System.arraycopy(_entries, 0, elems, 0, _entries.length); - _entries = elems; - } - /** The entries of the set (in a sparse array). */ protected Entry[] _entries = new Entry[INITIAL_CAPACITY]; @@ -377,4 +333,21 @@ public class DSet /** The default capacity of a set instance. */ protected static final int INITIAL_CAPACITY = 2; + + /** Used for lookups and to keep the set contents sorted on + * insertions. */ + protected static Comparator ENTRY_COMP = new Comparator() { + public int compare (Object o1, Object o2) { + Comparable c1 = (o1 instanceof Entry) ? + ((Entry)o1).getKey() : (Comparable)o1; + Comparable c2 = (o2 instanceof Entry) ? + ((Entry)o2).getKey() : (Comparable)o2; + return c1.compareTo(c2); + } + + public boolean equals (Object obj) { + // we don't care about comparing comparators + return (obj == this); + } + }; } diff --git a/src/java/com/threerings/presents/dobj/InvocationNotificationEvent.java b/src/java/com/threerings/presents/dobj/InvocationNotificationEvent.java new file mode 100644 index 000000000..dd0df1df3 --- /dev/null +++ b/src/java/com/threerings/presents/dobj/InvocationNotificationEvent.java @@ -0,0 +1,135 @@ +// +// $Id: InvocationNotificationEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $ + +package com.threerings.presents.dobj; + +import java.io.IOException; + +import com.samskivert.util.StringUtil; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; + +/** + * Used to dispatch an invocation notification from the server to a + * client. + * + * @see DObjectManager#postEvent + */ +public class InvocationNotificationEvent extends DEvent +{ + /** + * Constructs a new invocation notification event on the specified + * target object with the supplied receiver id, method id and + * arguments. + * + * @param targetOid the object id of the object on which the event is + * to be dispatched. + * @param receiverId identifies the receiver to which this notification + * is being dispatched. + * @param methodId the id of the method to be invoked. + * @param args the arguments for the method. This array should contain + * only values of valid distributed object types. + */ + public InvocationNotificationEvent ( + int targetOid, short receiverId, int methodId, Object[] args) + { + super(targetOid); + _receiverId = receiverId; + _methodId = (byte)methodId; + _args = args; + } + + /** + * Constructs a blank instance of this event in preparation for + * unserialization from the network. + */ + public InvocationNotificationEvent () + { + } + + /** + * Returns the receiver id associated with this notification. + */ + public int getReceiverId () + { + return _receiverId; + } + + /** + * Returns the id of the method associated with this notification. + */ + public int getMethodId () + { + return _methodId; + } + + /** + * Returns the arguments associated with this notification. + */ + public Object[] getArgs () + { + return _args; + } + + /** + * Applies this attribute change to the object. + */ + public boolean applyToObject (DObject target) + throws ObjectAccessException + { + // nothing to do here + return true; + } + + /** + * Writes our custom streamable fields. + */ + public void writeObject (ObjectOutputStream out) + throws IOException + { + super.writeObject(out); + out.writeShort(_receiverId); + out.writeByte(_methodId); + out.writeObject(_args); + } + + /** + * Reads our custom streamable fields. + */ + public void readObject (ObjectInputStream in) + throws IOException, ClassNotFoundException + { + super.readObject(in); + _receiverId = in.readShort(); + _methodId = in.readByte(); + _args = (Object[])in.readObject(); + } + + // documentation inherited + protected void notifyListener (Object listener) + { + // nothing to do here + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + buf.append("INOT:"); + super.toString(buf); + buf.append(", rcvId=").append(_receiverId); + buf.append(", methodId=").append(_methodId); + buf.append(", args=").append(StringUtil.toString(_args)); + } + + /** Identifies the receiver to which this notification is being + * dispatched. */ + protected short _receiverId; + + /** The id of the receiver method being invoked. */ + protected byte _methodId; + + /** The arguments to the receiver method being invoked. */ + protected Object[] _args; +} diff --git a/src/java/com/threerings/presents/dobj/InvocationRequestEvent.java b/src/java/com/threerings/presents/dobj/InvocationRequestEvent.java new file mode 100644 index 000000000..66cd81c7a --- /dev/null +++ b/src/java/com/threerings/presents/dobj/InvocationRequestEvent.java @@ -0,0 +1,132 @@ +// +// $Id: InvocationRequestEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $ + +package com.threerings.presents.dobj; + +import java.io.IOException; + +import com.samskivert.util.StringUtil; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; + +/** + * Used to dispatch an invocation request from the client to the server. + * + * @see DObjectManager#postEvent + */ +public class InvocationRequestEvent extends DEvent +{ + /** + * Constructs a new invocation request event on the specified target + * object with the supplied code, method and arguments. + * + * @param targetOid the object id of the object on which the event is + * to be dispatched. + * @param invCode the invocation provider identification code. + * @param methodId the id of the method to be invoked. + * @param args the arguments for the method. This array should contain + * only values of valid distributed object types. + */ + public InvocationRequestEvent ( + int targetOid, int invCode, int methodId, Object[] args) + { + super(targetOid); + _invCode = invCode; + _methodId = (byte)methodId; + _args = args; + } + + /** + * Constructs a blank instance of this event in preparation for + * unserialization from the network. + */ + public InvocationRequestEvent () + { + } + + /** + * Returns the invocation code associated with this request. + */ + public int getInvCode () + { + return _invCode; + } + + /** + * Returns the id of the method associated with this request. + */ + public int getMethodId () + { + return _methodId; + } + + /** + * Returns the arguments associated with this request. + */ + public Object[] getArgs () + { + return _args; + } + + /** + * Applies this attribute change to the object. + */ + public boolean applyToObject (DObject target) + throws ObjectAccessException + { + // nothing to do here + return true; + } + + /** + * Writes our custom streamable fields. + */ + public void writeObject (ObjectOutputStream out) + throws IOException + { + super.writeObject(out); + out.writeInt(_invCode); + out.writeByte(_methodId); + out.writeObject(_args); + } + + /** + * Reads our custom streamable fields. + */ + public void readObject (ObjectInputStream in) + throws IOException, ClassNotFoundException + { + super.readObject(in); + _invCode = in.readInt(); + _methodId = in.readByte(); + _args = (Object[])in.readObject(); + } + + // documentation inherited + protected void notifyListener (Object listener) + { + // nothing to do here + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + buf.append("IREQ:"); + super.toString(buf); + buf.append(", code=").append(_invCode); + buf.append(", methodId=").append(_methodId); + buf.append(", args=").append(StringUtil.toString(_args)); + } + + /** The code identifying which invocation provider to which this + * request is directed. */ + protected int _invCode; + + /** The id of the method being invoked. */ + protected byte _methodId; + + /** The arguments to the method being invoked. */ + protected Object[] _args; +} diff --git a/src/java/com/threerings/presents/dobj/InvocationResponseEvent.java b/src/java/com/threerings/presents/dobj/InvocationResponseEvent.java new file mode 100644 index 000000000..bf5e40aaf --- /dev/null +++ b/src/java/com/threerings/presents/dobj/InvocationResponseEvent.java @@ -0,0 +1,131 @@ +// +// $Id: InvocationResponseEvent.java,v 1.1 2002/08/14 19:07:55 mdb Exp $ + +package com.threerings.presents.dobj; + +import java.io.IOException; + +import com.samskivert.util.StringUtil; + +import com.threerings.io.ObjectInputStream; +import com.threerings.io.ObjectOutputStream; +import com.threerings.io.Streamable; + +/** + * Used to dispatch an invocation response from the server to the client. + * + * @see DObjectManager#postEvent + */ +public class InvocationResponseEvent extends DEvent +{ + /** + * Constructs a new invocation response event on the specified target + * object with the supplied code, method and arguments. + * + * @param targetOid the object id of the object on which the event is + * to be dispatched. + * @param requestId the id of the request to which we are responding. + * @param methodId the method to be invoked. + * @param args the arguments for the method. This array should contain + * only values of valid distributed object types. + */ + public InvocationResponseEvent ( + int targetOid, int requestId, int methodId, Object[] args) + { + super(targetOid); + _requestId = (short)requestId; + _methodId = (byte)methodId; + _args = args; + } + + /** + * Constructs a blank instance of this event in preparation for + * unserialization from the network. + */ + public InvocationResponseEvent () + { + } + + /** + * Returns the invocation request id associated with this response. + */ + public int getRequestId () + { + return _requestId; + } + + /** + * Returns the method associated with this response. + */ + public int getMethodId () + { + return _methodId; + } + + /** + * Returns the arguments associated with this response. + */ + public Object[] getArgs () + { + return _args; + } + + /** + * Applies this attribute change to the object. + */ + public boolean applyToObject (DObject target) + throws ObjectAccessException + { + // nothing to do here + return true; + } + + /** + * Writes our custom streamable fields. + */ + public void writeObject (ObjectOutputStream out) + throws IOException + { + super.writeObject(out); + out.writeShort(_requestId); + out.writeByte(_methodId); + out.writeObject(_args); + } + + /** + * Reads our custom streamable fields. + */ + public void readObject (ObjectInputStream in) + throws IOException, ClassNotFoundException + { + super.readObject(in); + _requestId = in.readShort(); + _methodId = in.readByte(); + _args = (Object[])in.readObject(); + } + + // documentation inherited + protected void notifyListener (Object listener) + { + // nothing to do here + } + + // documentation inherited + protected void toString (StringBuffer buf) + { + buf.append("IRSP:"); + super.toString(buf); + buf.append(", reqid=").append(_requestId); + buf.append(", methodId=").append(_methodId); + buf.append(", args=").append(StringUtil.toString(_args)); + } + + /** The id of the request with which this response is associated. */ + protected short _requestId; + + /** The id of the method being invoked. */ + protected byte _methodId; + + /** The arguments to the method being invoked. */ + protected Object[] _args; +} diff --git a/src/java/com/threerings/presents/net/BootstrapData.java b/src/java/com/threerings/presents/net/BootstrapData.java index c68ff510c..ba7b34208 100644 --- a/src/java/com/threerings/presents/net/BootstrapData.java +++ b/src/java/com/threerings/presents/net/BootstrapData.java @@ -1,9 +1,10 @@ // -// $Id: BootstrapData.java,v 1.6 2002/05/28 22:54:43 mdb Exp $ +// $Id: BootstrapData.java,v 1.7 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.net; import com.threerings.presents.dobj.DObject; +import com.threerings.util.StreamableArrayList; /** * A BootstrapData object is communicated back to the client @@ -16,6 +17,6 @@ public class BootstrapData extends DObject /** The oid of this client's associated distributed object. */ public int clientOid; - /** The oid to which to send invocation requests. */ - public int invOid; + /** A list of handles to invocation services. */ + public StreamableArrayList services; } diff --git a/src/java/com/threerings/presents/server/InvocationDispatcher.java b/src/java/com/threerings/presents/server/InvocationDispatcher.java new file mode 100644 index 000000000..8672faac1 --- /dev/null +++ b/src/java/com/threerings/presents/server/InvocationDispatcher.java @@ -0,0 +1,40 @@ +// +// $Id: InvocationDispatcher.java,v 1.1 2002/08/14 19:07:56 mdb Exp $ + +package com.threerings.presents.server; + +import com.samskivert.util.StringUtil; + +import com.threerings.presents.Log; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationMarshaller; + +/** + * Provides the base class via which invocation service requests are + * dispatched. + */ +public abstract class InvocationDispatcher +{ + /** The invocation provider for whom we're dispatching. */ + public InvocationProvider provider; + + /** + * Creates an instance of the appropriate {@link InvocationMarshaller} + * derived class for use with this dispatcher. + */ + public abstract InvocationMarshaller createMarshaller (); + + /** + * Dispatches the specified method to our provider. + */ + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + Log.warning("Requested to dispatch unknown method " + + "[provider=" + provider + + ", sourceOid=" + source.getOid() + + ", methodId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + } +} diff --git a/src/java/com/threerings/presents/server/InvocationException.java b/src/java/com/threerings/presents/server/InvocationException.java new file mode 100644 index 000000000..5ef63a11c --- /dev/null +++ b/src/java/com/threerings/presents/server/InvocationException.java @@ -0,0 +1,19 @@ +// +// $Id: InvocationException.java,v 1.1 2002/08/14 19:07:56 mdb Exp $ + +package com.threerings.presents.server; + +/** + * Used to report failures when executing service requests. + */ +public class InvocationException extends Exception +{ + /** + * Constructs an invocation exception with the supplied cause code + * string. + */ + public InvocationException (String cause) + { + super(cause); + } +} diff --git a/src/java/com/threerings/presents/server/InvocationManager.java b/src/java/com/threerings/presents/server/InvocationManager.java index 478b8e8cf..e1797d954 100644 --- a/src/java/com/threerings/presents/server/InvocationManager.java +++ b/src/java/com/threerings/presents/server/InvocationManager.java @@ -1,19 +1,30 @@ // -// $Id: InvocationManager.java,v 1.11 2002/04/17 18:20:04 mdb Exp $ +// $Id: InvocationManager.java,v 1.12 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.server; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.HashMap; - +import java.util.ArrayList; +import com.samskivert.util.HashIntMap; import com.samskivert.util.StringUtil; +import com.threerings.io.Streamable; +import com.threerings.util.StreamableArrayList; + import com.threerings.presents.Log; -import com.threerings.presents.dobj.*; import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationMarshaller.ListenerMarshaller; +import com.threerings.presents.data.InvocationMarshaller; import com.threerings.presents.data.InvocationObject; -import com.threerings.presents.util.ClassUtil; + +import com.threerings.presents.dobj.DEvent; +import com.threerings.presents.dobj.DObject; +import com.threerings.presents.dobj.EventListener; +import com.threerings.presents.dobj.InvocationRequestEvent; +import com.threerings.presents.dobj.MessageEvent; +import com.threerings.presents.dobj.ObjectAccessException; +import com.threerings.presents.dobj.RootDObjectManager; +import com.threerings.presents.dobj.Subscriber; +import com.threerings.presents.dobj.Subscriber; /** * The invocation services provide client to server invocations (service @@ -23,11 +34,12 @@ import com.threerings.presents.util.ClassUtil; * invoke code on the client. * *

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). + * and take arguments. All arguments must be {@link Streamable} objects, + * primitive types, or String objects. All arguments are passed by value + * (by serializing and unserializing the arguments); 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). * *

The server invocation manager listens for invocation requests from * the client and passes them on to the invocation provider registered for @@ -36,9 +48,19 @@ import com.threerings.presents.util.ClassUtil; * the client. */ public class InvocationManager - implements Subscriber, MessageListener + implements Subscriber, EventListener { - public InvocationManager (DObjectManager omgr) + /** The list of services that are to be provided to clients at boot + * time. Don't mess with this list! */ + public StreamableArrayList bootlist = new StreamableArrayList(); + + /** + * Constructs an invocation manager which will use the supplied + * distributed object manager to operate its invocation + * services. Generally only one invocation manager should be + * operational in a particular system. + */ + public InvocationManager (RootDObjectManager omgr) { _omgr = omgr; @@ -52,45 +74,42 @@ public class InvocationManager } /** - * Registers the supplied invocation provider instance as the handler - * for all invocation requests for the specified module. - */ - public void registerProvider (String module, InvocationProvider provider) - { - _providers.put(module, provider); - } - - /** - * Delivers an invocation notification to the specified client. The - * module argument selects which - * InvocationReceiver will be invoked and the - * procedure argument indicates which method will be - * invoked on that receiver. + * Registers the supplied invocation dispatcher, returning a + * marshaller that can be used to send requests to the provider for + * whom the dispatcher is proxying. * - *

The method is constructed as follows: a procedure name of - * Tell will result in a method call to - * handleTellNotification. The arguments provided with - * the notification define the necessary signature of that method, - * according to the argument conversion rules defined by the - * reflection services (Integer is converted to - * int, etc.). + * @param dispatcher the dispatcher to be registered. + * @param bootstrap if true, the service instance will be added to the + * list of invocation service objects provided to the client in the + * bootstrap data. */ - public void sendNotification ( - int cloid, String module, String procedure, Object[] args) + public InvocationMarshaller registerDispatcher ( + InvocationDispatcher dispatcher, boolean bootstrap) { - // package up the arguments - int alength = (args != null) ? args.length : 0; - Object[] nargs = new Object[alength + 2]; - nargs[0] = module; - nargs[1] = procedure; - if (args != null) { - System.arraycopy(args, 0, nargs, 2, alength); + // get the next invocation code + int invCode = nextInvCode(); + + // create the marshaller and initialize it + InvocationMarshaller marsh = dispatcher.createMarshaller(); + marsh.init(_invoid, invCode); + + // if we haven't yet finished our own initialization, we need to + // throw this dispatcher on a queue so that we can fill in its + // invocation oid when we know what it should be + if (_invoid == -1) { + _lateInitQueue.add(marsh); } - // construct a message event and deliver it - MessageEvent nevt = new MessageEvent( - cloid, InvocationObject.NOTIFICATION_NAME, nargs); - PresentsServer.omgr.postEvent(nevt); + // register the dispatcher + _dispatchers.put(invCode, dispatcher); + + // if it's a bootstrap service, slap it in the list + if (bootstrap) { + bootlist.add(marsh); + } + +// Log.info("Registered service [marsh=" + marsh + "]."); + return marsh; } public void objectAvailable (DObject object) @@ -100,6 +119,15 @@ public class InvocationManager // add ourselves as a message listener object.addListener(this); + + // let any early registered marshallers know about our invoid + while (_lateInitQueue.size() > 0) { + InvocationMarshaller marsh = (InvocationMarshaller) + _lateInitQueue.remove(0); + marsh.setInvocationOid(_invoid); + } + +// Log.info("Created invocation service object [oid=" + _invoid + "]."); } public void requestFailed (int oid, ObjectAccessException cause) @@ -111,85 +139,118 @@ public class InvocationManager _invoid = -1; } - public void messageReceived (MessageEvent event) + // documentation inherited from interface + public void eventReceived (DEvent event) { - // make sure the name is proper just for sanity's sake - if (!event.getName().equals(InvocationObject.REQUEST_NAME)) { - return; - } +// Log.info("Event received " + event + "."); - // we've got an invocation request, so we process it - Object[] args = event.getArgs(); - String module = (String)args[0]; - String procedure = (String)args[1]; - Integer invid = (Integer)args[2]; - - // locate a provider for this module - InvocationProvider provider = - (InvocationProvider)_providers.get(module); - if (provider == null) { - Log.warning("No provider registered for invocation request " + - "[evt=" + event + "]."); - return; - } - - // prune the method arguments from the full message arguments - Object[] margs = new Object[args.length-1]; - int cloid = event.getSourceOid(); - ClientObject source = (ClientObject) - PresentsServer.omgr.getObject(cloid); - // make sure the client is still around - if (source == null) { - Log.warning("Client no longer around for invocation provider " + - "request [module=" + module + - ", proc=" + procedure + ", cloid=" + cloid + "]."); - return; - } - margs[0] = source; - System.arraycopy(args, 2, margs, 1, args.length-2); - - // look up the method that will handle this procedure - String mname = "handle" + procedure + "Request"; - Method procmeth = ClassUtil.getMethod(mname, provider, _methcache); - if (procmeth == null) { - Log.warning("Unable to resolve provider procedure " + - "[provider=" + provider.getClass().getName() + - ", method=" + mname + "]."); - return; - } - - // and invoke it - try { - procmeth.invoke(provider, margs); - - } catch (InvocationTargetException ite) { - Throwable te = ite.getTargetException(); - if (te instanceof ServiceFailedException) { - // automatically generate a Failed response - provider.sendResponse(source, invid.intValue(), - procedure + FAILED_SUFFIX, - te.getMessage()); - - } else { - Log.warning("Invocation procedure failed " + - "[provider=" + provider + - ", method=" + procmeth + - ", args=" + StringUtil.toString(margs) + "]."); - Log.logStackTrace(te); - } - - } catch (Exception e) { - Log.warning("Error invoking invocation procedure " + - "[provider=" + provider + - ", method=" + procmeth + "]."); - Log.logStackTrace(e); + if (event instanceof InvocationRequestEvent) { + InvocationRequestEvent ire = (InvocationRequestEvent)event; + dispatchRequest(ire.getSourceOid(), ire.getInvCode(), + ire.getMethodId(), ire.getArgs()); } } - protected DObjectManager _omgr; - protected int _invoid; - protected HashMap _providers = new HashMap(); - protected HashMap _methcache = new HashMap(); + /** + * Called when we receive an invocation request message. Dispatches + * the request to the appropriate invocation provider via the + * registered invocation dispatcher. + */ + protected void dispatchRequest ( + int clientOid, int invCode, int methodId, Object[] args) + { + // make sure the client is still around + ClientObject source = (ClientObject)_omgr.getObject(clientOid); + if (source == null) { + Log.warning("Client no longer around for invocation " + + "request [clientOid=" + clientOid + + ", code=" + invCode + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + return; + } + + // look up the dispatcher + InvocationDispatcher disp = (InvocationDispatcher) + _dispatchers.get(invCode); + if (disp == null) { + Log.warning("Received invocation request for which we have " + + "no registered dispatcher [code=" + invCode + + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + return; + } + + // scan the args, initializing any listeners and keeping track of + // the "primary" listener + ListenerMarshaller rlist = null; + int acount = args.length; + for (int ii = 0; ii < acount; ii++) { + Object arg = args[ii]; + if (arg instanceof ListenerMarshaller) { + ListenerMarshaller list = (ListenerMarshaller)arg; + list.omgr = _omgr; + // keep track of the listener we'll inform if anything + // goes horribly awry + if (rlist == null) { + rlist = list; + } + } + } + +// Log.info("Dispatching invreq [caller=" + source.who() + +// ", methId=" + methodId + +// ", args=" + StringUtil.toString(args) + "]."); + + // dispatch the request + try { + disp.dispatchRequest(source, methodId, args); + + } catch (InvocationException ie) { + if (rlist != null) { + rlist.requestFailed(ie.getMessage()); + + } else { + Log.warning("Service request failed but we've got no " + + "listener to inform of the failure " + + "[clientOid=" + clientOid + ", code=" + invCode + + ", dispatcher=" + disp + ", methodId=" + methodId + + ", args=" + StringUtil.toString(args) + + ", error=" + ie + "]."); + } + + } catch (Throwable t) { + Log.warning("Dispatcher choked [disp=" + disp + + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + Log.logStackTrace(t); + } + } + + /** + * Used to generate monotonically increasing provider ids. + */ + protected synchronized int nextInvCode () + { + return _invCode++; + } + + /** The distributed object manager with which we're working. */ + protected RootDObjectManager _omgr; + + /** The object id of the object on which we receive invocation service + * requests. */ + protected int _invoid = -1; + + /** Used to generate monotonically increasing provider ids. */ + protected int _invCode; + + /** A table of invocation dispatchers each mapped by a unique code. */ + protected HashIntMap _dispatchers = new HashIntMap(); + + /** Used to keep track of marshallers registered before we had our + * invocation object so that we can fill their invocation id in + * belatedly. */ + protected ArrayList _lateInitQueue = new ArrayList(); /** The text that is appended to the procedure name when automatically * generating a failure response. */ diff --git a/src/java/com/threerings/presents/server/InvocationProvider.java b/src/java/com/threerings/presents/server/InvocationProvider.java index dcbc7e736..bbf7dd22b 100644 --- a/src/java/com/threerings/presents/server/InvocationProvider.java +++ b/src/java/com/threerings/presents/server/InvocationProvider.java @@ -1,151 +1,11 @@ // -// $Id: InvocationProvider.java,v 1.7 2002/04/17 18:20:04 mdb Exp $ +// $Id: InvocationProvider.java,v 1.8 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.server; -import com.samskivert.util.StringUtil; - -import com.threerings.presents.Log; -import com.threerings.presents.data.ClientObject; -import com.threerings.presents.data.InvocationObject; -import com.threerings.presents.dobj.MessageEvent; - /** - * Invocation providers should extend this class when implementing - * invocation services. Because the service procedures are identified by - * strings and the methods that are invoked are looked up via reflection, - * the derived class doesn't override or implement any particular method. - * However, the procedure names are still restricted. For example, a - * procedure identified by the name Tell would result in the - * invocation of a method named handleTellRequest. The - * arguments to that method would be defined by the arguments that - * accompanied the Tell invocation request along with the - * client object of the client that made the request. Specifically: - * - *

- *     // client makes request
- *     Object[] args = new Object[] { "one", new Integer(2) };
- *     invmgr.invoke(MODULE, "Test", args, rsptarget);
- *
- *     // provider registered for MODULE should look like:
- *     public class TestProvider extends InvocationProvider
- *     {
- *         public void handleTestRequest (ClientObject source, int invid,
- *                                        String one, int two)
- *         {
- *             // ...
- *         }
- *     }
- * 
- * - * If the arguments do not match, a reflection error will happen when - * trying to invoke the method and the whole request will fail. - * - *

Invocation procedures must also package up their response in a - * particular way which is through the use of the - * sendResponse methods. These take a response identifier - * (which determines the name of the method that will be invoked on the - * response target object provided in the client) and a variable number of - * arguments. If a response was created with the identifier - * TellFailed, that would result in the method - * handleTellFailed being invoked on the response target - * object in the client. Again the arguments much match exactly and follow - * the reflection rules for automatic conversion of primitive types - * (supply an Integer object for int params, - * etc.). - * - *

Note that if an invocation service method throws a {@link - * ServiceFailedException}, the invocation manager will automatically - * issue a response to the client with the string Failed - * appended to the request method. For example, if the client issues a - * request for Foo which results in a call to - * handleFooRequest, which throws a service failed exception, - * the server will automatically issue a failure response named - * FooFailed with the single argument being the - * reason string provided to the constructor of the service - * failed exception. The caller would then implement: - * - *

- * public void handleFooFailed (int invid, String reason)
- * 
- * - * to handle the failure. + * All invocation providers must implement this placeholder interface. */ -public class InvocationProvider +public interface InvocationProvider { - /** - * Delivers an invocation response properly configured with the - * supplied name and no arguments. - */ - protected void sendResponse (ClientObject source, int invid, String name) - { - deliverResponse(source, new Object[] { name, new Integer(invid) }); - } - - /** - * Delivers an invocation response properly configured with the - * supplied name and single argument. - */ - protected void sendResponse (ClientObject source, int invid, - String name, Object arg) - { - Object[] args = new Object[] { name, new Integer(invid), arg }; - deliverResponse(source, args); - } - - /** - * Delivers an invocation response properly configured with the - * supplied name and two arguments. - */ - protected void sendResponse (ClientObject source, int invid, - String name, Object arg1, Object arg2) - { - Object[] args = new Object[] { - name, new Integer(invid), arg1, arg2 }; - deliverResponse(source, args); - } - - /** - * Delivers an invocation response properly configured with the - * supplied name and three arguments. - */ - protected void sendResponse (ClientObject source, int invid, - String name, Object arg1, Object arg2, - Object arg3) - { - Object[] args = new Object[] { - name, new Integer(invid), arg1, arg2, arg3 }; - deliverResponse(source, args); - } - - /** - * Delivers an invocation response properly configured with the - * supplied name and varying number of arguments. - */ - protected void sendResponse (ClientObject source, int invid, - String name, Object[] args) - { - Object[] rargs = new Object[args.length+2]; - rargs[0] = name; - rargs[1] = new Integer(invid); - System.arraycopy(args, 0, rargs, 2, args.length); - deliverResponse(source, rargs); - } - - protected void deliverResponse (ClientObject source, Object[] args) - { - // make sure they didn't go away in the meanwhile - if (source.isActive()) { - // create the response event - MessageEvent mevt = new MessageEvent( - source.getOid(), InvocationObject.RESPONSE_NAME, args); - // and ship it off - PresentsServer.omgr.postEvent(mevt); - - } else { - Log.warning("Dropping invrsp due to disappearing client " + - "[cloid=" + source.getOid() + - ", args=" + StringUtil.toString(args) + "]."); - } - } } diff --git a/src/java/com/threerings/presents/server/InvocationSender.java b/src/java/com/threerings/presents/server/InvocationSender.java new file mode 100644 index 000000000..0bef475c9 --- /dev/null +++ b/src/java/com/threerings/presents/server/InvocationSender.java @@ -0,0 +1,45 @@ +// +// $Id: InvocationSender.java,v 1.1 2002/08/14 19:07:56 mdb Exp $ + +package com.threerings.presents.server; + +import com.samskivert.util.StringUtil; + +import com.threerings.presents.Log; +import com.threerings.presents.client.InvocationReceiver.Registration; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.dobj.InvocationNotificationEvent; + +/** + * Provides basic functionality used by all invocation sender classes. + */ +public abstract class InvocationSender +{ + /** + * Requests that the specified invocation notification be packaged up + * and sent to the supplied target client. + */ + public static void sendNotification ( + ClientObject target, String receiverCode, int methodId, Object[] args) + { + // convert the receiver hash id into the code used on this + // specific client + Registration rreg = (Registration)target.receivers.get(receiverCode); + if (rreg == null) { + Log.warning("Unable to locate registered receiver for " + + "invocation service notification [target=" + target + + ", code=" + receiverCode + ", methId=" + methodId + + ", args=" + StringUtil.toString(args) + "]."); + + } else { +// Log.info("Sending notification [target=" + target + +// ", code=" + receiverCode + ", methodId=" + methodId + +// ", args=" + StringUtil.toString(args) + "]."); + + // create and dispatch an invocation notification event + target.postEvent( + new InvocationNotificationEvent( + target.getOid(), rreg.receiverId, methodId, args)); + } + } +} diff --git a/src/java/com/threerings/presents/server/PresentsClient.java b/src/java/com/threerings/presents/server/PresentsClient.java index 3cde7a827..9c4b49cae 100644 --- a/src/java/com/threerings/presents/server/PresentsClient.java +++ b/src/java/com/threerings/presents/server/PresentsClient.java @@ -1,5 +1,5 @@ // -// $Id: PresentsClient.java,v 1.34 2002/07/23 05:52:49 mdb Exp $ +// $Id: PresentsClient.java,v 1.35 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.server; @@ -324,8 +324,8 @@ public class PresentsClient // give them the client object id data.clientOid = _clobj.getOid(); - // give them the invocation oid - data.invOid = PresentsServer.invmgr.getOid(); + // fill in the list of bootstrap services + data.services = PresentsServer.invmgr.bootlist; } /** diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java index 99bdaad15..0315bf625 100644 --- a/src/java/com/threerings/presents/server/PresentsServer.java +++ b/src/java/com/threerings/presents/server/PresentsServer.java @@ -1,5 +1,5 @@ // -// $Id: PresentsServer.java,v 1.23 2002/07/25 20:24:02 mdb Exp $ +// $Id: PresentsServer.java,v 1.24 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.server; @@ -66,57 +66,57 @@ public class PresentsServer // initialize the time base services TimeBaseProvider.init(invmgr, omgr); - // register our invocation service providers - registerProviders(PresentsConfig.getProviders()); +// // register our invocation service providers +// registerProviders(PresentsConfig.getProviders()); } - /** - * Registers invocation service providers as parsed from a - * configuration file. Each string in the array should contain an - * expression of the form: - * - *
-     * module = provider fully-qualified class name
-     * 
- * - * A comma separated list of these can be specified in the - * configuration file and loaded into a string array easily. These - * providers will be instantiated and registered with the invocation - * manager. - */ - protected void registerProviders (String[] providers) - { - // ignore null arrays to make life easier for the caller - if (providers == null) { - return; - } +// /** +// * Registers invocation service providers as parsed from a +// * configuration file. Each string in the array should contain an +// * expression of the form: +// * +// *
+//      * module = provider fully-qualified class name
+//      * 
+// * +// * A comma separated list of these can be specified in the +// * configuration file and loaded into a string array easily. These +// * providers will be instantiated and registered with the invocation +// * manager. +// */ +// protected void registerProviders (String[] providers) +// { +// // ignore null arrays to make life easier for the caller +// if (providers == null) { +// return; +// } - for (int i = 0; i < providers.length; i++) { - int eidx = providers[i].indexOf("="); - if (eidx == -1) { - Log.warning("Ignoring bogus provider declaration " + - "[decl=" + providers[i] + "]."); - continue; - } +// for (int i = 0; i < providers.length; i++) { +// int eidx = providers[i].indexOf("="); +// if (eidx == -1) { +// Log.warning("Ignoring bogus provider declaration " + +// "[decl=" + providers[i] + "]."); +// continue; +// } - String module = providers[i].substring(0, eidx).trim(); - String pname = providers[i].substring(eidx+1).trim(); +// String module = providers[i].substring(0, eidx).trim(); +// String pname = providers[i].substring(eidx+1).trim(); - // instantiate the provider class and register it - try { - Class pclass = Class.forName(pname); - InvocationProvider provider = (InvocationProvider) - pclass.newInstance(); - invmgr.registerProvider(module, provider); - Log.info("Registered provider [module=" + module + - ", provider=" + pname + "]."); +// // instantiate the provider class and register it +// try { +// Class pclass = Class.forName(pname); +// InvocationProvider provider = (InvocationProvider) +// pclass.newInstance(); +// invmgr.registerProvider(module, provider); +// Log.info("Registered provider [module=" + module + +// ", provider=" + pname + "]."); - } catch (Exception e) { - Log.warning("Unable to register provider [module=" + module + - ", provider=" + pname + ", error=" + e + "]."); - } - } - } +// } catch (Exception e) { +// Log.warning("Unable to register provider [module=" + module + +// ", provider=" + pname + ", error=" + e + "]."); +// } +// } +// } /** * Starts up all of the server services and enters the main server diff --git a/src/java/com/threerings/presents/server/ServiceFailedException.java b/src/java/com/threerings/presents/server/ServiceFailedException.java deleted file mode 100644 index 16e4c9d65..000000000 --- a/src/java/com/threerings/presents/server/ServiceFailedException.java +++ /dev/null @@ -1,45 +0,0 @@ -// -// $Id: ServiceFailedException.java,v 1.2 2001/10/11 04:07:53 mdb Exp $ - -package com.threerings.presents.server; - -/** - * An exception class for use in concert with invocation services when - * they need to communicate a failure of some kind and can't use the - * return value. - * - *

For example, consider an invitation service: - * - *

- * public class fooManager
- * {
- *     // returns invitation id, throws ServiceFailedException if
- *     // invitation couldn't be processed
- *     public int invite (...)
- *         throws ServiceFailedException
- *     {
- *     }
- * }
- *
- * public class fooProvider
- * {
- *     public void handleInviteRequest (...)
- *     {
- *         try {
- *             int inviteId = _mgr.invite(...);
- *             sendResponse(..., INVITE_RECEIVED, new Integer(inviteId));
- *
- *         } catch (ServiceFailedException sfe) {
- *             sendResponse(..., INVITE_FAILED, sfe.getMessage());
- *         }
- *     }
- * }
- * 
- */ -public class ServiceFailedException extends Exception -{ - public ServiceFailedException (String message) - { - super(message); - } -} diff --git a/src/java/com/threerings/presents/server/TimeBaseDispatcher.java b/src/java/com/threerings/presents/server/TimeBaseDispatcher.java new file mode 100644 index 000000000..064e8bdd4 --- /dev/null +++ b/src/java/com/threerings/presents/server/TimeBaseDispatcher.java @@ -0,0 +1,52 @@ +// +// $Id: TimeBaseDispatcher.java,v 1.1 2002/08/14 19:07:56 mdb Exp $ + +package com.threerings.presents.server; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.TimeBaseService; +import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.data.TimeBaseMarshaller; +import com.threerings.presents.server.InvocationDispatcher; +import com.threerings.presents.server.InvocationException; + +/** + * Dispatches requests to the {@link TimeBaseProvider}. + */ +public class TimeBaseDispatcher extends InvocationDispatcher +{ + /** + * Creates a dispatcher that may be registered to dispatch invocation + * service requests for the specified provider. + */ + public TimeBaseDispatcher (TimeBaseProvider provider) + { + this.provider = provider; + } + + // documentation inherited + public InvocationMarshaller createMarshaller () + { + return new TimeBaseMarshaller(); + } + + // documentation inherited + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + switch (methodId) { + case TimeBaseMarshaller.GET_TIME_OID: + ((TimeBaseProvider)provider).getTimeOid( + source, + (String)args[0], (GotTimeBaseListener)args[1] + ); + return; + + default: + super.dispatchRequest(source, methodId, args); + } + } +} diff --git a/src/java/com/threerings/presents/server/TimeBaseProvider.java b/src/java/com/threerings/presents/server/TimeBaseProvider.java index 58d33d6d5..b4b9eb245 100644 --- a/src/java/com/threerings/presents/server/TimeBaseProvider.java +++ b/src/java/com/threerings/presents/server/TimeBaseProvider.java @@ -1,5 +1,5 @@ // -// $Id: TimeBaseProvider.java,v 1.1 2002/05/28 23:14:06 mdb Exp $ +// $Id: TimeBaseProvider.java,v 1.2 2002/08/14 19:07:56 mdb Exp $ package com.threerings.presents.server; @@ -7,6 +7,8 @@ import java.util.HashMap; import com.samskivert.util.ResultListener; import com.threerings.presents.Log; +import com.threerings.presents.client.TimeBaseService.GotTimeBaseListener; + import com.threerings.presents.data.ClientObject; import com.threerings.presents.data.TimeBaseCodes; import com.threerings.presents.data.TimeBaseObject; @@ -22,8 +24,8 @@ import com.threerings.presents.dobj.Subscriber; * network which are expanded based on a shared base time into full time * stamps. */ -public class TimeBaseProvider extends InvocationProvider - implements TimeBaseCodes +public class TimeBaseProvider + implements InvocationProvider, TimeBaseCodes { /** * Registers the time provider with the appropriate managers. Called @@ -35,8 +37,9 @@ public class TimeBaseProvider extends InvocationProvider _invmgr = invmgr; _omgr = omgr; - // register an invocation provider instance - _invmgr.registerProvider(MODULE_NAME, new TimeBaseProvider()); + // register a provider instance + invmgr.registerDispatcher( + new TimeBaseDispatcher(new TimeBaseProvider()), true); } /** @@ -81,18 +84,17 @@ public class TimeBaseProvider extends InvocationProvider * Processes a request from a client to fetch the oid of the specified * time object. */ - public void handleGetTimeOidRequest ( - ClientObject source, int invid, String timeBase) - throws ServiceFailedException + public void getTimeOid ( + ClientObject source, String timeBase, GotTimeBaseListener listener) + throws InvocationException { // look up the time base object in question TimeBaseObject time = getTimeBase(timeBase); if (time == null) { - throw new ServiceFailedException(NO_SUCH_TIME_BASE); + throw new InvocationException(NO_SUCH_TIME_BASE); } // and send the response - sendResponse(source, invid, TIME_OID_RESPONSE, - new Integer(time.getOid())); + listener.gotTimeOid(time.getOid()); } /** Used to keep track of our time base objects. */ diff --git a/src/java/com/threerings/whirled/client/SceneDecoder.java b/src/java/com/threerings/whirled/client/SceneDecoder.java new file mode 100644 index 000000000..e0aa940bb --- /dev/null +++ b/src/java/com/threerings/whirled/client/SceneDecoder.java @@ -0,0 +1,52 @@ +// +// $Id: SceneDecoder.java,v 1.1 2002/08/14 19:07:57 mdb Exp $ + +package com.threerings.whirled.client; + +import com.threerings.presents.client.InvocationDecoder; +import com.threerings.whirled.client.SceneReceiver; + +/** + * Dispatches calls to a {@link SceneReceiver} instance. + */ +public class SceneDecoder extends InvocationDecoder +{ + /** The generated hash code used to identify this receiver class. */ + public static final String RECEIVER_CODE = "c4d0cf66b81a6e83d119b2d607725651"; + + /** The method id used to dispatch {@link SceneReceiver#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 SceneDecoder (SceneReceiver 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: + ((SceneReceiver)receiver).forcedMove( + ((Integer)args[0]).intValue() + ); + return; + + default: + super.dispatchNotification(methodId, args); + } + } + + // Generated on 11:25:48 08/12/02. +} diff --git a/src/java/com/threerings/whirled/client/SceneDirector.java b/src/java/com/threerings/whirled/client/SceneDirector.java index c52e592b1..69d484336 100644 --- a/src/java/com/threerings/whirled/client/SceneDirector.java +++ b/src/java/com/threerings/whirled/client/SceneDirector.java @@ -1,5 +1,5 @@ // -// $Id: SceneDirector.java,v 1.19 2002/06/14 01:40:16 ray Exp $ +// $Id: SceneDirector.java,v 1.20 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.client; @@ -7,9 +7,8 @@ import java.io.IOException; import com.samskivert.util.HashIntMap; import com.samskivert.util.ResultListener; +import com.threerings.presents.client.BasicDirector; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationReceiver; -import com.threerings.presents.client.SessionObserver; import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.ObjectAccessException; @@ -37,9 +36,9 @@ import com.threerings.whirled.util.WhirledContext; * LocationObserver#locationMayChange} and {@link * LocationObserver#locationChangeFailed}. */ -public class SceneDirector - implements SceneCodes, SessionObserver, LocationDirector.FailureHandler, - InvocationReceiver +public class SceneDirector extends BasicDirector + implements SceneCodes, LocationDirector.FailureHandler, + SceneReceiver, SceneService.SceneMoveListener { /** * Creates a new scene director with the specified context. @@ -55,6 +54,8 @@ public class SceneDirector public SceneDirector (WhirledContext ctx, LocationDirector locdir, SceneRepository screp, DisplaySceneFactory dsfact) { + super(ctx); + // we'll need these for later _ctx = ctx; _locdir = locdir; @@ -65,13 +66,9 @@ public class SceneDirector // director because we need to do special processing _locdir.setFailureHandler(this); - // register ourselves as a client observer so that we can clear - // out our scene when the client logs off - _ctx.getClient().addClientObserver(this); - // register for scene notifications _ctx.getClient().getInvocationDirector().registerReceiver( - MODULE_NAME, this); + new SceneDecoder(this)); } /** @@ -115,7 +112,7 @@ public class SceneDirector } // issue a moveTo request - SceneService.moveTo(_ctx.getClient(), sceneId, sceneVers, this); + _sservice.moveTo(_ctx.getClient(), sceneId, sceneVers, this); return true; } @@ -176,11 +173,8 @@ public class SceneDirector return _pendingModel; } - /** - * Called in response to a successful moveTo request. - */ - public void handleMoveSucceeded ( - int invid, int placeId, PlaceConfig config) + // documentation inherited from interface + public void moveSucceeded (int placeId, PlaceConfig config) { // our move request was successful, deal with subscribing to our // new place object @@ -214,13 +208,9 @@ public class SceneDirector _scene = _dsfact.createScene(_model, config); } - /** - * Called in response to a successful moveTo request when - * our cached scene was out of date and the server determined that we - * needed an updated copy. - */ - public void handleMoveSucceededPlusUpdate ( - int invid, int placeId, PlaceConfig config, SceneModel model) + // documentation inherited from interface + public void moveSucceededPlusUpdate ( + int placeId, PlaceConfig config, SceneModel model) { // update the model in the repository try { @@ -237,13 +227,11 @@ public class SceneDirector _scache.put(model.sceneId, model); // and pass through to the normal move succeeded handler - handleMoveSucceeded(invid, placeId, config); + moveSucceeded(placeId, config); } - /** - * Called in response to a failed moveTo request. - */ - public void handleMoveFailed (int invid, String reason) + // documentation inherited from interface + public void requestFailed (String reason) { // clear out our pending request oid int sceneId = _pendingSceneId; @@ -266,13 +254,8 @@ public class SceneDirector clearScene(); } - /** - * Called when the server has decided to forcibly move us to another - * scene. The server first ejects us from our previous scene and then - * sends us a notification with our new location. We then turn around - * and issue a standard moveTo request. - */ - public void handleMoveNotification (int sceneId) + // documentation inherited from interface + public void forcedMove (int sceneId) { Log.info("Moving at request of server [sceneId=" + sceneId + "]."); @@ -361,20 +344,26 @@ public class SceneDirector } // documentation inherited from interface - public void clientDidLogon (Client client) + public void clientDidLogoff (Client client) { - // nothing for now + super.clientDidLogoff(client); + + clearScene(); } // documentation inherited from interface - public void clientDidLogoff (Client client) + protected void fetchServices (Client client) { - clearScene(); + // get a handle on our scene service + _sservice = (SceneService)client.requireService(SceneService.class); } /** Access to general client services. */ protected WhirledContext _ctx; + /** Access to our scene services. */ + protected SceneService _sservice; + /** The client's active location director. */ protected LocationDirector _locdir; diff --git a/src/java/com/threerings/whirled/client/SceneReceiver.java b/src/java/com/threerings/whirled/client/SceneReceiver.java new file mode 100644 index 000000000..aa02c3408 --- /dev/null +++ b/src/java/com/threerings/whirled/client/SceneReceiver.java @@ -0,0 +1,21 @@ +// +// $Id: SceneReceiver.java,v 1.1 2002/08/14 19:07:57 mdb Exp $ + +package com.threerings.whirled.client; + +import com.threerings.presents.client.InvocationReceiver; + +/** + * Defines, for the scene services, a set of notifications delivered + * asynchronously by the server to the client. + */ +public interface SceneReceiver extends InvocationReceiver +{ + /** + * Used to communicate a required move notification to the client. The + * server will have removed the client from their existing scene + * and the client is then responsible for generating a {@link + * SceneService#moveTo} request to move to the new scene. + */ + public void forcedMove (int sceneId); +} diff --git a/src/java/com/threerings/whirled/client/SceneService.java b/src/java/com/threerings/whirled/client/SceneService.java index f60c332d6..d3095c7ae 100644 --- a/src/java/com/threerings/whirled/client/SceneService.java +++ b/src/java/com/threerings/whirled/client/SceneService.java @@ -1,20 +1,46 @@ // -// $Id: SceneService.java,v 1.8 2002/05/15 23:54:34 mdb Exp $ +// $Id: SceneService.java,v 1.9 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; -import com.threerings.whirled.Log; -import com.threerings.whirled.data.SceneCodes; +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.whirled.data.SceneModel; /** * The scene service class provides the client interface to the scene * related invocation services (e.g. moving from scene to scene). */ -public class SceneService implements SceneCodes +public interface SceneService extends InvocationService { + /** + * Used to communicate the response to a {@link #moveTo} request. + */ + public static interface SceneMoveListener extends InvocationListener + { + /** + * Indicates that a move succeeded. + * + * @param placeId the place object id of the newly occupied scene. + * @param config metadata related to the newly occupied scene. + */ + public void moveSucceeded (int placeId, PlaceConfig config); + + /** + * Indicates that a move succeeded and that the client's cached + * scene information should be updated with the supplied data. + * + * @param placeId the place object id of the newly occupied scene. + * @param config metadata related to the newly occupied scene. + * @param model the most recent scene data for the newly occupied + * scene. + */ + public void moveSucceededPlusUpdate ( + int placeId, PlaceConfig config, SceneModel model); + } + /** * Requests that that this client's body be moved to the specified * scene. @@ -23,14 +49,6 @@ public class SceneService implements SceneCodes * @param sceneVers the version number of the scene object that we * have in our local repository. */ - public static void moveTo (Client client, int sceneId, - int sceneVers, Object rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { - new Integer(sceneId), new Integer(sceneVers) }; - invdir.invoke(MODULE_NAME, MOVE_TO_REQUEST, args, rsptarget); - Log.debug("Sent moveTo request [scene=" + sceneId + - ", version=" + sceneVers + "]."); - } + public void moveTo (Client client, int sceneId, + int sceneVers, SceneMoveListener listener); } diff --git a/src/java/com/threerings/whirled/data/SceneCodes.java b/src/java/com/threerings/whirled/data/SceneCodes.java index 511b2ad61..f60d5353a 100644 --- a/src/java/com/threerings/whirled/data/SceneCodes.java +++ b/src/java/com/threerings/whirled/data/SceneCodes.java @@ -1,5 +1,5 @@ // -// $Id: SceneCodes.java,v 1.2 2002/04/15 18:06:20 mdb Exp $ +// $Id: SceneCodes.java,v 1.3 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.data; @@ -11,13 +11,4 @@ import com.threerings.whirled.client.SceneDirector; */ public interface SceneCodes extends LocationCodes { - /** The module name for the scene services. */ - public static final String MODULE_NAME = "whirled!scene"; - - /** The response identifier for a successful moveTo request that - * includes an updated scene model. This is mapped by the invocation - * services to a call to {@link - * SceneDirector#handleMoveSucceededPlusUpdate}. */ - public static final String MOVE_SUCCEEDED_PLUS_UPDATE_RESPONSE = - "MoveSucceededPlusUpdate"; } diff --git a/src/java/com/threerings/whirled/data/SceneMarshaller.java b/src/java/com/threerings/whirled/data/SceneMarshaller.java new file mode 100644 index 000000000..ca3bab876 --- /dev/null +++ b/src/java/com/threerings/whirled/data/SceneMarshaller.java @@ -0,0 +1,85 @@ +// +// $Id: SceneMarshaller.java,v 1.1 2002/08/14 19:07:57 mdb Exp $ + +package com.threerings.whirled.data; + +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.presents.client.Client; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; +import com.threerings.whirled.client.SceneService; +import com.threerings.whirled.client.SceneService.SceneMoveListener; +import com.threerings.whirled.data.SceneModel; + +/** + * Provides the implementation of the {@link SceneService} 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 SceneMarshaller extends InvocationMarshaller + implements SceneService +{ + // documentation inherited + public static class SceneMoveMarshaller extends ListenerMarshaller + implements SceneMoveListener + { + /** The method id used to dispatch {@link #moveSucceeded} + * responses. */ + public static final int MOVE_SUCCEEDED = 0; + + // documentation inherited from interface + public void moveSucceeded (int arg1, PlaceConfig arg2) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED, + new Object[] { new Integer(arg1), arg2 })); + } + /** The method id used to dispatch {@link #moveSucceededPlusUpdate} + * responses. */ + public static final int MOVE_SUCCEEDED_PLUS_UPDATE = 1; + + // documentation inherited from interface + public void moveSucceededPlusUpdate (int arg1, PlaceConfig arg2, SceneModel arg3) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED_PLUS_UPDATE, + new Object[] { new Integer(arg1), arg2, arg3 })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case MOVE_SUCCEEDED: + ((SceneMoveListener)listener).moveSucceeded( + ((Integer)args[0]).intValue(), (PlaceConfig)args[1]); + return; + + case MOVE_SUCCEEDED_PLUS_UPDATE: + ((SceneMoveListener)listener).moveSucceededPlusUpdate( + ((Integer)args[0]).intValue(), (PlaceConfig)args[1], (SceneModel)args[2]); + 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, int arg3, SceneMoveListener arg4) + { + SceneMoveMarshaller listener4 = new SceneMoveMarshaller(); + listener4.listener = arg4; + sendRequest(arg1, MOVE_TO, new Object[] { + new Integer(arg2), new Integer(arg3), listener4 + }); + } + + // Class file generated on 00:26:03 08/11/02. +} diff --git a/src/java/com/threerings/whirled/server/SceneDispatcher.java b/src/java/com/threerings/whirled/server/SceneDispatcher.java new file mode 100644 index 000000000..c8e2a29f6 --- /dev/null +++ b/src/java/com/threerings/whirled/server/SceneDispatcher.java @@ -0,0 +1,54 @@ +// +// $Id: SceneDispatcher.java,v 1.1 2002/08/14 19:07:57 mdb Exp $ + +package com.threerings.whirled.server; + +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; +import com.threerings.whirled.client.SceneService; +import com.threerings.whirled.client.SceneService.SceneMoveListener; +import com.threerings.whirled.data.SceneMarshaller; +import com.threerings.whirled.data.SceneModel; + +/** + * Dispatches requests to the {@link SceneProvider}. + */ +public class SceneDispatcher extends InvocationDispatcher +{ + /** + * Creates a dispatcher that may be registered to dispatch invocation + * service requests for the specified provider. + */ + public SceneDispatcher (SceneProvider provider) + { + this.provider = provider; + } + + // documentation inherited + public InvocationMarshaller createMarshaller () + { + return new SceneMarshaller(); + } + + // documentation inherited + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + switch (methodId) { + case SceneMarshaller.MOVE_TO: + ((SceneProvider)provider).moveTo( + source, + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (SceneMoveListener)args[2] + ); + return; + + default: + super.dispatchRequest(source, methodId, args); + } + } +} diff --git a/src/java/com/threerings/whirled/server/SceneProvider.java b/src/java/com/threerings/whirled/server/SceneProvider.java index ea8a48aff..2d12ea26b 100644 --- a/src/java/com/threerings/whirled/server/SceneProvider.java +++ b/src/java/com/threerings/whirled/server/SceneProvider.java @@ -1,11 +1,11 @@ // -// $Id: SceneProvider.java,v 1.10 2002/05/26 02:29:13 mdb Exp $ +// $Id: SceneProvider.java,v 1.11 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.server; -import com.threerings.presents.server.InvocationManager; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceConfig; @@ -13,6 +13,7 @@ import com.threerings.crowd.data.PlaceObject; import com.threerings.crowd.server.LocationProvider; import com.threerings.whirled.Log; +import com.threerings.whirled.client.SceneService.SceneMoveListener; import com.threerings.whirled.data.SceneCodes; import com.threerings.whirled.data.SceneModel; @@ -20,29 +21,26 @@ import com.threerings.whirled.data.SceneModel; * The scene provider handles the server side of the scene related * invocation services (e.g. moving from scene to scene). */ -public class SceneProvider extends InvocationProvider - implements SceneCodes +public class SceneProvider + implements InvocationProvider, SceneCodes { /** * Constructs a scene provider that will interact with the supplied * scene registry. */ - public SceneProvider (InvocationManager invmgr, SceneRegistry screg) + public SceneProvider (LocationProvider locprov, SceneRegistry screg) { - _invmgr = invmgr; + _locprov = locprov; _screg = screg; } /** * Processes a request from a client to move to a new scene. */ - public void handleMoveToRequest (BodyObject source, int invid, - int sceneId, int sceneVersion) + public void moveTo (ClientObject caller, int sceneId, + final int sceneVer, final SceneMoveListener listener) { - // avoid cluttering up the method declaration with final keywords - final BodyObject fsource = source; - final int finvid = invid; - final int fsceneVer = sceneVersion; + final BodyObject source = (BodyObject)caller; // create a callback object that will handle the resolution or // failed resolution of the scene @@ -51,7 +49,7 @@ public class SceneProvider extends InvocationProvider { public void sceneWasResolved (SceneManager scmgr) { - finishMoveToRequest(fsource, finvid, scmgr, fsceneVer); + finishMoveToRequest(source, scmgr, sceneVer, listener); } public void sceneFailedToResolve ( @@ -60,8 +58,7 @@ public class SceneProvider extends InvocationProvider Log.warning("Unable to resolve scene [sceneid=" + rsceneId + ", reason=" + reason + "]."); // pretend like the scene doesn't exist to the client - sendResponse(fsource, finvid, MOVE_FAILED_RESPONSE, - NO_SUCH_PLACE); + listener.requestFailed(NO_SUCH_PLACE); } }; @@ -74,8 +71,9 @@ public class SceneProvider extends InvocationProvider * This is called after the scene to which we are moving is guaranteed * to have been loaded into the server. */ - protected void finishMoveToRequest (BodyObject source, int invid, - SceneManager scmgr, int sceneVersion) + protected void finishMoveToRequest ( + BodyObject source, SceneManager scmgr, int sceneVersion, + SceneMoveListener listener) { // move to the place object associated with this scene PlaceObject plobj = scmgr.getPlaceObject(); @@ -83,23 +81,18 @@ public class SceneProvider extends InvocationProvider try { // try doing the actual move - PlaceConfig config = LocationProvider.moveTo(source, ploid); + PlaceConfig config = _locprov.moveTo(source, ploid); // check to see if they need a newer version of the scene data SceneModel model = scmgr.getSceneModel(); if (sceneVersion < model.version) { - // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_PLUS_UPDATE_RESPONSE, - new Integer(ploid), config, model); - + listener.moveSucceededPlusUpdate(ploid, config, model); } else { - // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, - new Integer(ploid), config); + listener.moveSucceeded(ploid, config); } - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); + } catch (InvocationException sfe) { + listener.requestFailed(sfe.getMessage()); } } @@ -108,20 +101,18 @@ public class SceneProvider extends InvocationProvider * request to move to the specified new scene. This is the * scene-equivalent to {@link LocationProvider#moveBody}. */ - public static void moveBody (BodyObject source, int sceneId) + public void moveBody (BodyObject source, int sceneId) { // first remove them from their old place - LocationProvider.leaveOccupiedPlace(source); + _locprov.leaveOccupiedPlace(source); - // then send a move notification - _invmgr.sendNotification( - source.getOid(), MODULE_NAME, MOVE_NOTIFICATION, - new Object[] { new Integer(sceneId) }); + // then send a forced move notification + SceneSender.forcedMove(source, sceneId); } - /** The invocation manager with which we interact. */ - protected static InvocationManager _invmgr; + /** The location provider we use to handle low-level location stuff. */ + protected LocationProvider _locprov; /** The scene registry with which we interact. */ - protected static SceneRegistry _screg; + protected SceneRegistry _screg; } diff --git a/src/java/com/threerings/whirled/server/SceneRegistry.java b/src/java/com/threerings/whirled/server/SceneRegistry.java index 340bd37b9..12a42db86 100644 --- a/src/java/com/threerings/whirled/server/SceneRegistry.java +++ b/src/java/com/threerings/whirled/server/SceneRegistry.java @@ -1,5 +1,5 @@ // -// $Id: SceneRegistry.java,v 1.15 2002/05/26 02:24:46 mdb Exp $ +// $Id: SceneRegistry.java,v 1.16 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.server; @@ -37,6 +37,9 @@ import com.threerings.whirled.server.persist.SceneRepository; */ public class SceneRegistry { + /** Used to provide scene-related server-side services. */ + public SceneProvider sceneprov; + /** * Constructs a scene registry, instructing it to load and store * scenes using the supplied scene repository. @@ -50,8 +53,8 @@ public class SceneRegistry _scfact = new DefaultRuntimeSceneFactory(); // create/register a scene provider with the invocation services - SceneProvider provider = new SceneProvider(invmgr, this); - invmgr.registerProvider(SceneProvider.MODULE_NAME, provider); + sceneprov = new SceneProvider(CrowdServer.plreg.locprov, this); + invmgr.registerDispatcher(new SceneDispatcher(sceneprov), true); } /** diff --git a/src/java/com/threerings/whirled/server/SceneSender.java b/src/java/com/threerings/whirled/server/SceneSender.java new file mode 100644 index 000000000..95288a087 --- /dev/null +++ b/src/java/com/threerings/whirled/server/SceneSender.java @@ -0,0 +1,30 @@ +// +// $Id: SceneSender.java,v 1.1 2002/08/14 19:07:57 mdb Exp $ + +package com.threerings.whirled.server; + +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationSender; +import com.threerings.whirled.client.SceneDecoder; +import com.threerings.whirled.client.SceneReceiver; + +/** + * Used to issue notifications to a {@link SceneReceiver} instance on a + * client. + */ +public class SceneSender extends InvocationSender +{ + /** + * Issues a notification that will result in a call to {@link + * SceneReceiver#forcedMove} on a client. + */ + public static void forcedMove ( + ClientObject target, int arg1) + { + sendNotification( + target, SceneDecoder.RECEIVER_CODE, SceneDecoder.FORCED_MOVE, + new Object[] { new Integer(arg1) }); + } + + // Generated on 11:25:48 08/12/02. +} diff --git a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java index 7c19cfd62..c6f681f18 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java +++ b/src/java/com/threerings/whirled/spot/client/SpotSceneDirector.java @@ -1,5 +1,5 @@ // -// $Id: SpotSceneDirector.java,v 1.17 2002/07/26 20:35:02 ray Exp $ +// $Id: SpotSceneDirector.java,v 1.18 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.spot.client; @@ -7,6 +7,9 @@ import java.util.Iterator; import com.samskivert.util.ResultListener; import com.samskivert.util.StringUtil; +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; + import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.DObjectManager; import com.threerings.presents.dobj.ObjectAccessException; @@ -31,8 +34,8 @@ import com.threerings.whirled.spot.data.SpotCodes; * Extends the standard scene director with facilities to move between * locations within a scene. */ -public class SpotSceneDirector - implements SpotCodes, Subscriber +public class SpotSceneDirector extends BasicDirector + implements SpotCodes, Subscriber, SpotService.ChangeLocListener { /** * This is used to communicate back to the caller of {@link @@ -64,6 +67,8 @@ public class SpotSceneDirector public SpotSceneDirector (WhirledContext ctx, LocationDirector locdir, SceneDirector scdir) { + super(ctx); + _ctx = ctx; _scdir = scdir; @@ -149,7 +154,7 @@ public class SpotSceneDirector } // issue a traversePortal request - SpotService.traversePortal( + _sservice.traversePortal( _ctx.getClient(), scene.getId(), portalId, sceneVer, _scdir); return true; } @@ -193,9 +198,9 @@ public class SpotSceneDirector // make a note that we're changing to this location _pendingLocId = locationId; _changeObserver = obs; + // and send the location change request - SpotService.changeLoc(_ctx.getClient(), scene.getId(), - locationId, this); + _sservice.changeLoc(_ctx.getClient(), scene.getId(), locationId, this); } /** @@ -243,15 +248,19 @@ public class SpotSceneDirector } // we're all clear to go - SpotService.clusterSpeak( - _ctx.getClient(), scene.getId(), _locationId, message, mode, this); + _sservice.clusterSpeak(_ctx.getClient(), scene.getId(), _locationId, + message, mode); return true; } - /** - * Called in response to a successful changeLoc request. - */ - public void handleChangeLocSucceeded (int invid, int clusterOid) + // documentation inherited + protected void fetchServices (Client client) + { + _sservice = (SpotService)client.requireService(SpotService.class); + } + + // documentation inherited from interface + public void changeLocSucceeded (int clusterOid) { ChangeObserver obs = _changeObserver; _locationId = _pendingLocId; @@ -285,10 +294,8 @@ public class SpotSceneDirector } } - /** - * Called in response to a failed changeLoc request. - */ - public void handleChangeLocFailed (int invid, String reason) + // documentation inherited from interface + public void requestFailed (String reason) { ChangeObserver obs = _changeObserver; int locId = _pendingLocId; @@ -343,6 +350,9 @@ public class SpotSceneDirector /** The active client context. */ protected WhirledContext _ctx; + /** Access to spot scene services. */ + protected SpotService _sservice; + /** The scene director with which we are cooperating. */ protected SceneDirector _scdir; diff --git a/src/java/com/threerings/whirled/spot/client/SpotService.java b/src/java/com/threerings/whirled/spot/client/SpotService.java index efc5d2c03..7337d3ce8 100644 --- a/src/java/com/threerings/whirled/spot/client/SpotService.java +++ b/src/java/com/threerings/whirled/spot/client/SpotService.java @@ -1,69 +1,53 @@ // -// $Id: SpotService.java,v 1.10 2002/07/22 22:54:04 ray Exp $ +// $Id: SpotService.java,v 1.11 2002/08/14 19:07:57 mdb Exp $ package com.threerings.whirled.spot.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; -import com.threerings.whirled.client.SceneDirector; - -import com.threerings.whirled.spot.Log; -import com.threerings.whirled.spot.data.SpotCodes; +import com.threerings.whirled.client.SceneService.SceneMoveListener; /** - * Provides a mechanism by which the client can request to move between + * Defines the mechanism by which the client can request to move between * locations within a scene and between scenes (taking exit and entry * locations into account). These services should not be used directly, * but instead should be accessed via the {@link SpotSceneDirector}. */ -public class SpotService implements SpotCodes +public interface SpotService extends InvocationService { /** * Requests to traverse the specified portal. */ - public static void traversePortal ( + public void traversePortal ( Client client, int sceneId, int portalId, int sceneVer, - Object rsptarget) + SceneMoveListener listener); + + /** + * Used to communicate responses to a {@link #changeLoc} request. + */ + public static interface ChangeLocListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { - new Integer(sceneId), new Integer(portalId), - new Integer(sceneVer) }; - invdir.invoke(MODULE_NAME, TRAVERSE_PORTAL_REQUEST, args, rsptarget); - Log.debug("Sent traversePortal request [sceneId=" + sceneId + - ", portalId=" + portalId + ", sceneVer=" + sceneVer + "]."); + /** + * Called when the change location request succeeded. + * + * @param clusterOid the object id of the cluster object + * associated with the new location. + */ + public void changeLocSucceeded (int clusterOid); } /** * Requests that this client's body be made to occupy the specified * location. */ - public static void changeLoc (Client client, int sceneId, int locationId, - Object rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { new Integer(sceneId), - new Integer(locationId) }; - invdir.invoke(MODULE_NAME, CHANGE_LOC_REQUEST, args, rsptarget); - Log.debug("Sent changeLoc request [sceneId=" + sceneId + - ", locId=" + locationId + "]."); - } + public void changeLoc (Client client, int sceneId, int locationId, + ChangeLocListener listener); /** * Requests that the supplied message be delivered to listeners in the * cluster to which the specified location belongs. */ - public static void clusterSpeak ( - Client client, int sceneId, int locationId, String message, - byte mode, SpotSceneDirector rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { - new Integer(sceneId), new Integer(locationId), message, - new Byte(mode) }; - invdir.invoke(MODULE_NAME, CLUSTER_SPEAK_REQUEST, args, rsptarget); - Log.debug("Sent clusterSpeak request [sceneId=" + sceneId + - ", locId=" + locationId + ", message=" + message + "]."); - } + public void clusterSpeak (Client client, int sceneId, int locationId, + String message, byte mode); } diff --git a/src/java/com/threerings/whirled/spot/data/SpotCodes.java b/src/java/com/threerings/whirled/spot/data/SpotCodes.java index 688fd25b1..6d37c4555 100644 --- a/src/java/com/threerings/whirled/spot/data/SpotCodes.java +++ b/src/java/com/threerings/whirled/spot/data/SpotCodes.java @@ -1,5 +1,5 @@ // -// $Id: SpotCodes.java,v 1.2 2002/04/15 18:06:20 mdb Exp $ +// $Id: SpotCodes.java,v 1.3 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.spot.data; @@ -13,39 +13,14 @@ import com.threerings.whirled.spot.client.SpotSceneDirector; */ public interface SpotCodes extends ChatCodes, SceneCodes { - /** The module name for the Spot services. */ - public static final String MODULE_NAME = "whirled!spot"; - - /** The message identifier for a traversePortal request. Such a - * request generates a moveTo response rather than a specialized - * response. */ - public static final String TRAVERSE_PORTAL_REQUEST = "TraversePortal"; - /** An error code indicating that the portal specified in a * traversePortal request does not exist. */ public static final String NO_SUCH_PORTAL = "m.no_such_portal"; - /** The message identifier for a changeLoc request. */ - public static final String CHANGE_LOC_REQUEST = "ChangeLoc"; - - /** The response identifier for a successful changeLoc request. This - * is mapped by the invocation services to a call to {@link - * SpotSceneDirector#handleChangeLocSucceeded}. */ - public static final String CHANGE_LOC_SUCCEEDED_RESPONSE = - "ChangeLocSucceeded"; - - /** The response identifier for a failed changeLoc request. This is - * mapped by the invocation services to a call to {@link - * SpotSceneDirector#handleChangeLocFailed}. */ - public static final String CHANGE_LOC_FAILED_RESPONSE = "ChangeLocFailed"; - /** An error code indicating that a location is occupied. Usually * generated by a failed changeLoc request. */ public static final String LOCATION_OCCUPIED = "m.location_occupied"; - /** The message identifier for a cluster speak request. */ - public static final String CLUSTER_SPEAK_REQUEST = "ClusterSpeak"; - /** The chat type code with which we register our cluster auxiliary * chat objects. Chat display implementations should interpret chat * messages with this type accordingly. */ diff --git a/src/java/com/threerings/whirled/spot/data/SpotMarshaller.java b/src/java/com/threerings/whirled/spot/data/SpotMarshaller.java new file mode 100644 index 000000000..dba50b515 --- /dev/null +++ b/src/java/com/threerings/whirled/spot/data/SpotMarshaller.java @@ -0,0 +1,93 @@ +// +// $Id: SpotMarshaller.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.spot.data; + +import com.threerings.presents.client.Client; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; +import com.threerings.whirled.client.SceneService.SceneMoveListener; +import com.threerings.whirled.data.SceneMarshaller.SceneMoveMarshaller; +import com.threerings.whirled.spot.client.SpotService; +import com.threerings.whirled.spot.client.SpotService.ChangeLocListener; + +/** + * Provides the implementation of the {@link SpotService} 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 SpotMarshaller extends InvocationMarshaller + implements SpotService +{ + // documentation inherited + public static class ChangeLocMarshaller extends ListenerMarshaller + implements ChangeLocListener + { + /** The method id used to dispatch {@link #changeLocSucceeded} + * responses. */ + public static final int CHANGE_LOC_SUCCEEDED = 0; + + // documentation inherited from interface + public void changeLocSucceeded (int arg1) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, CHANGE_LOC_SUCCEEDED, + new Object[] { new Integer(arg1) })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case CHANGE_LOC_SUCCEEDED: + ((ChangeLocListener)listener).changeLocSucceeded( + ((Integer)args[0]).intValue()); + return; + + default: + super.dispatchResponse(methodId, args); + } + } + } + + /** The method id used to dispatch {@link #traversePortal} requests. */ + public static final int TRAVERSE_PORTAL = 1; + + // documentation inherited from interface + public void traversePortal (Client arg1, int arg2, int arg3, int arg4, SceneMoveListener arg5) + { + SceneMoveMarshaller listener5 = new SceneMoveMarshaller(); + listener5.listener = arg5; + sendRequest(arg1, TRAVERSE_PORTAL, new Object[] { + new Integer(arg2), new Integer(arg3), new Integer(arg4), listener5 + }); + } + + /** The method id used to dispatch {@link #changeLoc} requests. */ + public static final int CHANGE_LOC = 2; + + // documentation inherited from interface + public void changeLoc (Client arg1, int arg2, int arg3, ChangeLocListener arg4) + { + ChangeLocMarshaller listener4 = new ChangeLocMarshaller(); + listener4.listener = arg4; + sendRequest(arg1, CHANGE_LOC, new Object[] { + new Integer(arg2), new Integer(arg3), listener4 + }); + } + + /** The method id used to dispatch {@link #clusterSpeak} requests. */ + public static final int CLUSTER_SPEAK = 3; + + // documentation inherited from interface + public void clusterSpeak (Client arg1, int arg2, int arg3, String arg4, byte arg5) + { + sendRequest(arg1, CLUSTER_SPEAK, new Object[] { + new Integer(arg2), new Integer(arg3), arg4, new Byte(arg5) + }); + } + + // Class file generated on 00:26:02 08/11/02. +} diff --git a/src/java/com/threerings/whirled/spot/server/SpotDispatcher.java b/src/java/com/threerings/whirled/spot/server/SpotDispatcher.java new file mode 100644 index 000000000..2c10c76b7 --- /dev/null +++ b/src/java/com/threerings/whirled/spot/server/SpotDispatcher.java @@ -0,0 +1,68 @@ +// +// $Id: SpotDispatcher.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.spot.server; + +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 com.threerings.whirled.client.SceneService.SceneMoveListener; +import com.threerings.whirled.data.SceneMarshaller.SceneMoveMarshaller; +import com.threerings.whirled.spot.client.SpotService; +import com.threerings.whirled.spot.client.SpotService.ChangeLocListener; +import com.threerings.whirled.spot.data.SpotMarshaller; + +/** + * Dispatches requests to the {@link SpotProvider}. + */ +public class SpotDispatcher extends InvocationDispatcher +{ + /** + * Creates a dispatcher that may be registered to dispatch invocation + * service requests for the specified provider. + */ + public SpotDispatcher (SpotProvider provider) + { + this.provider = provider; + } + + // documentation inherited + public InvocationMarshaller createMarshaller () + { + return new SpotMarshaller(); + } + + // documentation inherited + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + switch (methodId) { + case SpotMarshaller.TRAVERSE_PORTAL: + ((SpotProvider)provider).traversePortal( + source, + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue(), (SceneMoveListener)args[3] + ); + return; + + case SpotMarshaller.CHANGE_LOC: + ((SpotProvider)provider).changeLoc( + source, + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (ChangeLocListener)args[2] + ); + return; + + case SpotMarshaller.CLUSTER_SPEAK: + ((SpotProvider)provider).clusterSpeak( + source, + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), (String)args[2], ((Byte)args[3]).byteValue() + ); + return; + + default: + super.dispatchRequest(source, methodId, args); + } + } +} diff --git a/src/java/com/threerings/whirled/spot/server/SpotProvider.java b/src/java/com/threerings/whirled/spot/server/SpotProvider.java index 782862789..6ea8a4ba5 100644 --- a/src/java/com/threerings/whirled/spot/server/SpotProvider.java +++ b/src/java/com/threerings/whirled/spot/server/SpotProvider.java @@ -1,123 +1,117 @@ // -// $Id: SpotProvider.java,v 1.12 2002/07/22 22:54:04 ray Exp $ +// $Id: SpotProvider.java,v 1.13 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.spot.server; +import com.threerings.presents.data.ClientObject; import com.threerings.presents.dobj.RootDObjectManager; +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationManager; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceConfig; import com.threerings.crowd.data.PlaceObject; -import com.threerings.crowd.server.LocationProvider; +import com.threerings.crowd.server.PlaceRegistry; +import com.threerings.whirled.client.SceneService.SceneMoveListener; import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.server.SceneManager; import com.threerings.whirled.server.SceneRegistry; import com.threerings.whirled.spot.Log; +import com.threerings.whirled.spot.client.SpotService.ChangeLocListener; import com.threerings.whirled.spot.data.SpotCodes; import com.threerings.whirled.spot.data.SpotOccupantInfo; /** - * This class provides the server side of the spot services. + * Provides the server-side implementation of the spot services. */ -public class SpotProvider extends InvocationProvider - implements SpotCodes +public class SpotProvider + implements SpotCodes, InvocationProvider { /** - * Constructs a spot provider and registers it with the invocation - * manager to handle spot services. This need be done by a server that - * wishes to make use of the Spot services. + * Creates a spot provider that can be registered with the invocation + * manager to handle spot services. */ - public static void init ( - InvocationManager invmgr, SceneRegistry screg, RootDObjectManager omgr) + public SpotProvider (RootDObjectManager omgr, PlaceRegistry plreg, + SceneRegistry screg) { // we'll need these later + _plreg = plreg; _screg = screg; _omgr = omgr; - - // register a spot provider instance - invmgr.registerProvider(MODULE_NAME, new SpotProvider()); } /** * Processes a request from a client to traverse a portal. * * @param source the body object of the client making the request. - * @param invid the invocation service invocation id. * @param sceneId the source scene id. * @param portalId the portal in the source scene that is being * traversed. * @param sceneVer the version of the destination scene data that the * client has cached. + * @param listener the entity to which we communicate our response. */ - public void handleTraversePortalRequest ( - BodyObject source, int invid, int sceneId, int portalId, int sceneVer) + public void traversePortal (ClientObject caller, int sceneId, int portalId, + int sceneVer, SceneMoveListener listener) + throws InvocationException { - try { - // obtain the source scene - SpotSceneManager smgr = (SpotSceneManager) - _screg.getSceneManager(sceneId); - if (smgr == null) { - Log.warning("Traverse portal missing source scene " + - "[user=" + source.who() + ", sceneId=" + sceneId + - ", portalId=" + portalId + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); - } + // avoid cluttering up the method declaration with final keywords + final BodyObject fsource = (BodyObject)caller; + final int fportalId = portalId; + final int fsceneVer = sceneVer; + final SceneMoveListener flistener = listener; - // obtain the destination scene and location id - RuntimeSpotScene rss = (RuntimeSpotScene)smgr.getScene(); - int destSceneId = rss.getTargetSceneId(portalId); - final int destLocId = rss.getTargetLocationId(portalId); - - // make sure this portal has valid info - if (destSceneId == -1) { - Log.warning("Traverse portal provided with invalid portal " + - "[user=" + source.who() + ", sceneId=" + sceneId + - ", portalId=" + portalId + - ", destSceneId=" + destSceneId + "]."); - throw new ServiceFailedException(NO_SUCH_PORTAL); - } - - // avoid cluttering up the method declaration with final - // keywords - final BodyObject fsource = source; - final int finvid = invid; - final int fportalId = portalId; - final int fsceneVer = sceneVer; - - // create a callback object that will handle the resolution or - // failed resolution of the scene - SceneRegistry.ResolutionListener rl = - new SceneRegistry.ResolutionListener() { - public void sceneWasResolved (SceneManager scmgr) { - SpotSceneManager sscmgr = (SpotSceneManager)scmgr; - finishTraversePortalRequest( - fsource, finvid, sscmgr, fsceneVer, - fportalId, destLocId); - } - - public void sceneFailedToResolve ( - int rsceneId, Exception reason) { - Log.warning("Unable to resolve target scene " + - "[sceneId=" + rsceneId + - ", reason=" + reason + "]."); - // pretend like the scene doesn't exist to the client - sendResponse(fsource, finvid, MOVE_FAILED_RESPONSE, - NO_SUCH_PLACE); - } - }; - - // make sure the scene they are headed to is actually loaded into - // the server - _screg.resolveScene(destSceneId, rl); - - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); + // obtain the source scene + SpotSceneManager smgr = (SpotSceneManager) + _screg.getSceneManager(sceneId); + if (smgr == null) { + Log.warning("Traverse portal missing source scene " + + "[user=" + fsource.who() + ", sceneId=" + sceneId + + ", portalId=" + portalId + "]."); + throw new InvocationException(INTERNAL_ERROR); } + + // obtain the destination scene and location id + RuntimeSpotScene rss = (RuntimeSpotScene)smgr.getScene(); + int destSceneId = rss.getTargetSceneId(portalId); + final int destLocId = rss.getTargetLocationId(portalId); + + // make sure this portal has valid info + if (destSceneId == -1) { + Log.warning("Traverse portal provided with invalid portal " + + "[user=" + fsource.who() + ", sceneId=" + sceneId + + ", portalId=" + portalId + + ", destSceneId=" + destSceneId + "]."); + throw new InvocationException(NO_SUCH_PORTAL); + } + + // create a callback object that will handle the resolution or + // failed resolution of the scene + SceneRegistry.ResolutionListener rl = + new SceneRegistry.ResolutionListener() { + public void sceneWasResolved (SceneManager scmgr) { + SpotSceneManager sscmgr = (SpotSceneManager)scmgr; + finishTraversePortalRequest( + fsource, sscmgr, fsceneVer, fportalId, destLocId, + flistener); + } + + public void sceneFailedToResolve ( + int rsceneId, Exception reason) { + Log.warning("Unable to resolve target scene " + + "[sceneId=" + rsceneId + + ", reason=" + reason + "]."); + // pretend like the scene doesn't exist to the client + flistener.requestFailed(NO_SUCH_PLACE); + } + }; + + // make sure the scene they are headed to is actually loaded into + // the server + _screg.resolveScene(destSceneId, rl); } /** @@ -125,8 +119,8 @@ public class SpotProvider extends InvocationProvider * to have been loaded into the server. */ protected void finishTraversePortalRequest ( - BodyObject source, int invid, SpotSceneManager scmgr, - int sceneVer, int exitPortalId, int destLocId) + BodyObject source, SpotSceneManager scmgr, int sceneVer, + int exitPortalId, int destLocId, SceneMoveListener listener) { // move to the place object associated with this scene PlaceObject plobj = scmgr.getPlaceObject(); @@ -138,23 +132,21 @@ public class SpotProvider extends InvocationProvider try { // try doing the actual move - PlaceConfig config = LocationProvider.moveTo(source, ploid); + PlaceConfig config = _plreg.locprov.moveTo(source, ploid); // check to see if they need a newer version of the scene data SceneModel model = scmgr.getSceneModel(); if (sceneVer < model.version) { // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_PLUS_UPDATE_RESPONSE, - new Integer(ploid), config, model); + listener.moveSucceededPlusUpdate(ploid, config, model); } else { // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, - new Integer(ploid), config); + listener.moveSucceeded(ploid, config); } - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, MOVE_FAILED_RESPONSE, sfe.getMessage()); + } catch (InvocationException sfe) { + listener.requestFailed(sfe.getMessage()); // and let the destination scene manager know that we're no // longer coming in @@ -165,39 +157,35 @@ public class SpotProvider extends InvocationProvider /** * Processes a request from a client to move to a new location. */ - public void handleChangeLocRequest ( - BodyObject source, int invid, int sceneId, int locationId) + public void changeLoc (ClientObject caller, int sceneId, int locationId, + ChangeLocListener listener) + throws InvocationException { - try { - // look up the scene manager for the specified scene - SpotSceneManager smgr = (SpotSceneManager) - _screg.getSceneManager(sceneId); - if (smgr == null) { - Log.warning("User requested to change location in " + - "non-existent scene [user=" + source.who() + - ", sceneId=" + sceneId + - ", locId=" + locationId + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); - } + BodyObject source = (BodyObject)caller; - int locOid = smgr.handleChangeLocRequest(source, locationId); - sendResponse(source, invid, CHANGE_LOC_SUCCEEDED_RESPONSE, - new Integer(locOid)); - - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, CHANGE_LOC_FAILED_RESPONSE, - sfe.getMessage()); + // look up the scene manager for the specified scene + SpotSceneManager smgr = (SpotSceneManager) + _screg.getSceneManager(sceneId); + if (smgr == null) { + Log.warning("User requested to change location in " + + "non-existent scene [user=" + source.who() + + ", sceneId=" + sceneId + + ", locId=" + locationId + "]."); + throw new InvocationException(INTERNAL_ERROR); } + + int locOid = smgr.handleChangeLocRequest(source, locationId); + listener.changeLocSucceeded(locOid); } /** - * Handles {@link SpotCodes#CLUSTER_SPEAK_REQUEST} messages. + * Handles request to generate a speak message in the specified cluster. */ - public void handleClusterSpeakRequest ( - BodyObject source, int invid, int sceneId, int locId, String message, - byte mode) + public void clusterSpeak (ClientObject caller, int sceneId, int locationId, + String message, byte mode) { - sendClusterChatMessage(sceneId, locId, source.getOid(), + BodyObject source = (BodyObject)caller; + sendClusterChatMessage(sceneId, locationId, source.getOid(), source.username, null, message, mode); } @@ -220,7 +208,7 @@ public class SpotProvider extends InvocationProvider * from a real live human who wrote it in their native tongue). * @param message the text of the chat message. */ - public static void sendClusterChatMessage ( + public void sendClusterChatMessage ( int sceneId, int locId, int speakerOid, String speaker, String bundle, String message, byte mode) { @@ -242,9 +230,12 @@ public class SpotProvider extends InvocationProvider } } + /** The place registry with which we interoperate. */ + protected PlaceRegistry _plreg; + /** The scene registry with which we interoperate. */ - protected static SceneRegistry _screg; + protected SceneRegistry _screg; /** The object manager we use to do dobject stuff. */ - protected static RootDObjectManager _omgr; + protected RootDObjectManager _omgr; } diff --git a/src/java/com/threerings/whirled/spot/server/SpotSceneManager.java b/src/java/com/threerings/whirled/spot/server/SpotSceneManager.java index b17ab1362..b303ba9fc 100644 --- a/src/java/com/threerings/whirled/spot/server/SpotSceneManager.java +++ b/src/java/com/threerings/whirled/spot/server/SpotSceneManager.java @@ -1,5 +1,5 @@ // -// $Id: SpotSceneManager.java,v 1.15 2002/07/22 22:54:04 ray Exp $ +// $Id: SpotSceneManager.java,v 1.16 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.spot.server; @@ -10,9 +10,9 @@ import com.samskivert.util.StringUtil; import com.threerings.presents.dobj.DObject; import com.threerings.presents.dobj.Subscriber; import com.threerings.presents.dobj.ObjectAccessException; -import com.threerings.presents.server.ServiceFailedException; +import com.threerings.presents.server.InvocationException; -import com.threerings.crowd.chat.ChatProvider; +import com.threerings.crowd.chat.SpeakProvider; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.OccupantInfo; import com.threerings.whirled.server.SceneManager; @@ -82,13 +82,13 @@ public class SpotSceneManager extends SceneManager // now that we have our scene, we create chat objects for each of // the clusters in the scene - _clusterOids = new int[_sscene.getClusterCount()]; + _clusterObjs = new DObject[_sscene.getClusterCount()]; // create a subscriber that will grab the oids when we hear back // about object creation Subscriber sub = new Subscriber() { public void objectAvailable (DObject object) { - _clusterOids[_index++] = object.getOid(); + _clusterObjs[_index++] = object; } public void requestFailed (int oid, ObjectAccessException cause) { @@ -103,7 +103,7 @@ public class SpotSceneManager extends SceneManager }; // now issue the object creation requests - for (int i = 0; i < _clusterOids.length; i++) { + for (int i = 0; i < _clusterObjs.length; i++) { _omgr.createObject(DObject.class, sub); } @@ -148,12 +148,12 @@ public class SpotSceneManager extends SceneManager * which this location belongs or -1 if the location is not part of a * cluster. * - * @exception ServiceFailedException thrown with a reason code - * explaining the location change failure if there is a problem - * processing the location change request. + * @exception InvocationException thrown with a reason code explaining + * the location change failure if there is a problem processing the + * location change request. */ protected int handleChangeLocRequest (BodyObject source, int locationId) - throws ServiceFailedException + throws InvocationException { // make sure no one is already in the requested location int locidx = _sscene.getLocationIndex(locationId); @@ -161,14 +161,14 @@ public class SpotSceneManager extends SceneManager Log.warning("Ignoring request to move to non-existent location " + "[where=" + where() + ", user=" + source.who() + ", locId=" + locationId + "]."); - throw new ServiceFailedException(LOCATION_OCCUPIED); + throw new InvocationException(LOCATION_OCCUPIED); } else if (_locationOccs[locidx] > 0) { Log.info("Ignoring request to move to occupied location " + "[where=" + where() + ", user=" + source.who() + ", locId=" + locationId + ", occupantOid=" + _locationOccs[locidx] + "]."); - throw new ServiceFailedException(LOCATION_OCCUPIED); + throw new InvocationException(LOCATION_OCCUPIED); } // make sure they have an occupant info object in the place @@ -179,7 +179,7 @@ public class SpotSceneManager extends SceneManager Log.warning("Aiya! Can't update non-existent occupant info " + "with new location [where=" + where() + ", body=" + source.who() + "]."); - throw new ServiceFailedException(INTERNAL_ERROR); + throw new InvocationException(INTERNAL_ERROR); } // clear out any location they previously occupied @@ -209,7 +209,7 @@ public class SpotSceneManager extends SceneManager // figure out the cluster chat oid int clusterIdx = _sscene.getClusterIndex(locidx); - return (clusterIdx == -1) ? -1 : _clusterOids[clusterIdx]; + return (clusterIdx == -1) ? -1 : _clusterObjs[clusterIdx].getOid(); } /** @@ -241,10 +241,9 @@ public class SpotSceneManager extends SceneManager } // all is well, generate a chat notification - int clusterOid = _clusterOids[clusterIndex]; - if (clusterOid > 0) { - ChatProvider.sendChatMessage( - clusterOid, source, bundle, message, mode); + DObject clusterObj = _clusterObjs[clusterIndex]; + if (clusterObj != null) { + SpeakProvider.sendSpeak(clusterObj, source, bundle, message, mode); } else { Log.warning("Have no cluster object for CCREQ " + @@ -297,8 +296,8 @@ public class SpotSceneManager extends SceneManager /** A casted reference to our runtime scene instance. */ protected RuntimeSpotScene _sscene; - /** Oids of the cluster chat objects. */ - protected int[] _clusterOids; + /** Our cluster chat objects. */ + protected DObject[] _clusterObjs; /** Oids of the bodies that occupy each of our locations. */ protected int[] _locationOccs; diff --git a/src/java/com/threerings/whirled/zone/client/ZoneDecoder.java b/src/java/com/threerings/whirled/zone/client/ZoneDecoder.java new file mode 100644 index 000000000..baaa032cf --- /dev/null +++ b/src/java/com/threerings/whirled/zone/client/ZoneDecoder.java @@ -0,0 +1,52 @@ +// +// $Id: ZoneDecoder.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.zone.client; + +import com.threerings.presents.client.InvocationDecoder; +import com.threerings.whirled.zone.client.ZoneReceiver; + +/** + * Dispatches calls to a {@link ZoneReceiver} instance. + */ +public class ZoneDecoder extends InvocationDecoder +{ + /** The generated hash code used to identify this receiver class. */ + public static final String RECEIVER_CODE = "2d900cf54355111b4bb4befcdff42b82"; + + /** The method id used to dispatch {@link ZoneReceiver#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 ZoneDecoder (ZoneReceiver 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: + ((ZoneReceiver)receiver).forcedMove( + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue() + ); + return; + + default: + super.dispatchNotification(methodId, args); + } + } + + // Generated on 11:25:47 08/12/02. +} diff --git a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java index 761ee1c45..e19686be5 100644 --- a/src/java/com/threerings/whirled/zone/client/ZoneDirector.java +++ b/src/java/com/threerings/whirled/zone/client/ZoneDirector.java @@ -1,5 +1,5 @@ // -// $Id: ZoneDirector.java,v 1.8 2002/06/14 01:40:16 ray Exp $ +// $Id: ZoneDirector.java,v 1.9 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.zone.client; @@ -7,7 +7,9 @@ import java.util.ArrayList; import com.samskivert.util.ResultListener; -import com.threerings.presents.client.InvocationReceiver; +import com.threerings.presents.client.BasicDirector; +import com.threerings.presents.client.Client; + import com.threerings.crowd.data.PlaceConfig; import com.threerings.whirled.client.SceneDirector; @@ -15,7 +17,6 @@ import com.threerings.whirled.data.SceneModel; import com.threerings.whirled.util.WhirledContext; import com.threerings.whirled.zone.Log; -import com.threerings.whirled.zone.data.ZoneCodes; import com.threerings.whirled.zone.data.ZoneSummary; /** @@ -27,8 +28,8 @@ import com.threerings.whirled.zone.data.ZoneSummary; * summary which provides information on the zone which can be used to * generate an overview map or similar. */ -public class ZoneDirector - implements InvocationReceiver, ZoneCodes +public class ZoneDirector extends BasicDirector + implements ZoneReceiver, ZoneService.ZoneMoveListener { /** * Constructs a zone director with the supplied context, and delegate @@ -38,12 +39,13 @@ public class ZoneDirector */ public ZoneDirector (WhirledContext ctx, SceneDirector scdir) { + super(ctx); _ctx = ctx; _scdir = scdir; // register for zone notifications _ctx.getClient().getInvocationDirector().registerReceiver( - MODULE_NAME, this); + new ZoneDecoder(this)); } /** @@ -116,41 +118,46 @@ public class ZoneDirector } // issue a moveTo request - ZoneService.moveTo(_ctx.getClient(), zoneId, sceneId, sceneVers, this); + _zservice.moveTo(_ctx.getClient(), zoneId, sceneId, sceneVers, this); return true; } + // documentation inherited + protected void fetchServices (Client client) + { + _zservice = (ZoneService)client.requireService(ZoneService.class); + } + /** - * Called in response to a successful zoned moveTo + * Called in response to a successful {@link ZoneService#moveTo} * request. */ - public void handleMoveSucceeded ( - int invid, int placeId, PlaceConfig config, ZoneSummary summary) + public void moveSucceeded ( + int placeId, PlaceConfig config, ZoneSummary summary) { // keep track of the summary _summary = summary; // pass the rest off to the standard scene transition code - _scdir.handleMoveSucceeded(invid, placeId, config); + _scdir.moveSucceeded(placeId, config); // and let the zone observers know what's up notifyObservers(summary); } /** - * Called in response to a successful zoned moveTo + * Called in response to a successful {@link ZoneService#moveTo} * request when our cached scene was out of date and the server * determined that we needed an updated copy. */ - public void handleMoveSucceededPlusUpdate ( - int invid, int placeId, PlaceConfig config, ZoneSummary summary, - SceneModel model) + public void moveSucceededPlusUpdate ( + int placeId, PlaceConfig config, ZoneSummary summary, SceneModel model) { // keep track of the summary _summary = summary; // pass the rest off to the standard scene transition code - _scdir.handleMoveSucceededPlusUpdate(invid, placeId, config, model); + _scdir.moveSucceededPlusUpdate(placeId, config, model); // and let the zone observers know what's up notifyObservers(summary); @@ -159,22 +166,17 @@ public class ZoneDirector /** * Called in response to a failed zoned moveTo request. */ - public void handleMoveFailed (int invid, String reason) + public void requestFailed (String reason) { // let the scene director cope - _scdir.handleMoveFailed(invid, reason); + _scdir.requestFailed(reason); // and let the observers know what's up notifyObservers(reason); } - /** - * Called when the server has decided to forcibly move us to another - * zone and scene. The server first ejects us from our previous scene - * and then sends us a notification with our new location. We then - * turn around and issue a standard moveTo request. - */ - public void handleMoveNotification (int zoneId, int sceneId) + // documentation inherited from interface + public void forcedMove (int zoneId, int sceneId) { Log.info("Moving at request of server [zoneId=" + zoneId + ", sceneId=" + sceneId + "]."); @@ -218,6 +220,9 @@ public class ZoneDirector /** A reference to the scene director with which we coordinate. */ protected SceneDirector _scdir; + /** Provides access to zone services. */ + protected ZoneService _zservice; + /** A reference to the zone summary for the currently occupied * zone. */ protected ZoneSummary _summary; diff --git a/src/java/com/threerings/whirled/zone/client/ZoneReceiver.java b/src/java/com/threerings/whirled/zone/client/ZoneReceiver.java new file mode 100644 index 000000000..6fac092a5 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/client/ZoneReceiver.java @@ -0,0 +1,22 @@ +// +// $Id: ZoneReceiver.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.zone.client; + +import com.threerings.presents.client.InvocationReceiver; + +/** + * Defines, for the zone services, a set of notifications delivered + * asynchronously by the server to the client. + */ +public interface ZoneReceiver extends InvocationReceiver +{ + /** + * Used to communicate a required move notification to the client. The + * server will have removed the client from their existing scene and + * the client is then responsible for generating a {@link + * ZoneService#moveTo} request to move to the new scene in the + * specified zone. + */ + public void forcedMove (int zoneId, int sceneId); +} diff --git a/src/java/com/threerings/whirled/zone/client/ZoneService.java b/src/java/com/threerings/whirled/zone/client/ZoneService.java index fca0ca65e..613f9a41f 100644 --- a/src/java/com/threerings/whirled/zone/client/ZoneService.java +++ b/src/java/com/threerings/whirled/zone/client/ZoneService.java @@ -1,20 +1,32 @@ // -// $Id: ZoneService.java,v 1.5 2002/05/15 23:54:35 mdb Exp $ +// $Id: ZoneService.java,v 1.6 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.zone.client; import com.threerings.presents.client.Client; -import com.threerings.presents.client.InvocationDirector; +import com.threerings.presents.client.InvocationService; -import com.threerings.whirled.zone.Log; -import com.threerings.whirled.zone.data.ZoneCodes; +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.whirled.data.SceneModel; + +import com.threerings.whirled.zone.data.ZoneSummary; /** - * The zone service class provides the client interface to the zone - * related invocation services (e.g. moving between zones). + * Defines the client interface to the zone related invocation services + * (e.g. moving between zones). */ -public class ZoneService implements ZoneCodes +public interface ZoneService extends InvocationService { + public static interface ZoneMoveListener extends InvocationListener + { + public void moveSucceeded ( + int placeId, PlaceConfig config, ZoneSummary summary); + + public void moveSucceededPlusUpdate ( + int placeId, PlaceConfig config, ZoneSummary summary, + SceneModel model); + } + /** * Requests that that this client's body be moved to the specified * scene in the specified zone. @@ -23,17 +35,9 @@ public class ZoneService implements ZoneCodes * @param sceneId the scene id to which we want to move. * @param sceneVers the version number of the scene object that we * have in our local repository. - * @param rsptarget the object that will receive the callback when the + * @param listener the object that will receive the callback when the * request succeeds or fails. */ - public static void moveTo (Client client, int zoneId, int sceneId, - int sceneVers, Object rsptarget) - { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { - new Integer(zoneId), new Integer(sceneId), new Integer(sceneVers) }; - invdir.invoke(MODULE_NAME, MOVE_TO_REQUEST, args, rsptarget); - Log.debug("Sent moveTo request [zone=" + zoneId + - ", scene=" + sceneId + ", version=" + sceneVers + "]."); - } + public void moveTo (Client client, int zoneId, int sceneId, + int sceneVers, ZoneMoveListener listener); } diff --git a/src/java/com/threerings/whirled/zone/data/ZoneCodes.java b/src/java/com/threerings/whirled/zone/data/ZoneCodes.java index 92c5695ad..4a1312152 100644 --- a/src/java/com/threerings/whirled/zone/data/ZoneCodes.java +++ b/src/java/com/threerings/whirled/zone/data/ZoneCodes.java @@ -1,5 +1,5 @@ // -// $Id: ZoneCodes.java,v 1.1 2002/04/15 16:28:04 shaper Exp $ +// $Id: ZoneCodes.java,v 1.2 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.zone.data; @@ -10,9 +10,6 @@ import com.threerings.whirled.data.SceneCodes; */ public interface ZoneCodes extends SceneCodes { - /** The module name for the zone services. */ - public static final String MODULE_NAME = "whirled!zone"; - /** An error code indicating that a zone identified by a particular * zone id does not exist. Usually generated by a failed moveTo * request. */ diff --git a/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java b/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java new file mode 100644 index 000000000..2e6b5fc63 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/data/ZoneMarshaller.java @@ -0,0 +1,86 @@ +// +// $Id: ZoneMarshaller.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.zone.data; + +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.presents.client.Client; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.zone.client.ZoneService; +import com.threerings.whirled.zone.client.ZoneService.ZoneMoveListener; +import com.threerings.whirled.zone.data.ZoneSummary; + +/** + * Provides the implementation of the {@link ZoneService} 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 ZoneMarshaller extends InvocationMarshaller + implements ZoneService +{ + // documentation inherited + public static class ZoneMoveMarshaller extends ListenerMarshaller + implements ZoneMoveListener + { + /** The method id used to dispatch {@link #moveSucceeded} + * responses. */ + public static final int MOVE_SUCCEEDED = 0; + + // documentation inherited from interface + public void moveSucceeded (int arg1, PlaceConfig arg2, ZoneSummary arg3) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED, + new Object[] { new Integer(arg1), arg2, arg3 })); + } + /** The method id used to dispatch {@link #moveSucceededPlusUpdate} + * responses. */ + public static final int MOVE_SUCCEEDED_PLUS_UPDATE = 1; + + // documentation inherited from interface + public void moveSucceededPlusUpdate (int arg1, PlaceConfig arg2, ZoneSummary arg3, SceneModel arg4) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, MOVE_SUCCEEDED_PLUS_UPDATE, + new Object[] { new Integer(arg1), arg2, arg3, arg4 })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case MOVE_SUCCEEDED: + ((ZoneMoveListener)listener).moveSucceeded( + ((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2]); + return; + + case MOVE_SUCCEEDED_PLUS_UPDATE: + ((ZoneMoveListener)listener).moveSucceededPlusUpdate( + ((Integer)args[0]).intValue(), (PlaceConfig)args[1], (ZoneSummary)args[2], (SceneModel)args[3]); + 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, int arg3, int arg4, ZoneMoveListener arg5) + { + ZoneMoveMarshaller listener5 = new ZoneMoveMarshaller(); + listener5.listener = arg5; + sendRequest(arg1, MOVE_TO, new Object[] { + new Integer(arg2), new Integer(arg3), new Integer(arg4), listener5 + }); + } + + // Class file generated on 00:26:02 08/11/02. +} diff --git a/src/java/com/threerings/whirled/zone/server/ZoneDispatcher.java b/src/java/com/threerings/whirled/zone/server/ZoneDispatcher.java new file mode 100644 index 000000000..b742a9c90 --- /dev/null +++ b/src/java/com/threerings/whirled/zone/server/ZoneDispatcher.java @@ -0,0 +1,55 @@ +// +// $Id: ZoneDispatcher.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.zone.server; + +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; +import com.threerings.whirled.data.SceneModel; +import com.threerings.whirled.zone.client.ZoneService; +import com.threerings.whirled.zone.client.ZoneService.ZoneMoveListener; +import com.threerings.whirled.zone.data.ZoneMarshaller; +import com.threerings.whirled.zone.data.ZoneSummary; + +/** + * Dispatches requests to the {@link ZoneProvider}. + */ +public class ZoneDispatcher extends InvocationDispatcher +{ + /** + * Creates a dispatcher that may be registered to dispatch invocation + * service requests for the specified provider. + */ + public ZoneDispatcher (ZoneProvider provider) + { + this.provider = provider; + } + + // documentation inherited + public InvocationMarshaller createMarshaller () + { + return new ZoneMarshaller(); + } + + // documentation inherited + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + switch (methodId) { + case ZoneMarshaller.MOVE_TO: + ((ZoneProvider)provider).moveTo( + source, + ((Integer)args[0]).intValue(), ((Integer)args[1]).intValue(), ((Integer)args[2]).intValue(), (ZoneMoveListener)args[3] + ); + return; + + default: + super.dispatchRequest(source, methodId, args); + } + } +} diff --git a/src/java/com/threerings/whirled/zone/server/ZoneProvider.java b/src/java/com/threerings/whirled/zone/server/ZoneProvider.java index 189d9b54e..e3a8d2555 100644 --- a/src/java/com/threerings/whirled/zone/server/ZoneProvider.java +++ b/src/java/com/threerings/whirled/zone/server/ZoneProvider.java @@ -1,11 +1,11 @@ // -// $Id: ZoneProvider.java,v 1.9 2002/05/26 02:29:13 mdb Exp $ +// $Id: ZoneProvider.java,v 1.10 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.zone.server; -import com.threerings.presents.server.InvocationManager; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationException; import com.threerings.presents.server.InvocationProvider; -import com.threerings.presents.server.ServiceFailedException; import com.threerings.crowd.data.BodyObject; import com.threerings.crowd.data.PlaceConfig; @@ -17,6 +17,7 @@ import com.threerings.whirled.server.SceneRegistry; import com.threerings.whirled.server.SceneManager; import com.threerings.whirled.zone.Log; +import com.threerings.whirled.zone.client.ZoneService.ZoneMoveListener; import com.threerings.whirled.zone.data.ZoneCodes; import com.threerings.whirled.zone.data.ZoneSummary; import com.threerings.whirled.zone.data.ZonedBodyObject; @@ -26,7 +27,7 @@ import com.threerings.whirled.zone.data.ZonedBodyObject; * from zone to zone. */ public class ZoneProvider - extends InvocationProvider implements ZoneCodes + implements ZoneCodes, InvocationProvider { /** * Constructs a zone provider that will interoperate with the supplied @@ -34,10 +35,10 @@ public class ZoneProvider * constructed and registered by the {@link ZoneRegistry}, which a * zone-using system must create and initialize in their server. */ - public ZoneProvider ( - InvocationManager invmgr, ZoneRegistry zonereg, SceneRegistry screg) + public ZoneProvider (LocationProvider locprov, ZoneRegistry zonereg, + SceneRegistry screg) { - _invmgr = invmgr; + _locprov = locprov; _zonereg = zonereg; _screg = screg; } @@ -45,45 +46,45 @@ public class ZoneProvider /** * Processes a request from a client to move to a scene in a new zone. * - * @param source the user requesting the move. + * @param caller the user requesting the move. * @param invid the invocation id of the request. * @param zoneId the qualified zone id of the new zone. * @param sceneId the identifier of the new scene. - * @param sceneVew the version of the scene model currently held by + * @param sceneVer the version of the scene model currently held by * the client. + * @param listener the entity to inform of success or failure. */ - public void handleMoveToRequest (BodyObject source, int invid, - int zoneId, int sceneId, int sceneVer) + public void moveTo (ClientObject caller, int zoneId, int sceneId, + int sceneVer, ZoneMoveListener listener) + throws InvocationException { + // avoid cluttering up the method declaration with final keywords + final BodyObject fsource = (BodyObject)caller; + final int fsceneId = sceneId; + final int fsceneVer = sceneVer; + final ZoneMoveListener flistener = listener; + // look up the zone manager for the zone ZoneManager zmgr = _zonereg.getZoneManager(zoneId); if (zmgr == null) { Log.warning("Requested to enter a zone for which we have no " + - "manager [user=" + source + + "manager [user=" + fsource.who() + ", zoneId=" + zoneId + "]."); - sendResponse(source, invid, MOVE_FAILED_RESPONSE, NO_SUCH_ZONE); - return; + throw new InvocationException(NO_SUCH_ZONE); } - // avoid cluttering up the method declaration with final keywords - final BodyObject fsource = source; - final int finvid = invid; - final int fsceneId = sceneId; - final int fsceneVer = sceneVer; - // resolve the zone! ZoneManager.ResolutionListener zl = new ZoneManager.ResolutionListener() { public void zoneWasResolved (ZoneSummary summary) { - continueMoveToRequest(fsource, finvid, summary, - fsceneId, fsceneVer); + continueMoveTo( + fsource, summary, fsceneId, fsceneVer, flistener); } public void zoneFailedToResolve (int zoneId, Exception reason) { Log.warning("Unable to resolve zone [zoneId=" + zoneId + ", reason=" + reason + "]."); - sendResponse(fsource, finvid, - MOVE_FAILED_RESPONSE, NO_SUCH_ZONE); + flistener.requestFailed(NO_SUCH_ZONE); } }; zmgr.resolveZone(zoneId, zl); @@ -92,21 +93,21 @@ public class ZoneProvider /** * This is called after we have resolved our zone. */ - protected void continueMoveToRequest ( - BodyObject source, int invid, ZoneSummary summary, - int sceneId, int sceneVer) + protected void continueMoveTo ( + BodyObject source, ZoneSummary summary, int sceneId, int sceneVer, + ZoneMoveListener listener) { // avoid cluttering up the method declaration with final keywords final BodyObject fsource = source; - final int finvid = invid; final ZoneSummary fsum = summary; final int fsceneVer = sceneVer; + final ZoneMoveListener flistener = listener; // give the zone manager a chance to veto the request ZoneManager zmgr = _zonereg.getZoneManager(summary.zoneId); String errmsg = zmgr.ratifyBodyEntry(source, summary.zoneId); if (errmsg != null) { - sendResponse(fsource, finvid, MOVE_FAILED_RESPONSE, errmsg); + listener.requestFailed(errmsg); return; } @@ -116,15 +117,14 @@ public class ZoneProvider new SceneRegistry.ResolutionListener() { public void sceneWasResolved (SceneManager scmgr) { - finishMoveToRequest(fsource, finvid, fsum, scmgr, fsceneVer); + finishMoveTo(fsource, fsum, scmgr, fsceneVer, flistener); } public void sceneFailedToResolve (int sceneId, Exception reason) { Log.warning("Unable to resolve scene [sceneid=" + sceneId + ", reason=" + reason + "]."); // pretend like the scene doesn't exist to the client - sendResponse(fsource, finvid, - MOVE_FAILED_RESPONSE, NO_SUCH_PLACE); + flistener.requestFailed(NO_SUCH_PLACE); } }; @@ -137,9 +137,9 @@ public class ZoneProvider * This is called after the scene to which we are moving is guaranteed * to have been loaded into the server. */ - protected void finishMoveToRequest ( - BodyObject source, int invid, ZoneSummary summary, - SceneManager scmgr, int sceneVersion) + protected void finishMoveTo ( + BodyObject source, ZoneSummary summary, SceneManager scmgr, + int sceneVersion, ZoneMoveListener listener) { // move to the place object associated with this scene PlaceObject plobj = scmgr.getPlaceObject(); @@ -147,7 +147,7 @@ public class ZoneProvider try { // try doing the actual move - PlaceConfig config = LocationProvider.moveTo(source, ploid); + PlaceConfig config = _locprov.moveTo(source, ploid); // now that we've finally moved, we can update the user object // with the new zone id @@ -157,23 +157,19 @@ public class ZoneProvider SceneModel model = scmgr.getSceneModel(); if (sceneVersion < model.version) { // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_PLUS_UPDATE_RESPONSE, - new Object[] { new Integer(ploid), config, - summary, model }); + listener.moveSucceededPlusUpdate(ploid, config, summary, model); } else { // then send the moveTo response - sendResponse(source, invid, MOVE_SUCCEEDED_RESPONSE, - new Integer(ploid), config, summary); + listener.moveSucceeded(ploid, config, summary); } // let the zone manager know that someone just came on in ZoneManager zmgr = _zonereg.getZoneManager(summary.zoneId); zmgr.bodyDidEnterZone(source, summary.zoneId); - } catch (ServiceFailedException sfe) { - sendResponse(source, invid, - MOVE_FAILED_RESPONSE, sfe.getMessage()); + } catch (InvocationException ie) { + listener.requestFailed(ie.getMessage()); } } @@ -182,23 +178,21 @@ public class ZoneProvider * request to move to the specified new zone and scene. This is the * zone-equivalent to {@link LocationProvider#moveBody}. */ - public static void moveBody (BodyObject source, int zoneId, int sceneId) + public void moveBody (BodyObject source, int zoneId, int sceneId) { // first remove them from their old place - LocationProvider.leaveOccupiedPlace(source); + _locprov.leaveOccupiedPlace(source); - // then send a move notification - _invmgr.sendNotification( - source.getOid(), MODULE_NAME, MOVE_NOTIFICATION, - new Object[] { new Integer(zoneId), new Integer(sceneId) }); + // then send a forced move notification + ZoneSender.forcedMove(source, zoneId, sceneId); } - /** The invocation manager with which we interact. */ - protected static InvocationManager _invmgr; + /** The entity that handles basic location changes. */ + protected LocationProvider _locprov; /** The zone registry with which we communicate. */ - protected static ZoneRegistry _zonereg; + protected ZoneRegistry _zonereg; /** The scene registry with which we communicate. */ - protected static SceneRegistry _screg; + protected SceneRegistry _screg; } diff --git a/src/java/com/threerings/whirled/zone/server/ZoneRegistry.java b/src/java/com/threerings/whirled/zone/server/ZoneRegistry.java index 9e24f4b05..1a2b294a9 100644 --- a/src/java/com/threerings/whirled/zone/server/ZoneRegistry.java +++ b/src/java/com/threerings/whirled/zone/server/ZoneRegistry.java @@ -1,11 +1,13 @@ // -// $Id: ZoneRegistry.java,v 1.7 2002/05/26 02:24:46 mdb Exp $ +// $Id: ZoneRegistry.java,v 1.8 2002/08/14 19:07:58 mdb Exp $ package com.threerings.whirled.zone.server; import com.samskivert.util.HashIntMap; import com.threerings.presents.server.InvocationManager; + +import com.threerings.crowd.server.PlaceRegistry; import com.threerings.whirled.server.SceneRegistry; import com.threerings.whirled.zone.Log; @@ -17,15 +19,19 @@ import com.threerings.whirled.zone.util.ZoneUtil; */ public class ZoneRegistry { + /** Implements the server-side of the zone-related services. */ + public ZoneProvider zoneprov; + /** * Creates a zone manager with the supplied configuration. */ - public ZoneRegistry (InvocationManager invmgr, SceneRegistry screg) + public ZoneRegistry (InvocationManager invmgr, PlaceRegistry plreg, + SceneRegistry screg) { // create a zone provider and register it with the invocation // services - ZoneProvider provider = new ZoneProvider(invmgr, this, screg); - invmgr.registerProvider(ZoneProvider.MODULE_NAME, provider); + zoneprov = new ZoneProvider(plreg.locprov, this, screg); + invmgr.registerDispatcher(new ZoneDispatcher(zoneprov), true); } /** diff --git a/src/java/com/threerings/whirled/zone/server/ZoneSender.java b/src/java/com/threerings/whirled/zone/server/ZoneSender.java new file mode 100644 index 000000000..b058d673c --- /dev/null +++ b/src/java/com/threerings/whirled/zone/server/ZoneSender.java @@ -0,0 +1,30 @@ +// +// $Id: ZoneSender.java,v 1.1 2002/08/14 19:07:58 mdb Exp $ + +package com.threerings.whirled.zone.server; + +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationSender; +import com.threerings.whirled.zone.client.ZoneDecoder; +import com.threerings.whirled.zone.client.ZoneReceiver; + +/** + * Used to issue notifications to a {@link ZoneReceiver} instance on a + * client. + */ +public class ZoneSender extends InvocationSender +{ + /** + * Issues a notification that will result in a call to {@link + * ZoneReceiver#forcedMove} on a client. + */ + public static void forcedMove ( + ClientObject target, int arg1, int arg2) + { + sendNotification( + target, ZoneDecoder.RECEIVER_CODE, ZoneDecoder.FORCED_MOVE, + new Object[] { new Integer(arg1), new Integer(arg2) }); + } + + // Generated on 11:25:47 08/12/02. +} diff --git a/tests/build.xml b/tests/build.xml index a35923536..4a1426a8b 100644 --- a/tests/build.xml +++ b/tests/build.xml @@ -18,9 +18,11 @@ + + - - + + @@ -33,14 +35,40 @@ + + + + + + + + + + + + + + + + + + + + + + - - + + - + @@ -52,7 +80,7 @@ - diff --git a/tests/src/java/com/threerings/parlor/TestClient.java b/tests/src/java/com/threerings/parlor/TestClient.java index b4ac98afe..4c1f8f77b 100644 --- a/tests/src/java/com/threerings/parlor/TestClient.java +++ b/tests/src/java/com/threerings/parlor/TestClient.java @@ -1,5 +1,5 @@ // -// $Id: TestClient.java,v 1.8 2002/04/15 14:38:45 shaper Exp $ +// $Id: TestClient.java,v 1.9 2002/08/14 19:07:59 mdb Exp $ package com.threerings.parlor; @@ -99,35 +99,33 @@ public class TestClient System.exit(0); } - public void invitationReceived (int inviteId, String inviter, - GameConfig config) + public void invitationReceived (Invitation invite) { - Log.info("Invitation received [inviteId=" + inviteId + - ", inviter=" + inviter + ", config=" + config + "]."); + Log.info("Invitation received [invite=" + invite + "]."); // accept the invitation. we're game... - _pardtr.accept(inviteId); + invite.accept(); } - public void invitationCancelled (int inviteId) + public void invitationCancelled (Invitation invite) { - Log.info("Invitation cancelled [inviteId=" + inviteId + "]."); + Log.info("Invitation cancelled [invite=" + invite + "]."); } - public void invitationAccepted (int inviteId) + public void invitationAccepted (Invitation invite) { - Log.info("Invitation accepted [inviteId=" + inviteId + "]."); + Log.info("Invitation accepted [invite=" + invite + "]."); } - public void invitationRefused (int inviteId, String message) + public void invitationRefused (Invitation invite, String message) { - Log.info("Invitation refused [inviteId=" + inviteId + + Log.info("Invitation refused [invite=" + invite + ", message=" + message + "]."); } - public void invitationCountered (int inviteId, GameConfig config) + public void invitationCountered (Invitation invite, GameConfig config) { - Log.info("Invitation countered [inviteId=" + inviteId + + Log.info("Invitation countered [invite=" + invite + ", config=" + config + "]."); } diff --git a/tests/src/java/com/threerings/parlor/TestServer.java b/tests/src/java/com/threerings/parlor/TestServer.java index 5dceab2df..adaaa9fbb 100644 --- a/tests/src/java/com/threerings/parlor/TestServer.java +++ b/tests/src/java/com/threerings/parlor/TestServer.java @@ -1,5 +1,5 @@ // -// $Id: TestServer.java,v 1.4 2002/03/28 22:32:33 mdb Exp $ +// $Id: TestServer.java,v 1.5 2002/08/14 19:07:59 mdb Exp $ package com.threerings.parlor; @@ -23,7 +23,7 @@ public class TestServer extends CrowdServer super.init(); // initialize our parlor manager - parmgr.init(invmgr); + parmgr.init(invmgr, plreg); Log.info("Parlor server initialized."); } diff --git a/tests/src/java/com/threerings/presents/client/TestClient.java b/tests/src/java/com/threerings/presents/client/TestClient.java index 9d3fc0986..e5f877f19 100644 --- a/tests/src/java/com/threerings/presents/client/TestClient.java +++ b/tests/src/java/com/threerings/presents/client/TestClient.java @@ -1,5 +1,5 @@ // -// $Id: TestClient.java,v 1.13 2002/02/09 20:47:11 mdb Exp $ +// $Id: TestClient.java,v 1.14 2002/08/14 19:07:59 mdb Exp $ package com.threerings.presents.client; @@ -14,7 +14,9 @@ import com.threerings.presents.server.TestObject; * A standalone test client. */ public class TestClient - implements Client.Invoker, ClientObserver, Subscriber, EventListener + implements Client.Invoker, SessionObserver, Subscriber, EventListener, + TestService.TestFuncListener, TestService.TestOidListener, + TestReceiver { public void setClient (Client client) { @@ -39,29 +41,15 @@ public class TestClient public void clientDidLogon (Client client) { Log.info("Client did logon [client=" + client + "]."); - // register our test notification receiver - client.getInvocationDirector().registerReceiver(TestService.MODULE, - new TestReceiver()); + + // register ourselves as a test notification receiver + client.getInvocationDirector().registerReceiver( + new TestDecoder(this)); + // get the test object id - TestService.getTestOid(client, this); - } - - public void clientFailedToLogon (Client client, Exception cause) - { - Log.info("Client failed to logon [client=" + client + - ", cause=" + cause + "]."); - } - - public void clientConnectionFailed (Client client, Exception cause) - { - Log.info("Client connection failed [client=" + client + - ", cause=" + cause + "]."); - } - - public boolean clientWillLogoff (Client client) - { - Log.info("Client will logoff [client=" + client + "]."); - return true; + TestService service = (TestService) + client.requireService(TestService.class); + service.getTestOid(client, this); } public void clientDidLogoff (Client client) @@ -99,15 +87,30 @@ public class TestClient } } - public void handleTestSucceeded (int invid, String one, int two) + // documentation inherited from interface + public void testSucceeded (String one, int two) { Log.info("Got test response [one=" + one + ", two=" + two + "]."); } - public void handleGotTestOid (int invid, int oid) + // documentation inherited from interface + public void gotTestOid (int testOid) { // subscribe to the test object - _client.getDObjectManager().subscribeToObject(oid, this); + _client.getDObjectManager().subscribeToObject(testOid, this); + } + + // documentation inherited from interface + public void requestFailed (String reason) + { + Log.info("Urk! Request failed [reason=" + reason + "]."); + } + + // documentation inherited from interface + public void receivedTest (int one, String two) + { + Log.info("Received test notification [one=" + one + + ", two=" + two + "]."); } public static void main (String[] args) diff --git a/tests/src/java/com/threerings/presents/client/TestDecoder.java b/tests/src/java/com/threerings/presents/client/TestDecoder.java new file mode 100644 index 000000000..779aef66c --- /dev/null +++ b/tests/src/java/com/threerings/presents/client/TestDecoder.java @@ -0,0 +1,52 @@ +// +// $Id: TestDecoder.java,v 1.1 2002/08/14 19:07:59 mdb Exp $ + +package com.threerings.presents.client; + +import com.threerings.presents.client.InvocationDecoder; +import com.threerings.presents.client.TestReceiver; + +/** + * Dispatches calls to a {@link TestReceiver} instance. + */ +public class TestDecoder extends InvocationDecoder +{ + /** The generated hash code used to identify this receiver class. */ + public static final String RECEIVER_CODE = "b4b66d24b85d870d04c8da3524c188eb"; + + /** The method id used to dispatch {@link TestReceiver#receivedTest} + * notifications. */ + public static final int RECEIVED_TEST = 1; + + /** + * Creates a decoder that may be registered to dispatch invocation + * service notifications to the specified receiver. + */ + public TestDecoder (TestReceiver 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_TEST: + ((TestReceiver)receiver).receivedTest( + ((Integer)args[0]).intValue(), (String)args[1] + ); + return; + + default: + super.dispatchNotification(methodId, args); + } + } + + // Generated on 12:14:10 08/12/02. +} diff --git a/tests/src/java/com/threerings/presents/client/TestReceiver.java b/tests/src/java/com/threerings/presents/client/TestReceiver.java index 4040872f7..7a6c1c813 100644 --- a/tests/src/java/com/threerings/presents/client/TestReceiver.java +++ b/tests/src/java/com/threerings/presents/client/TestReceiver.java @@ -1,15 +1,15 @@ // -// $Id: TestReceiver.java,v 1.5 2001/11/08 02:07:36 mdb Exp $ +// $Id: TestReceiver.java,v 1.6 2002/08/14 19:07:59 mdb Exp $ package com.threerings.presents.client; -import com.threerings.presents.Log; - -public class TestReceiver implements InvocationReceiver +/** + * A test of the invocation notification services. + */ +public interface TestReceiver extends InvocationReceiver { - public void handleTestNotification (int invid, int one, String two) - { - Log.info("Received tell notification [invid=" + invid + - ", one=" + one + ", two=" + two + "]."); - } + /** + * Dispatches a test notification. + */ + public void receivedTest (int one, String two); } diff --git a/tests/src/java/com/threerings/presents/client/TestService.java b/tests/src/java/com/threerings/presents/client/TestService.java index 10c9749eb..c8c536d8f 100644 --- a/tests/src/java/com/threerings/presents/client/TestService.java +++ b/tests/src/java/com/threerings/presents/client/TestService.java @@ -1,30 +1,31 @@ // -// $Id: TestService.java,v 1.5 2001/11/08 02:07:36 mdb Exp $ +// $Id: TestService.java,v 1.6 2002/08/14 19:07:59 mdb Exp $ package com.threerings.presents.client; -import com.threerings.presents.Log; - /** * A test of the invocation services. */ -public class TestService +public interface TestService extends InvocationService { - public static final String MODULE = "test"; - - public static void test ( - Client client, String one, int two, Object rsptarget) + /** Used to dispatch responses to {@link #test} requests. */ + public static interface TestFuncListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[] { one, new Integer(two) }; - invdir.invoke(MODULE, "Test", args, rsptarget); - Log.info("Sent test request [one=" + one + ", two=" + two + "]."); + /** Informs listener of successful {@link #test} request. */ + public void testSucceeded (String one, int two); } - public static void getTestOid (Client client, Object rsptarget) + /** Issues a test request. */ + public void test ( + Client client, String one, int two, TestFuncListener listener); + + /** Used to dispatch responses to {@link #getTestOid} requests. */ + public static interface TestOidListener extends InvocationListener { - InvocationDirector invdir = client.getInvocationDirector(); - Object[] args = new Object[0]; - invdir.invoke(MODULE, "GetTestOid", args, rsptarget); + /** Communicates test oid to listener. */ + public void gotTestOid (int testOid); } + + /** Issues a request for the test oid. */ + public void getTestOid (Client client, TestOidListener listener); } diff --git a/tests/src/java/com/threerings/presents/data/TestMarshaller.java b/tests/src/java/com/threerings/presents/data/TestMarshaller.java new file mode 100644 index 000000000..e6b5d0b94 --- /dev/null +++ b/tests/src/java/com/threerings/presents/data/TestMarshaller.java @@ -0,0 +1,112 @@ +// +// $Id: TestMarshaller.java,v 1.1 2002/08/14 19:07:59 mdb Exp $ + +package com.threerings.presents.data; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.TestService; +import com.threerings.presents.client.TestService.TestFuncListener; +import com.threerings.presents.client.TestService.TestOidListener; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.dobj.InvocationResponseEvent; + +/** + * Provides the implementation of the {@link TestService} 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 TestMarshaller extends InvocationMarshaller + implements TestService +{ + // documentation inherited + public static class TestFuncMarshaller extends ListenerMarshaller + implements TestFuncListener + { + /** The method id used to dispatch {@link #testSucceeded} + * responses. */ + public static final int TEST_SUCCEEDED = 0; + + // documentation inherited from interface + public void testSucceeded (String arg1, int arg2) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, TEST_SUCCEEDED, + new Object[] { arg1, new Integer(arg2) })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case TEST_SUCCEEDED: + ((TestFuncListener)listener).testSucceeded( + (String)args[0], ((Integer)args[1]).intValue()); + return; + + default: + super.dispatchResponse(methodId, args); + } + } + } + + // documentation inherited + public static class TestOidMarshaller extends ListenerMarshaller + implements TestOidListener + { + /** The method id used to dispatch {@link #gotTestOid} + * responses. */ + public static final int GOT_TEST_OID = 0; + + // documentation inherited from interface + public void gotTestOid (int arg1) + { + omgr.postEvent(new InvocationResponseEvent( + callerOid, requestId, GOT_TEST_OID, + new Object[] { new Integer(arg1) })); + } + + // documentation inherited + public void dispatchResponse (int methodId, Object[] args) + { + switch (methodId) { + case GOT_TEST_OID: + ((TestOidListener)listener).gotTestOid( + ((Integer)args[0]).intValue()); + return; + + default: + super.dispatchResponse(methodId, args); + } + } + } + + /** The method id used to dispatch {@link #test} requests. */ + public static final int TEST = 1; + + // documentation inherited from interface + public void test (Client arg1, String arg2, int arg3, TestFuncListener arg4) + { + TestFuncMarshaller listener4 = new TestFuncMarshaller(); + listener4.listener = arg4; + sendRequest(arg1, TEST, new Object[] { + arg2, new Integer(arg3), listener4 + }); + } + + /** The method id used to dispatch {@link #getTestOid} requests. */ + public static final int GET_TEST_OID = 2; + + // documentation inherited from interface + public void getTestOid (Client arg1, TestOidListener arg2) + { + TestOidMarshaller listener2 = new TestOidMarshaller(); + listener2.listener = arg2; + sendRequest(arg1, GET_TEST_OID, new Object[] { + listener2 + }); + } + + // Class file generated on 14:28:55 08/12/02. +} diff --git a/tests/src/java/com/threerings/presents/dobj/DSetTest.java b/tests/src/java/com/threerings/presents/dobj/DSetTest.java new file mode 100644 index 000000000..700006f42 --- /dev/null +++ b/tests/src/java/com/threerings/presents/dobj/DSetTest.java @@ -0,0 +1,67 @@ +// +// $Id: DSetTest.java,v 1.1 2002/08/14 19:08:00 mdb Exp $ + +package com.threerings.presents.dobj; + +import java.util.ArrayList; + +import junit.framework.Test; +import junit.framework.TestCase; + +/** + * Tests the {@link DSet} class. + */ +public class DSetTest extends TestCase +{ + public static class TestEntry implements DSet.Entry + { + public TestEntry (int value) + { + _value = new Integer(value); + } + + public Comparable getKey () + { + return _value; + } + + public String toString () + { + return _value.toString(); + } + + protected Integer _value; + } + + public DSetTest () + { + super(DSetTest.class.getName()); + } + + public void runTest () + { + ArrayList seed = new ArrayList(); + seed.add(new TestEntry(15)); + seed.add(new TestEntry(7)); + seed.add(new TestEntry(3)); + seed.add(new TestEntry(29)); + seed.add(new TestEntry(32)); + + DSet set = new DSet(seed.iterator()); + System.out.println(set.add(new TestEntry(15)) + ": " + set); + System.out.println(set.add(new TestEntry(9)) + ": " + set); + System.out.println(set.remove(new TestEntry(32)) + ": " + set); + System.out.println(set.add(new TestEntry(32)) + ": " + set); + } + + public static Test suite () + { + return new DSetTest(); + } + + public static void main (String[] args) + { + DSetTest test = new DSetTest(); + test.runTest(); + } +} diff --git a/tests/src/java/com/threerings/presents/server/TestDispatcher.java b/tests/src/java/com/threerings/presents/server/TestDispatcher.java new file mode 100644 index 000000000..dd4781a2c --- /dev/null +++ b/tests/src/java/com/threerings/presents/server/TestDispatcher.java @@ -0,0 +1,60 @@ +// +// $Id: TestDispatcher.java,v 1.1 2002/08/14 19:08:00 mdb Exp $ + +package com.threerings.presents.server; + +import com.threerings.presents.client.Client; +import com.threerings.presents.client.TestService; +import com.threerings.presents.client.TestService.TestFuncListener; +import com.threerings.presents.client.TestService.TestOidListener; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.data.InvocationMarshaller; +import com.threerings.presents.data.TestMarshaller; +import com.threerings.presents.server.InvocationDispatcher; +import com.threerings.presents.server.InvocationException; + +/** + * Dispatches requests to the {@link TestProvider}. + */ +public class TestDispatcher extends InvocationDispatcher +{ + /** + * Creates a dispatcher that may be registered to dispatch invocation + * service requests for the specified provider. + */ + public TestDispatcher (TestProvider provider) + { + this.provider = provider; + } + + // documentation inherited + public InvocationMarshaller createMarshaller () + { + return new TestMarshaller(); + } + + // documentation inherited + public void dispatchRequest ( + ClientObject source, int methodId, Object[] args) + throws InvocationException + { + switch (methodId) { + case TestMarshaller.TEST: + ((TestProvider)provider).test( + source, + (String)args[0], ((Integer)args[1]).intValue(), (TestFuncListener)args[2] + ); + return; + + case TestMarshaller.GET_TEST_OID: + ((TestProvider)provider).getTestOid( + source, + (TestOidListener)args[0] + ); + return; + + default: + super.dispatchRequest(source, methodId, args); + } + } +} diff --git a/tests/src/java/com/threerings/presents/server/TestProvider.java b/tests/src/java/com/threerings/presents/server/TestProvider.java index f45f0c4de..fa83b0858 100644 --- a/tests/src/java/com/threerings/presents/server/TestProvider.java +++ b/tests/src/java/com/threerings/presents/server/TestProvider.java @@ -1,36 +1,38 @@ // -// $Id: TestProvider.java,v 1.10 2001/11/08 02:57:03 mdb Exp $ +// $Id: TestProvider.java,v 1.11 2002/08/14 19:08:00 mdb Exp $ package com.threerings.presents.server; import com.threerings.presents.Log; import com.threerings.presents.client.TestService; +import com.threerings.presents.client.TestService.TestFuncListener; +import com.threerings.presents.client.TestService.TestOidListener; import com.threerings.presents.data.ClientObject; /** * A test of the invocation services. */ -public class TestProvider extends InvocationProvider +public class TestProvider implements InvocationProvider { - public void handleTestRequest ( - ClientObject source, int invid, String one, int two) + public void test ( + ClientObject caller, String one, int two, TestFuncListener listener) { Log.info("Test request [one=" + one + ", two=" + two + "]."); // issue a test notification just for kicks - Object[] args = new Object[] { new Integer(1), "two" }; - PresentsServer.invmgr.sendNotification( - source.getOid(), TestService.MODULE, "Test", args); + TestSender.sendTest(caller, 1, "two"); // and issue a response to this invocation request - sendResponse(source, invid, "TestSucceeded", one, new Integer(two)); + listener.testSucceeded(one, two); } - public void handleGetTestOidRequest (ClientObject source, int invid) + public void getTestOid (ClientObject caller, TestOidListener listener) { - Log.info("Handling get test oid [src=" + source.getOid() + - ", invid=" + invid + "]."); - int oid = TestServer.testobj.getOid(); - sendResponse(source, invid, "GotTestOid", new Integer(oid)); + Log.info("Handling get test oid [src=" + caller + "]."); + + // issue a test notification just for kicks + TestSender.sendTest(caller, 1, "two"); + + listener.gotTestOid(TestServer.testobj.getOid()); } } diff --git a/tests/src/java/com/threerings/presents/server/TestSender.java b/tests/src/java/com/threerings/presents/server/TestSender.java new file mode 100644 index 000000000..17438d4e2 --- /dev/null +++ b/tests/src/java/com/threerings/presents/server/TestSender.java @@ -0,0 +1,30 @@ +// +// $Id: TestSender.java,v 1.1 2002/08/14 19:08:01 mdb Exp $ + +package com.threerings.presents.server; + +import com.threerings.presents.client.TestDecoder; +import com.threerings.presents.client.TestReceiver; +import com.threerings.presents.data.ClientObject; +import com.threerings.presents.server.InvocationSender; + +/** + * Used to issue notifications to a {@link TestReceiver} instance on a + * client. + */ +public class TestSender extends InvocationSender +{ + /** + * Issues a notification that will result in a call to {@link + * TestReceiver#receivedTest} on a client. + */ + public static void sendTest ( + ClientObject target, int arg1, String arg2) + { + sendNotification( + target, TestDecoder.RECEIVER_CODE, TestDecoder.RECEIVED_TEST, + new Object[] { new Integer(arg1), arg2 }); + } + + // Generated on 12:14:10 08/12/02. +} diff --git a/tests/src/java/com/threerings/presents/server/TestServer.java b/tests/src/java/com/threerings/presents/server/TestServer.java index 7c3264706..b24aa7c4b 100644 --- a/tests/src/java/com/threerings/presents/server/TestServer.java +++ b/tests/src/java/com/threerings/presents/server/TestServer.java @@ -1,5 +1,5 @@ // -// $Id: TestServer.java,v 1.8 2002/03/28 22:32:33 mdb Exp $ +// $Id: TestServer.java,v 1.9 2002/08/14 19:08:01 mdb Exp $ package com.threerings.presents.server; @@ -15,6 +15,9 @@ public class TestServer extends PresentsServer { super.init(); + // register our test provider + invmgr.registerDispatcher(new TestDispatcher(new TestProvider()), true); + // create a test object Subscriber sub = new Subscriber() {