diff --git a/src/java/com/threerings/jme/JmeApp.java b/src/java/com/threerings/jme/JmeApp.java index 79d59c54b..f7ca2d453 100644 --- a/src/java/com/threerings/jme/JmeApp.java +++ b/src/java/com/threerings/jme/JmeApp.java @@ -27,6 +27,10 @@ import com.samskivert.util.Queue; import com.samskivert.util.RunQueue; import com.samskivert.util.StringUtil; +import com.jme.renderer.Camera; +import com.jme.renderer.ColorRGBA; +import com.jme.renderer.Renderer; + import com.jme.scene.Node; import com.jme.scene.state.LightState; import com.jme.scene.state.ZBufferState; @@ -36,18 +40,20 @@ import com.jme.system.JmeException; import com.jme.system.PropertiesIO; import com.jme.system.lwjgl.LWJGLPropertiesDialog; -import com.jme.app.AbstractGame; +import com.jme.ui.UIFonts; +import com.jme.ui.UIColorScheme; + import com.jme.input.InputHandler; import com.jme.input.InputSystem; + import com.jme.light.PointLight; import com.jme.math.Vector3f; -import com.jme.renderer.Camera; -import com.jme.renderer.ColorRGBA; import com.jme.util.Timer; import com.threerings.presents.client.Client; import com.threerings.jme.input.GodViewHandler; +import com.threerings.jme.input.HardwareMouse; /** * Defines a basic application framework providing integration with the @@ -57,19 +63,6 @@ import com.threerings.jme.input.GodViewHandler; public class JmeApp implements RunQueue { - /** - * Returns our average frames per second since the last call to this - * method. - */ - public float getFramesPerSecond () - { - float now = _timer.getTime(), delta = now - _lastFPSStamp; - float fps = _frames / delta; - _lastFPSStamp = now; - _frames = 0; - return fps; - } - /** * Configures the target frame rate in frames per second. */ @@ -81,6 +74,15 @@ public class JmeApp _targetFrameTicks = _timer.getResolution() / framesPerSecond; } + /** + * Returns a context implementation that provides access to all the + * necessary bits. + */ + public JmeContext getContext () + { + return _ctx; + } + /** * Does the main initialization of the application. This method should * be called first, and then the {@link #run} method should be called @@ -126,6 +128,9 @@ public class JmeApp // initialize the lighting initLighting(); + // initialize the UI support stuff + initInterface(); + // update everything for the zeroth tick _root.updateGeometricState(0f, true); _root.updateRenderState(); @@ -239,10 +244,14 @@ public class JmeApp protected void initInput () { _input = new GodViewHandler(this, _camera, _properties.getRenderer()); + _bufferedInput = new InputHandler(); -// /** Signal to all key inputs they should work Nx faster. */ -// input.setKeySpeed(150f); -// input.setMouseSpeed(1f); + // we don't hide the cursor + InputSystem.getMouseInput().setCursorVisible(true); + HardwareMouse mouse = new HardwareMouse("Mouse"); + mouse.setMouseInput(InputSystem.getMouseInput()); + _input.setMouse(mouse); + _bufferedInput.setMouse(mouse); } /** @@ -276,6 +285,17 @@ public class JmeApp _root.setRenderState(_lights); } + /** + * Initializes our user interface bits. + */ + protected void initInterface () + { + String[] names = { "main" }; + String[] locs = { StatsDisplay.DEFAULT_JME_FONT }; + _fonts = new UIFonts(names, locs); + _colorScheme = new UIColorScheme(); + } + /** * Processes a single frame. */ @@ -287,7 +307,6 @@ public class JmeApp render(frameStart); _display.getRenderer().displayBackBuffer(); - _frames++; // now process events or sleep until the next frame (assume zero // frame duration to start to ensure that we always process at @@ -320,6 +339,7 @@ public class JmeApp // update the input system float timePerFrame = _timer.getTimePerFrame(); _input.update(timePerFrame); + _bufferedInput.update(timePerFrame); // run all of the controllers attached to nodes _root.updateGeometricState(timePerFrame, true); @@ -406,25 +426,53 @@ public class JmeApp return cfgdir + File.separator + file; } + /** Provides access to various needed bits. */ + protected JmeContext _ctx = new JmeContext() { + public Renderer getRenderer () { + return _display.getRenderer(); + } + + public Node getRoot () { + return _root; + } + + public InputHandler getInputHandler () { + return _input; + } + + public InputHandler getBufferedInputHandler () { + return _bufferedInput; + } + + public UIColorScheme getColorScheme () { + return _colorScheme; + } + + public UIFonts getFonts () { + return _fonts; + } + }; + protected Timer _timer; protected Thread _dispatchThread; protected Queue _evqueue = new Queue(); - protected boolean _finished; protected PropertiesIO _properties; protected DisplaySystem _display; protected Camera _camera; protected InputHandler _input; + protected InputHandler _bufferedInput; + + protected long _targetFrameTicks; + protected boolean _finished; + protected int _failures; protected Node _root; protected LightState _lights; protected StatsDisplay _stats; - protected int _failures; - - protected int _frames; - protected float _lastFPSStamp; - protected long _targetFrameTicks; + protected UIColorScheme _colorScheme; + protected UIFonts _fonts; /** If we fail 100 frames in a row, stick a fork in ourselves. */ protected static final int MAX_SUCCESSIVE_FAILURES = 100; diff --git a/src/java/com/threerings/jme/JmeContext.java b/src/java/com/threerings/jme/JmeContext.java new file mode 100644 index 000000000..95da239ec --- /dev/null +++ b/src/java/com/threerings/jme/JmeContext.java @@ -0,0 +1,54 @@ +// +// $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.jme; + +import com.jme.input.InputHandler; +import com.jme.renderer.Renderer; +import com.jme.scene.Node; + +import com.jme.ui.UIColorScheme; +import com.jme.ui.UIFonts; + +/** + * Provides access to the various bits needed by things that operate in + * JME land. + */ +public interface JmeContext +{ + /** Returns the renderer being used to draw everythign. */ + public Renderer getRenderer (); + + /** Returns the root of our scene graph. */ + public Node getRoot (); + + /** Returns our main (unbuffered) input handler. */ + public InputHandler getInputHandler (); + + /** Returns our buffered input handler. */ + public InputHandler getBufferedInputHandler (); + + /** Returns the color scheme to be used by UI elements. */ + public UIColorScheme getColorScheme (); + + /** Returns our font collection. */ + public UIFonts getFonts (); +} diff --git a/src/java/com/threerings/jme/chat/ChatView.java b/src/java/com/threerings/jme/chat/ChatView.java new file mode 100644 index 000000000..d34f1e4d1 --- /dev/null +++ b/src/java/com/threerings/jme/chat/ChatView.java @@ -0,0 +1,195 @@ +// +// $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.jme.chat; + +import java.util.StringTokenizer; + +import com.jme.image.Texture; +import com.jme.math.Vector3f; +import com.jme.scene.Controller; +import com.jme.scene.Node; +import com.jme.scene.Text; +import com.jme.scene.state.AlphaState; +import com.jme.scene.state.TextureState; +import com.jme.ui.UIEditBox; +import com.jme.ui.UIObject; +import com.jme.util.TextureManager; + +import com.threerings.util.Name; + +import com.threerings.crowd.chat.client.ChatDirector; +import com.threerings.crowd.chat.client.ChatDisplay; +import com.threerings.crowd.chat.data.ChatCodes; +import com.threerings.crowd.chat.data.ChatMessage; +import com.threerings.crowd.chat.data.SystemMessage; +import com.threerings.crowd.chat.data.UserMessage; +import com.threerings.crowd.data.PlaceObject; + +import com.threerings.jme.JmeContext; +import com.threerings.jme.Log; + +/** + * Displays chat messages and allows for their input. + */ +public class ChatView extends Node + implements ChatDisplay +{ + public ChatView (JmeContext ctx, ChatDirector chatdtr) + { + super("ChatView"); + + _chatdtr = chatdtr; + + // create an alpha state that will allow us to blend the text on + // top of whatever else is below + AlphaState astate = ctx.getRenderer().createAlphaState(); + astate.setBlendEnabled(true); + astate.setSrcFunction(AlphaState.SB_SRC_ALPHA); + astate.setDstFunction(AlphaState.DB_ONE); + astate.setTestEnabled(true); + astate.setTestFunction(AlphaState.TF_GREATER); + astate.setEnabled(true); + + // create a font texture + TextureState font = ctx.getRenderer().createTextureState(); + font.setTexture( + TextureManager.loadTexture( + getClass().getClassLoader().getResource(DEFAULT_JME_FONT), + Texture.MM_LINEAR, Texture.FM_LINEAR)); + font.setEnabled(true); + + _entry = new UIEditBox( + "Entry", 20, 20, 300, 20, ctx.getInputHandler(), + ctx.getBufferedInputHandler(), ctx.getColorScheme(), + ctx.getFonts(), "main", "", 65.0f, 0.0f, UIObject.INVERSE_BORDER); + attachChild(_entry); + + // create a fixed number of text lines to display our text + _text = new Text[5]; + _history = new String[_text.length]; + int ypos = 40; + for (int ii = 0; ii < _text.length; ii++) { + _history[ii] = "Line " + ii; + _text[ii] = new Text("History" + ii, _history[ii]); + _text[ii].setLocalTranslation(new Vector3f(0, ypos, 0)); + ypos += 20; + attachChild(_text[ii]); + } + + setRenderState(font); + setRenderState(astate); + + // add a controller that will update our edit field + addController(new Controller() { + public void update (float time) { + _entry.update(time); + } + }); + } + + public void willEnterPlace (PlaceObject plobj) + { + _chatdtr.addChatDisplay(this); + } + + public void didLeavePlace (PlaceObject plobj) + { + _chatdtr.removeChatDisplay(this); + } + + // documentation inherited from interface ChatDisplay + public void clear () + { + String text = _entry.getText(); + + // if the message to send begins with /tell then parse it and + // generate a tell request rather than a speak request + if (text.startsWith("/tell")) { + StringTokenizer tok = new StringTokenizer(text); + // there should be at least three tokens: '/tell target word' + if (tok.countTokens() < 3) { + displayError("Usage: /tell username message"); + return; + } + + // skip the /tell and grab the username + tok.nextToken(); + String username = tok.nextToken(); + + // now strip off everything up to the username to get the + // message + int uidx = text.indexOf(username); + String message = text.substring(uidx + username.length()).trim(); + + // request to send this text as a tell message + _chatdtr.requestTell(new Name(username), message, null); + + } else if (text.startsWith("/clear")) { + // clear the chat box + _chatdtr.clearDisplays(); + + } else { + // request to send this text as a chat message + _chatdtr.requestSpeak(text); + } + + // clear out the input because we sent a request + _entry.setText(""); + } + + // documentation inherited from interface ChatDisplay + public void displayMessage (ChatMessage msg) + { + if (msg instanceof UserMessage) { + UserMessage umsg = (UserMessage) msg; + if (umsg.localtype == ChatCodes.USER_CHAT_TYPE) { + append("[" + umsg.speaker + " whispers] " + umsg.message); + } else { + append("<" + umsg.speaker + "> " + umsg.message); + } + + } else if (msg instanceof SystemMessage) { + append(msg.message); + + } else { + Log.warning("Received unknown message type: " + msg + "."); + } + } + + protected void displayError (String message) + { + append(message); + } + + protected void append (String text) + { + } + + protected ChatDirector _chatdtr; + protected UIEditBox _entry; + + protected String[] _history; + protected Text[] _text; + + protected static final String DEFAULT_JME_FONT = + "com/jme/app/defaultfont.tga"; +} diff --git a/src/java/com/threerings/jme/input/HardwareMouse.java b/src/java/com/threerings/jme/input/HardwareMouse.java new file mode 100644 index 000000000..2a1e85f49 --- /dev/null +++ b/src/java/com/threerings/jme/input/HardwareMouse.java @@ -0,0 +1,52 @@ +// +// $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.jme.input; + +import com.jme.input.Mouse; +import com.jme.math.Vector3f; + +/** + * Implements the {@link Mouse} methods by simply querying the position of + * the hardware mouse under the assumption that it will not be turned off. + */ +public class HardwareMouse extends Mouse +{ + public HardwareMouse (String name) + { + super(name); + } + + public void update () + { + update(true); + } + + public void update (boolean updateState) + { + } + + public Vector3f getHotSpotPosition() { + hotSpotLocation.x = mouse.getXAbsolute(); + hotSpotLocation.y = mouse.getYAbsolute(); + return hotSpotLocation; + } +} diff --git a/tests/src/java/com/threerings/jme/client/JabberApp.java b/tests/src/java/com/threerings/jme/client/JabberApp.java index 8467a9fe6..a9cc3d370 100644 --- a/tests/src/java/com/threerings/jme/client/JabberApp.java +++ b/tests/src/java/com/threerings/jme/client/JabberApp.java @@ -21,6 +21,8 @@ package com.threerings.jme.client; +import com.jme.util.LoggingSystem; + import com.threerings.util.Name; import com.threerings.presents.client.Client; @@ -80,6 +82,8 @@ public class JabberApp extends JmeApp public static void main (String[] args) { + LoggingSystem.getLogger().setLevel(java.util.logging.Level.OFF); + String server = "localhost"; if (args.length > 0) { server = args[0]; diff --git a/tests/src/java/com/threerings/jme/client/JabberClient.java b/tests/src/java/com/threerings/jme/client/JabberClient.java index 4c43734ad..76d7cc9d3 100644 --- a/tests/src/java/com/threerings/jme/client/JabberClient.java +++ b/tests/src/java/com/threerings/jme/client/JabberClient.java @@ -21,6 +21,12 @@ package com.threerings.jme.client; +import com.jme.input.InputHandler; +import com.jme.renderer.Renderer; +import com.jme.scene.Node; +import com.jme.ui.UIColorScheme; +import com.jme.ui.UIFonts; + import com.samskivert.util.Config; import com.threerings.util.MessageManager; @@ -34,6 +40,8 @@ import com.threerings.crowd.client.OccupantDirector; import com.threerings.crowd.client.PlaceView; import com.threerings.crowd.util.CrowdContext; +import com.threerings.jme.JmeContext; + /** * The Jabber client takes care of instantiating all of the proper * managers and loading up all of the necessary configuration and getting @@ -49,6 +57,8 @@ public class JabberClient */ public void init (JabberApp app) { + _app = app; + // create our context _ctx = createContextImpl(); @@ -125,7 +135,7 @@ public class JabberClient * The context implementation. This provides access to all of the * objects and services that are needed by the operating client. */ - protected class JabberContextImpl implements CrowdContext + protected class JabberContextImpl implements CrowdContext, JmeContext { /** * Apparently the default constructor has default access, rather @@ -133,52 +143,68 @@ public class JabberClient * protected. Why, I don't know, but we need to be able to extend * this class elsewhere, so we need this. */ - protected JabberContextImpl () - { + protected JabberContextImpl () { } - public Client getClient () - { + public Client getClient () { return _client; } - public DObjectManager getDObjectManager () - { + public DObjectManager getDObjectManager () { return _client.getDObjectManager(); } - public Config getConfig () - { + public Config getConfig () { return _config; } - public LocationDirector getLocationDirector () - { + public LocationDirector getLocationDirector () { return _locdir; } - public OccupantDirector getOccupantDirector () - { + public OccupantDirector getOccupantDirector () { return _occdir; } - public ChatDirector getChatDirector () - { + public ChatDirector getChatDirector () { return _chatdir; } - public void setPlaceView (PlaceView view) - { + public void setPlaceView (PlaceView view) { // TBD } - public void clearPlaceView (PlaceView view) - { + public void clearPlaceView (PlaceView view) { // we'll just let the next place view replace our old one } + + public Renderer getRenderer () { + return _app.getContext().getRenderer(); + } + + public Node getRoot () { + return _app.getContext().getRoot(); + } + + public InputHandler getInputHandler () { + return _app.getContext().getInputHandler(); + } + + public InputHandler getBufferedInputHandler () { + return _app.getContext().getBufferedInputHandler(); + } + + public UIColorScheme getColorScheme () { + return _app.getContext().getColorScheme(); + } + + public UIFonts getFonts () { + return _app.getContext().getFonts(); + } } protected CrowdContext _ctx; + protected JabberApp _app; protected Config _config = new Config("jabber"); protected Client _client; diff --git a/tests/src/java/com/threerings/jme/client/JabberController.java b/tests/src/java/com/threerings/jme/client/JabberController.java index 81d434eb0..329381b6a 100644 --- a/tests/src/java/com/threerings/jme/client/JabberController.java +++ b/tests/src/java/com/threerings/jme/client/JabberController.java @@ -14,6 +14,6 @@ public class JabberController extends PlaceController { protected PlaceView createPlaceView (CrowdContext ctx) { - return null; + return new JabberView(ctx); } } diff --git a/tests/src/java/com/threerings/jme/client/JabberView.java b/tests/src/java/com/threerings/jme/client/JabberView.java new file mode 100644 index 000000000..920734129 --- /dev/null +++ b/tests/src/java/com/threerings/jme/client/JabberView.java @@ -0,0 +1,42 @@ +// +// $Id$ + +package com.threerings.jme.client; + +import com.threerings.crowd.client.PlaceView; +import com.threerings.crowd.data.PlaceObject; +import com.threerings.crowd.util.CrowdContext; + +import com.threerings.jme.JmeContext; +import com.threerings.jme.chat.ChatView; + +/** + * Manages the "view" when we're in the chat room. + */ +public class JabberView + implements PlaceView +{ + public JabberView (CrowdContext ctx) + { + _ctx = ctx; + _jctx = (JmeContext)ctx; + _chat = new ChatView(_jctx, _ctx.getChatDirector()); + _jctx.getRoot().attachChild(_chat); + } + + // documentation inherited from interface PlaceView + public void willEnterPlace (PlaceObject plobj) + { + _chat.willEnterPlace(plobj); + } + + // documentation inherited from interface PlaceView + public void didLeavePlace (PlaceObject plobj) + { + _chat.didLeavePlace(plobj); + } + + protected CrowdContext _ctx; + protected JmeContext _jctx; + protected ChatView _chat; +} diff --git a/tests/src/java/com/threerings/jme/data/JabberConfig.java b/tests/src/java/com/threerings/jme/data/JabberConfig.java new file mode 100644 index 000000000..4f6400139 --- /dev/null +++ b/tests/src/java/com/threerings/jme/data/JabberConfig.java @@ -0,0 +1,26 @@ +// +// $Id$ + +package com.threerings.jme.data; + +import com.threerings.crowd.data.PlaceConfig; +import com.threerings.jme.client.JabberController; + +/** + * Defines the necessary bits for our chat room. + */ +public class JabberConfig extends PlaceConfig +{ + // documentation inherited + public Class getControllerClass () + { + return JabberController.class; + } + + // documentation inherited + public String getManagerClassName () + { + // nothing special needed on the server side + return "com.threerings.crowd.server.PlaceManager"; + } +} diff --git a/tests/src/java/com/threerings/jme/server/JabberServer.java b/tests/src/java/com/threerings/jme/server/JabberServer.java new file mode 100644 index 000000000..8ff438702 --- /dev/null +++ b/tests/src/java/com/threerings/jme/server/JabberServer.java @@ -0,0 +1,49 @@ +// +// $Id$ + +package com.threerings.jme.server; + +import com.threerings.crowd.Log; +import com.threerings.crowd.data.PlaceObject; +import com.threerings.crowd.server.CrowdServer; +import com.threerings.crowd.server.PlaceManager; +import com.threerings.crowd.server.PlaceRegistry; + +import com.threerings.jme.data.JabberConfig; + +/** + * A basic server that creates a single room and sticks everyone in it + * where they can chat with one another. + */ +public class JabberServer extends CrowdServer +{ + // documentation inherited + public void init () + throws Exception + { + super.init(); + + // create a single location + plreg.createPlace( + new JabberConfig(), new PlaceRegistry.CreationObserver() { + public void placeCreated (PlaceObject place, PlaceManager pmgr) { + Log.info("Created chat room " + pmgr.where() + "."); + _place = pmgr; + } + }); + } + + public static void main (String[] args) + { + JabberServer server = new JabberServer(); + try { + server.init(); + server.run(); + } catch (Exception e) { + Log.warning("Unable to initialize server."); + Log.logStackTrace(e); + } + } + + protected PlaceManager _place; +}