diff --git a/src/java/com/threerings/presents/client/Client.java b/src/java/com/threerings/presents/client/Client.java index dad06c5db..2df498329 100644 --- a/src/java/com/threerings/presents/client/Client.java +++ b/src/java/com/threerings/presents/client/Client.java @@ -208,7 +208,7 @@ public class Client */ public DObjectManager getDObjectManager () { - return (_comm != null) ? _comm.getDObjectManager() : null; + return _omgr; } /** @@ -408,6 +408,37 @@ public class Client return true; } + /** + * Called by the {@link ClientDObjectMgr} when our bootstrap + * notification arrives. If the client and server are being run in + * "merged" mode in a single JVM, this is how the client is configured + * with the server's distributed object manager and provided with + * bootstrap data. + */ + public void gotBootstrap (BootstrapData data, DObjectManager omgr) + { + Log.debug("Got bootstrap " + data + "."); + + // keep these around for interested parties + _bstrap = data; + _omgr = omgr; + + // extract bootstrap information + _cloid = data.clientOid; + + // initialize our invocation director + _invdir.init(omgr, _cloid, this); + + // send a few pings to the server to establish the clock offset + // between this client and server standard time + establishClockDelta(System.currentTimeMillis()); + + // we can't quite call initialization completed at this point + // because we need for the invocation director to fully initialize + // (which requires a round trip to the server) before turning the + // client loose to do things like request invocation services + } + /** * Called every five seconds; ensures that we ping the server if we * haven't communicated in a long while and periodically resyncs the @@ -452,12 +483,14 @@ public class Client */ protected void establishClockDelta (long now) { - // create a new delta calculator and start the process - _dcalc = new DeltaCalculator(); - PingRequest req = new PingRequest(); - _comm.postMessage(req); - _dcalc.sentPing(req); - _lastSync = now; + if (_comm != null) { + // create a new delta calculator and start the process + _dcalc = new DeltaCalculator(); + PingRequest req = new PingRequest(); + _comm.postMessage(req); + _dcalc.sentPing(req); + _lastSync = now; + } } /** @@ -535,6 +568,7 @@ public class Client public void run () { // clear out our references _comm = null; + _omgr = null; _clobj = null; _cloid = -1; // and let our invocation director know we're logged off @@ -543,32 +577,6 @@ public class Client }); } - /** - * Called by the omgr when a bootstrap notification arrives. - */ - void gotBootstrap (BootstrapData data) - { - Log.debug("Got bootstrap " + data + "."); - - // keep this around for interested parties - _bstrap = data; - - // extract bootstrap information - _cloid = data.clientOid; - - // initialize our invocation director - _invdir.init(_comm.getDObjectManager(), _cloid, this); - - // send a few pings to the server to establish the clock offset - // between this client and server standard time - establishClockDelta(System.currentTimeMillis()); - - // we can't quite call initialization completed at this point - // because we need for the invocation director to fully initialize - // (which requires a round trip to the server) before turning the - // client loose to do things like request invocation services - } - /** * Called when we receive a pong packet. We may be in the process of * calculating the client/server time differential, or we may have @@ -670,6 +678,9 @@ public class Client * client thread (which is also the AWT thread). */ protected RunQueue _runQueue; + /** The distributed object manager we're using during this session. */ + protected DObjectManager _omgr; + /** The data associated with our authentication response. */ protected AuthResponseData _authData; diff --git a/src/java/com/threerings/presents/client/ClientDObjectMgr.java b/src/java/com/threerings/presents/client/ClientDObjectMgr.java index a5322a5f6..24dba6a1a 100644 --- a/src/java/com/threerings/presents/client/ClientDObjectMgr.java +++ b/src/java/com/threerings/presents/client/ClientDObjectMgr.java @@ -184,7 +184,7 @@ public class ClientDObjectMgr // do the proper thing depending on the object if (obj instanceof BootstrapNotification) { BootstrapData data = ((BootstrapNotification)obj).getData(); - _client.gotBootstrap(data); + _client.gotBootstrap(data, this); } else if (obj instanceof EventNotification) { DEvent evt = ((EventNotification)obj).getEvent(); diff --git a/src/java/com/threerings/presents/client/Communicator.java b/src/java/com/threerings/presents/client/Communicator.java index 2572fd00f..6fa44b111 100644 --- a/src/java/com/threerings/presents/client/Communicator.java +++ b/src/java/com/threerings/presents/client/Communicator.java @@ -83,16 +83,6 @@ public class Communicator _client = client; } - /** - * Returns the distributed object manager in effect for this session. - * This instance is only valid while the client is connected to the - * server. - */ - public DObjectManager getDObjectManager () - { - return _omgr; - } - /** * Logs on to the server and initiates our full-duplex message * exchange. diff --git a/src/java/com/threerings/presents/server/LocalDObjectMgr.java b/src/java/com/threerings/presents/server/LocalDObjectMgr.java new file mode 100644 index 000000000..39a2a787e --- /dev/null +++ b/src/java/com/threerings/presents/server/LocalDObjectMgr.java @@ -0,0 +1,61 @@ +// +// $Id$ +// +// Narya library - tools for developing networked games +// Copyright (C) 2002-2005 Three Rings Design, Inc., All Rights Reserved +// http://www.threerings.net/code/narya/ +// +// This library is free software; you can redistribute it and/or modify it +// under the terms of the GNU Lesser General Public License as published +// by the Free Software Foundation; either version 2.1 of the License, or +// (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +package com.threerings.presents.server; + +import java.awt.EventQueue; + +import com.threerings.presents.dobj.DEvent; + +/** + * A special version of the distributed object manager, modified to + * operate on the AWT thread so that it can run in a client with a GUI and + * provide a "light" server for local operation of a normally distributed + * application. + */ +public class LocalDObjectMgr extends PresentsDObjectMgr +{ + // documentation inherited + public synchronized boolean isDispatchThread () + { + return EventQueue.isDispatchThread(); + } + + // documentation inherited + public void postEvent (final DEvent event) + { + EventQueue.invokeLater(new Runnable() { + public void run () { + processUnit(event); + } + }); + } + + // documentation inherited + public void postRunnable (Runnable unit) + { + // we just pass this right on to the AWT event queue rather than + // running them through processUnit() which would basically just + // call run() though we lose a tiny bit of inconsequential + // accounting data + EventQueue.invokeLater(unit); + } +} diff --git a/src/java/com/threerings/presents/server/PresentsDObjectMgr.java b/src/java/com/threerings/presents/server/PresentsDObjectMgr.java index 0837d34ef..16def2970 100644 --- a/src/java/com/threerings/presents/server/PresentsDObjectMgr.java +++ b/src/java/com/threerings/presents/server/PresentsDObjectMgr.java @@ -196,61 +196,68 @@ public class PresentsDObjectMgr } while (isRunning()) { - // pop the next unit off the queue - Object unit = _evqueue.get(); - long start = 0L; - if (_eventCount % UNIT_PROFILING_INTERVAL == 0) { - start = _timer.highResCounter(); - } - - try { - if (unit instanceof Runnable) { - // if this is a runnable, it's just an executable unit - // that should be invoked - ((Runnable)unit).run(); - - } else if (unit instanceof CompoundEvent) { - processCompoundEvent((CompoundEvent)unit); - - } else { - processEvent((DEvent)unit); - } - - } catch (Exception e) { - Log.warning("Execution unit failed [unit=" + unit + "]."); - Log.logStackTrace(e); - } catch (OutOfMemoryError oome) { - handleFatalError(unit, oome); - } catch (StackOverflowError soe) { - handleFatalError(unit, soe); - } - - if (start != 0L) { - long elapsed = _timer.highResCounter() - start; - - // convert the elapsed time to microseconds - elapsed = elapsed * 1000000 / _timer.highResFrequency(); - - // report excessively long units - if (elapsed > 500000) { - Log.warning("Unit '" + StringUtil.safeToString(unit) + - " [" + StringUtil.shortClassName(unit) + - "]' ran for " + elapsed + "ms."); - } - - // record the time spent processing this unit - String cname = StringUtil.shortClassName(unit); - UnitProfile uprof = (UnitProfile)_profiles.get(cname); - if (uprof == null) { - _profiles.put(cname, uprof = new UnitProfile()); - } - uprof.record(start, elapsed); - } + // pop the next unit off the queue and process it + processUnit(_evqueue.get()); } Log.info("DOMGR exited."); } + /** + * Processes a single unit from the queue. + */ + protected void processUnit (Object unit) + { + long start = 0L; + if (_eventCount % UNIT_PROFILING_INTERVAL == 0) { + start = _timer.highResCounter(); + } + + try { + if (unit instanceof Runnable) { + // if this is a runnable, it's just an executable unit + // that should be invoked + ((Runnable)unit).run(); + + } else if (unit instanceof CompoundEvent) { + processCompoundEvent((CompoundEvent)unit); + + } else { + processEvent((DEvent)unit); + } + + } catch (Exception e) { + Log.warning("Execution unit failed [unit=" + unit + "]."); + Log.logStackTrace(e); + } catch (OutOfMemoryError oome) { + handleFatalError(unit, oome); + } catch (StackOverflowError soe) { + handleFatalError(unit, soe); + } + + if (start != 0L) { + long elapsed = _timer.highResCounter() - start; + + // convert the elapsed time to microseconds + elapsed = elapsed * 1000000 / _timer.highResFrequency(); + + // report excessively long units + if (elapsed > 500000) { + Log.warning("Unit '" + StringUtil.safeToString(unit) + + " [" + StringUtil.shortClassName(unit) + + "]' ran for " + elapsed + "ms."); + } + + // record the time spent processing this unit + String cname = StringUtil.shortClassName(unit); + UnitProfile uprof = (UnitProfile)_profiles.get(cname); + if (uprof == null) { + _profiles.put(cname, uprof = new UnitProfile()); + } + uprof.record(start, elapsed); + } + } + /** * Performs the processing associated with a compound event, notifying * listeners and the like. diff --git a/src/java/com/threerings/presents/server/PresentsServer.java b/src/java/com/threerings/presents/server/PresentsServer.java index f5aa34550..690d687e9 100644 --- a/src/java/com/threerings/presents/server/PresentsServer.java +++ b/src/java/com/threerings/presents/server/PresentsServer.java @@ -109,7 +109,7 @@ public class PresentsServer SignalManager.registerSignalHandler(SignalManager.SIGINT, this); // create our distributed object manager - omgr = new PresentsDObjectMgr(); + omgr = createDObjectManager(); // create and start up our invoker invoker = new PresentsInvoker(omgr); @@ -136,6 +136,14 @@ public class PresentsServer }.schedule(REPORT_INTERVAL, true); } + /** + * Creates the distributed object manager to be used on this server. + */ + protected PresentsDObjectMgr createDObjectManager () + { + return new PresentsDObjectMgr(); + } + /** * Returns the port on which the connection manager will listen for * client connections.