Big chat revamp to migrate various useful things from Yohoho like /command
handling, chat history support and chat mogrification into the base framework. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@3690 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -21,9 +21,17 @@
|
||||
|
||||
package com.threerings.crowd.chat.client;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.samskivert.util.Collections;
|
||||
import com.samskivert.util.HashIntMap;
|
||||
import com.samskivert.util.ObserverList;
|
||||
import com.samskivert.util.ResultListener;
|
||||
@@ -85,11 +93,52 @@ public class ChatDirector extends BasicDirector
|
||||
public boolean isChatterValid (Name username);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to implement a slash command (e.g. <code>/who</code>).
|
||||
*/
|
||||
public static abstract class CommandHandler
|
||||
{
|
||||
/**
|
||||
* Handles the specified chat command.
|
||||
*
|
||||
* @param speakSvc an optional SpeakService object representing
|
||||
* the object to send the chat message on.
|
||||
* @param command the slash command that was used to invoke this
|
||||
* handler (e.g. <code>/tell</code>).
|
||||
* @param args the arguments provided along with the command (e.g.
|
||||
* <code>Bob hello</code>) or <code>null</code> if no arguments
|
||||
* were supplied.
|
||||
* @param history an in/out parameter that allows the command to
|
||||
* modify the text that will be appended to the chat history. If
|
||||
* this is set to null, nothing will be appended.
|
||||
*
|
||||
* @return an untranslated string that will be reported to the
|
||||
* chat box to convey an error response to the user, or {@link
|
||||
* ChatCodes#SUCCESS}.
|
||||
*/
|
||||
public abstract String handleCommand (
|
||||
SpeakService speakSvc, String command, String args,
|
||||
String[] history);
|
||||
|
||||
/**
|
||||
* Returns true if this user should have access to this chat
|
||||
* command.
|
||||
*/
|
||||
public boolean checkAccess (BodyObject user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a chat director and initializes it with the supplied
|
||||
* context. The chat director will register itself as a location
|
||||
* observer so that it can automatically process place constrained
|
||||
* chat.
|
||||
*
|
||||
* @param msgmgr the message manager via which we do our translations.
|
||||
* @param bundle the message bundle from which we obtain our
|
||||
* chat-related translation strings.
|
||||
*/
|
||||
public ChatDirector (CrowdContext ctx, MessageManager msgmgr, String bundle)
|
||||
{
|
||||
@@ -102,6 +151,14 @@ public class ChatDirector extends BasicDirector
|
||||
|
||||
// register ourselves as a location observer
|
||||
_ctx.getLocationDirector().addLocationObserver(this);
|
||||
|
||||
// register our default chat handlers
|
||||
MessageBundle msg = _msgmgr.getBundle(_bundle);
|
||||
registerCommandHandler(msg, "help", new HelpHandler());
|
||||
registerCommandHandler(msg, "clear", new ClearHandler());
|
||||
registerCommandHandler(msg, "speak", new SpeakHandler());
|
||||
registerCommandHandler(msg, "emote", new EmoteHandler());
|
||||
registerCommandHandler(msg, "think", new ThinkHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,41 +226,54 @@ public class ChatDirector extends BasicDirector
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a chatter to our list of recent chatters.
|
||||
* Registers a chat command handler.
|
||||
*
|
||||
* @param msg the message bundle via which the slash command will be
|
||||
* translated (as <code>c.</code><i>command</i>). If no translation
|
||||
* exists the command will be <code>/</code><i>command</i>.
|
||||
* @param command the name of the command that will be used to invoke
|
||||
* this handler (e.g. <code>tell</code> if the command will be invoked
|
||||
* as <code>/tell</code>).
|
||||
* @param handler the chat command handler itself.
|
||||
*/
|
||||
protected void addChatter (Name name)
|
||||
public void registerCommandHandler (
|
||||
MessageBundle msg, String command, CommandHandler handler)
|
||||
{
|
||||
// check to see if the chatter validator approves..
|
||||
if ((_chatterValidator != null) &&
|
||||
(!_chatterValidator.isChatterValid(name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean wasthere = _chatters.remove(name);
|
||||
_chatters.addFirst(name);
|
||||
|
||||
if (!wasthere) {
|
||||
if (_chatters.size() > MAX_CHATTERS) {
|
||||
_chatters.removeLast();
|
||||
String key = "c." + command;
|
||||
if (msg.exists(key)) {
|
||||
StringTokenizer st = new StringTokenizer(msg.get(key));
|
||||
while (st.hasMoreTokens()) {
|
||||
_handlers.put(st.nextToken(), handler);
|
||||
}
|
||||
|
||||
notifyChatterObservers();
|
||||
} else {
|
||||
// fall back to just using the English command
|
||||
_handlers.put(command, handler);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered {@link ChatterObserver}s that the list of
|
||||
* chatters has changed.
|
||||
* Return the current size of the history.
|
||||
*/
|
||||
protected void notifyChatterObservers ()
|
||||
public int getCommandHistorySize ()
|
||||
{
|
||||
_chatterObservers.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object observer) {
|
||||
((ChatterObserver)observer).chattersUpdated(
|
||||
_chatters.listIterator());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return _history.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the chat history entry at the specified index,
|
||||
* with 0 being the oldest.
|
||||
*/
|
||||
public String getCommandHistory (int index)
|
||||
{
|
||||
return (String)_history.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the chat command history.
|
||||
*/
|
||||
public void clearCommandHistory ()
|
||||
{
|
||||
_history.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,7 +288,7 @@ public class ChatDirector extends BasicDirector
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a system INFO message as if it had come from the server.
|
||||
* The localtype of the message will be PLACE_CHAT_TYPE.
|
||||
@@ -268,23 +338,6 @@ public class ChatDirector extends BasicDirector
|
||||
bundle, message, SystemMessage.ATTENTION, PLACE_CHAT_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified system message as if it had come from the server.
|
||||
*/
|
||||
protected void displaySystem (
|
||||
String bundle, String message, byte attLevel, String localtype)
|
||||
{
|
||||
// nothing should be untranslated, so pass the default bundle if need
|
||||
// be.
|
||||
if (bundle == null) {
|
||||
bundle = _bundle;
|
||||
}
|
||||
SystemMessage msg = new SystemMessage();
|
||||
msg.attentionLevel = attLevel;
|
||||
msg.setClientInfo(xlate(bundle, message), localtype);
|
||||
dispatchMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the provided message to our chat displays.
|
||||
*/
|
||||
@@ -293,29 +346,84 @@ public class ChatDirector extends BasicDirector
|
||||
_displayMessageOp.setMessage(message);
|
||||
_displays.apply(_displayMessageOp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the
|
||||
* place object that we currently occupy.
|
||||
*/
|
||||
public void requestSpeak (String message)
|
||||
{
|
||||
requestSpeak(message, DEFAULT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches a {@link #requestSpeak(SpeakService,String,byte)} on the
|
||||
* place object that we currently occupy.
|
||||
* Parses and delivers the supplied chat message. Slash command
|
||||
* processing and mogrification are performed and the message is added
|
||||
* to the chat history if appropriate.
|
||||
*
|
||||
* @param speakSvc the SpeakService representing the target dobj of
|
||||
* the speak or null if we should speak in the "default" way.
|
||||
* @param text the text to be parsed and sent.
|
||||
* @param record if text is a command, should it be added to the history?
|
||||
*
|
||||
* @return <code>ChatCodes#SUCCESS</code> if the message was parsed
|
||||
* and sent correctly, a translatable error string if there was some
|
||||
* problem.
|
||||
*/
|
||||
public void requestSpeak (String message, byte mode)
|
||||
public String requestChat (
|
||||
SpeakService speakSvc, String text, boolean record)
|
||||
{
|
||||
// make sure we're currently in a place
|
||||
if (_place == null) {
|
||||
return;
|
||||
if (text.startsWith("/")) {
|
||||
// split the text up into a command and arguments
|
||||
String command = text.substring(1).toLowerCase();
|
||||
String[] hist = new String[1];
|
||||
String args = "";
|
||||
int sidx = text.indexOf(" ");
|
||||
if (sidx != -1) {
|
||||
command = text.substring(1, sidx).toLowerCase();
|
||||
args = text.substring(sidx+1).trim();
|
||||
}
|
||||
|
||||
HashMap possibleCommands = getCommandHandlers(command);
|
||||
switch (possibleCommands.size()) {
|
||||
case 0:
|
||||
StringTokenizer tok = new StringTokenizer(text);
|
||||
return MessageBundle.tcompose(
|
||||
"m.unknown_command", tok.nextToken());
|
||||
|
||||
case 1:
|
||||
Iterator itr = possibleCommands.entrySet().iterator();
|
||||
Map.Entry entry = (Map.Entry) itr.next();
|
||||
String cmdName = (String) entry.getKey();
|
||||
CommandHandler cmd = (CommandHandler) entry.getValue();
|
||||
|
||||
String result = cmd.handleCommand(speakSvc, cmdName, args, hist);
|
||||
if (!result.equals(ChatCodes.SUCCESS)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (record) {
|
||||
// get the final history-ready command string
|
||||
hist[0] = "/" + ((hist[0] == null) ? command : hist[0]);
|
||||
|
||||
// remove from history if it was present and
|
||||
// add it to the end
|
||||
addToHistory(hist[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
default:
|
||||
String alternativeCommands = "";
|
||||
itr = Collections.getSortedIterator(possibleCommands.keySet());
|
||||
while (itr.hasNext()) {
|
||||
cmdName = (String)itr.next();
|
||||
alternativeCommands += " /" + cmdName;
|
||||
}
|
||||
return MessageBundle.tcompose(
|
||||
"m.unspecific_command", alternativeCommands);
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch a speak request on the active place object
|
||||
requestSpeak(_place.speakService, message, mode);
|
||||
// if not a command then just speak
|
||||
String message = text.trim();
|
||||
if (StringUtil.blank(message)) {
|
||||
// report silent failure for now
|
||||
return ChatCodes.SUCCESS;
|
||||
}
|
||||
|
||||
return deliverChat(speakSvc, message, ChatCodes.DEFAULT_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -326,7 +434,7 @@ public class ChatDirector extends BasicDirector
|
||||
* possibly vetoed) before being dispatched.
|
||||
*
|
||||
* @param speakService the speak service to use when generating the
|
||||
* speak request.
|
||||
* speak request or null if we should speak in the current "place".
|
||||
* @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
|
||||
@@ -335,6 +443,13 @@ public class ChatDirector extends BasicDirector
|
||||
public void requestSpeak (
|
||||
SpeakService speakService, String message, byte mode)
|
||||
{
|
||||
if (speakService == null) {
|
||||
if (_place == null) {
|
||||
return;
|
||||
}
|
||||
speakService = _place.speakService;
|
||||
}
|
||||
|
||||
// make sure they can say what they want to say
|
||||
message = filter(message, null, true);
|
||||
if (message == null) {
|
||||
@@ -483,11 +598,22 @@ public class ChatDirector extends BasicDirector
|
||||
_auxes.remove(source.getOid());
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
protected void fetchServices (Client client)
|
||||
/**
|
||||
* Run a message through all the currently registered filters.
|
||||
*/
|
||||
public String filter (String msg, Name otherUser, boolean outgoing)
|
||||
{
|
||||
// get a handle on our chat service
|
||||
_cservice = (ChatService)client.requireService(ChatService.class);
|
||||
_filterMessageOp.setMessage(msg, otherUser, outgoing);
|
||||
_filters.apply(_filterMessageOp);
|
||||
return _filterMessageOp.getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the supplied message through the various chat mogrifications.
|
||||
*/
|
||||
public String mogrifyChat (String text)
|
||||
{
|
||||
return mogrifyChat(text, false, true);
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
@@ -565,42 +691,6 @@ public class ChatDirector extends BasicDirector
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the specified message using the specified bundle.
|
||||
*/
|
||||
protected String xlate (String bundle, String message)
|
||||
{
|
||||
if (bundle != null && _msgmgr != null) {
|
||||
MessageBundle msgb = _msgmgr.getBundle(bundle);
|
||||
if (msgb == null) {
|
||||
Log.warning(
|
||||
"No message bundle available to translate message " +
|
||||
"[bundle=" + bundle + ", message=" + message + "].");
|
||||
} else {
|
||||
message = msgb.xlate(message);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns the message type associated with the specified
|
||||
* oid.
|
||||
*/
|
||||
protected String getLocalType (int oid)
|
||||
{
|
||||
String type = (String)_auxes.get(oid);
|
||||
return (type == null) ? PLACE_CHAT_TYPE : type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to assign unique ids to all speak requests.
|
||||
*/
|
||||
protected synchronized int nextRequestId ()
|
||||
{
|
||||
return _requestId++;
|
||||
}
|
||||
|
||||
// documentation inherited
|
||||
public void clientDidLogon (Client client)
|
||||
{
|
||||
@@ -645,17 +735,295 @@ public class ChatDirector extends BasicDirector
|
||||
locationDidChange(null);
|
||||
|
||||
// clear our service
|
||||
_cservice = null;
|
||||
_cservice = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run a message through all the currently registered filters.
|
||||
* Called to determine whether we are permitted to post the supplied
|
||||
* chat message. Derived classes may wish to throttle chat or restrict
|
||||
* certain types in certain circumstances for whatever reason.
|
||||
*
|
||||
* @return null if the chat is permitted, a translatable string
|
||||
* indicating the reason for rejection if not.
|
||||
*/
|
||||
public String filter (String msg, Name otherUser, boolean outgoing)
|
||||
protected String checkCanChat (
|
||||
SpeakService speakSvc, String message, byte mode)
|
||||
{
|
||||
_filterMessageOp.setMessage(msg, otherUser, outgoing);
|
||||
_filters.apply(_filterMessageOp);
|
||||
return _filterMessageOp.getMessage();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delivers a plain chat message (not a slash command) on the
|
||||
* specified speak service in the specified mode. The message will be
|
||||
* mogrified and filtered prior to delivery.
|
||||
*
|
||||
* @return {@link ChatCodes#SUCCESS} if the message was delivered or a
|
||||
* string indicating why it failed.
|
||||
*/
|
||||
protected String deliverChat (
|
||||
SpeakService speakSvc, String message, byte mode)
|
||||
{
|
||||
// run the message through our mogrification process
|
||||
message = mogrifyChat(message, true, mode != ChatCodes.EMOTE_MODE);
|
||||
|
||||
// mogrification may result in something being turned into a slash
|
||||
// command, in which case we have to run everything through again
|
||||
// from the start
|
||||
if (message.startsWith("/")) {
|
||||
return requestChat(speakSvc, message, false);
|
||||
}
|
||||
|
||||
// make sure this client is not restricted from performing this
|
||||
// chat message for some reason or other
|
||||
String errmsg = checkCanChat(speakSvc, message, mode);
|
||||
if (errmsg != null) {
|
||||
return errmsg;
|
||||
}
|
||||
|
||||
// speak on the specified service
|
||||
requestSpeak(speakSvc, message, mode);
|
||||
|
||||
return ChatCodes.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified command to the history.
|
||||
*/
|
||||
protected void addToHistory (String cmd)
|
||||
{
|
||||
// remove any previous instance of this command
|
||||
_history.remove(cmd);
|
||||
|
||||
// append it to the end
|
||||
_history.add(cmd);
|
||||
|
||||
// prune the history once it extends beyond max size
|
||||
if (_history.size() > MAX_COMMAND_HISTORY) {
|
||||
_history.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mogrify common literary crutches into more appealing chat or
|
||||
* commands.
|
||||
*
|
||||
* @param transformsAllowed if true, the chat may transformed into a
|
||||
* different mode. (lol -> /emote laughs)
|
||||
* @param capFirst if true, the first letter of the text is
|
||||
* capitalized. This is not desired if the chat is already an emote.
|
||||
*/
|
||||
protected String mogrifyChat (
|
||||
String text, boolean transformsAllowed, boolean capFirst)
|
||||
{
|
||||
int tlen = text.length();
|
||||
if (tlen == 0) {
|
||||
return text;
|
||||
|
||||
// check to make sure there aren't too many caps
|
||||
} else if (tlen > 7) {
|
||||
// count caps
|
||||
int caps = 0;
|
||||
for (int ii=0; ii < tlen; ii++) {
|
||||
if (Character.isUpperCase(text.charAt(ii))) {
|
||||
caps++;
|
||||
if (caps > (tlen / 2)) {
|
||||
// lowercase the whole string if there are
|
||||
text = text.toLowerCase();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer buf = new StringBuffer(text);
|
||||
buf = mogrifyChat(buf, transformsAllowed, capFirst);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/** Helper function for {@link #mogrifyChat}. */
|
||||
protected StringBuffer mogrifyChat (
|
||||
StringBuffer buf, boolean transformsAllowed, boolean capFirst)
|
||||
{
|
||||
// do the generic mogrifications and translations
|
||||
buf = translatedReplacements("x.mogrifies", buf);
|
||||
|
||||
// perform themed expansions and transformations
|
||||
if (transformsAllowed) {
|
||||
buf = translatedReplacements("x.transforms", buf);
|
||||
}
|
||||
|
||||
/*
|
||||
// capitalize the first letter
|
||||
if (capFirst) {
|
||||
buf.setCharAt(0, Character.toUpperCase(buf.charAt(0)));
|
||||
}
|
||||
// and capitalize any letters after a sentence-ending punctuation
|
||||
Pattern p = Pattern.compile("([^\\.][\\.\\?\\!](\\s)+\\p{Ll})");
|
||||
Matcher m = p.matcher(buf);
|
||||
if (m.find()) {
|
||||
buf = new StringBuffer();
|
||||
m.appendReplacement(buf, m.group().toUpperCase());
|
||||
while (m.find()) {
|
||||
m.appendReplacement(buf, m.group().toUpperCase());
|
||||
}
|
||||
m.appendTail(buf);
|
||||
}
|
||||
*/
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do all the replacements (mogrifications) specified in the
|
||||
* translation string specified by the key.
|
||||
*/
|
||||
protected StringBuffer translatedReplacements (String key, StringBuffer buf)
|
||||
{
|
||||
MessageBundle bundle = _msgmgr.getBundle(_bundle);
|
||||
if (!bundle.exists(key)) {
|
||||
return buf;
|
||||
}
|
||||
StringTokenizer st = new StringTokenizer(bundle.get(key), "#");
|
||||
// apply the replacements to each mogrification that matches
|
||||
while (st.hasMoreTokens()) {
|
||||
String pattern = st.nextToken();
|
||||
String replace = st.nextToken();
|
||||
Matcher m = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE).
|
||||
matcher(buf);
|
||||
if (m.find()) {
|
||||
buf = new StringBuffer();
|
||||
m.appendReplacement(buf, replace);
|
||||
// they may match more than once
|
||||
while (m.find()) {
|
||||
m.appendReplacement(buf, replace);
|
||||
}
|
||||
m.appendTail(buf);
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a hashmap containing all command handlers that match the
|
||||
* specified command (i.e. the specified command is a prefix of their
|
||||
* registered command string).
|
||||
*/
|
||||
protected HashMap getCommandHandlers (String command)
|
||||
{
|
||||
HashMap matches = new HashMap();
|
||||
BodyObject user = (BodyObject)_ctx.getClient().getClientObject();
|
||||
Iterator itr = _handlers.entrySet().iterator();
|
||||
while (itr.hasNext()) {
|
||||
Map.Entry entry = (Map.Entry) itr.next();
|
||||
String cmd = (String) entry.getKey();
|
||||
if (!cmd.startsWith(command)) {
|
||||
continue;
|
||||
}
|
||||
CommandHandler handler = (CommandHandler)entry.getValue();
|
||||
if (!handler.checkAccess(user)) {
|
||||
continue;
|
||||
}
|
||||
matches.put(cmd, handler);
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a chatter to our list of recent chatters.
|
||||
*/
|
||||
protected void addChatter (Name name)
|
||||
{
|
||||
// check to see if the chatter validator approves..
|
||||
if ((_chatterValidator != null) &&
|
||||
(!_chatterValidator.isChatterValid(name))) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean wasthere = _chatters.remove(name);
|
||||
_chatters.addFirst(name);
|
||||
|
||||
if (!wasthere) {
|
||||
if (_chatters.size() > MAX_CHATTERS) {
|
||||
_chatters.removeLast();
|
||||
}
|
||||
|
||||
notifyChatterObservers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered {@link ChatterObserver}s that the list of
|
||||
* chatters has changed.
|
||||
*/
|
||||
protected void notifyChatterObservers ()
|
||||
{
|
||||
_chatterObservers.apply(new ObserverList.ObserverOp() {
|
||||
public boolean apply (Object observer) {
|
||||
((ChatterObserver)observer).chattersUpdated(
|
||||
_chatters.listIterator());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates the specified message using the specified bundle.
|
||||
*/
|
||||
protected String xlate (String bundle, String message)
|
||||
{
|
||||
if (bundle != null && _msgmgr != null) {
|
||||
MessageBundle msgb = _msgmgr.getBundle(bundle);
|
||||
if (msgb == null) {
|
||||
Log.warning(
|
||||
"No message bundle available to translate message " +
|
||||
"[bundle=" + bundle + ", message=" + message + "].");
|
||||
} else {
|
||||
message = msgb.xlate(message);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified system message as if it had come from the server.
|
||||
*/
|
||||
protected void displaySystem (
|
||||
String bundle, String message, byte attLevel, String localtype)
|
||||
{
|
||||
// nothing should be untranslated, so pass the default bundle if need
|
||||
// be.
|
||||
if (bundle == null) {
|
||||
bundle = _bundle;
|
||||
}
|
||||
SystemMessage msg = new SystemMessage();
|
||||
msg.attentionLevel = attLevel;
|
||||
msg.setClientInfo(xlate(bundle, message), localtype);
|
||||
dispatchMessage(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up and returns the message type associated with the specified
|
||||
* oid.
|
||||
*/
|
||||
protected String getLocalType (int oid)
|
||||
{
|
||||
String type = (String)_auxes.get(oid);
|
||||
return (type == null) ? PLACE_CHAT_TYPE : type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to assign unique ids to all speak requests.
|
||||
*/
|
||||
protected synchronized int nextRequestId ()
|
||||
{
|
||||
return _requestId++;
|
||||
}
|
||||
|
||||
// documentation inherited from interface
|
||||
protected void fetchServices (Client client)
|
||||
{
|
||||
// get a handle on our chat service
|
||||
_cservice = (ChatService)client.requireService(ChatService.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -708,6 +1076,114 @@ public class ChatDirector extends BasicDirector
|
||||
protected ChatMessage _message;
|
||||
}
|
||||
|
||||
/** Implements <code>/help</code>. */
|
||||
protected class HelpHandler extends CommandHandler
|
||||
{
|
||||
public String handleCommand (
|
||||
SpeakService speakSvc, String command, String args, String[] history)
|
||||
{
|
||||
String hcmd = "";
|
||||
|
||||
// grab the command they want help on
|
||||
if (!StringUtil.blank(args)) {
|
||||
hcmd = args;
|
||||
int sidx = args.indexOf(" ");
|
||||
if (sidx != -1) {
|
||||
hcmd = args.substring(0, sidx);
|
||||
}
|
||||
}
|
||||
|
||||
// let the user give commands with or with the /
|
||||
if (hcmd.startsWith("/")) {
|
||||
hcmd = hcmd.substring(1);
|
||||
}
|
||||
|
||||
// handle "/help help" and "/help someboguscommand"
|
||||
HashMap possibleCommands = getCommandHandlers(hcmd);
|
||||
if (hcmd.equals("help") || possibleCommands.isEmpty()) {
|
||||
possibleCommands = getCommandHandlers("");
|
||||
possibleCommands.remove("help"); // remove help from the list
|
||||
}
|
||||
|
||||
// if there is only one possible command display its usage
|
||||
switch (possibleCommands.size()) {
|
||||
case 1:
|
||||
Iterator itr = possibleCommands.keySet().iterator();
|
||||
// this is a little funny, but we display the feeback
|
||||
// message by hand and return SUCCESS so that the chat
|
||||
// entry field doesn't think that we've failed and
|
||||
// preserve our command text
|
||||
displayFeedback(null, "m.usage_" + (String)itr.next());
|
||||
return ChatCodes.SUCCESS;
|
||||
|
||||
default:
|
||||
Object[] commands = possibleCommands.keySet().toArray();
|
||||
Arrays.sort(commands);
|
||||
String commandList = "";
|
||||
for (int ii = 0; ii < commands.length; ii++) {
|
||||
commandList += " /" + commands[ii];
|
||||
}
|
||||
return MessageBundle.tcompose("m.usage_help", commandList);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements <code>/clear</code>. */
|
||||
protected class ClearHandler extends CommandHandler
|
||||
{
|
||||
public String handleCommand (
|
||||
SpeakService speakSvc, String command, String args, String[] history)
|
||||
{
|
||||
clearDisplays();
|
||||
return ChatCodes.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements <code>/speak</code>. */
|
||||
protected class SpeakHandler extends CommandHandler
|
||||
{
|
||||
public String handleCommand (
|
||||
SpeakService speakSvc, String command, String args, String[] history)
|
||||
{
|
||||
if (StringUtil.blank(args)) {
|
||||
return "m.usage_speak";
|
||||
}
|
||||
// note the command to be stored in the history
|
||||
history[0] = command + " ";
|
||||
return requestChat(null, args, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements <code>/emote</code>. */
|
||||
protected class EmoteHandler extends CommandHandler
|
||||
{
|
||||
public String handleCommand (
|
||||
SpeakService speakSvc, String command, String args, String[] history)
|
||||
{
|
||||
if (StringUtil.blank(args)) {
|
||||
return "m.usage_emote";
|
||||
}
|
||||
// note the command to be stored in the history
|
||||
history[0] = command + " ";
|
||||
return deliverChat(speakSvc, args, ChatCodes.EMOTE_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
/** Implements <code>/think</code>. */
|
||||
protected class ThinkHandler extends CommandHandler
|
||||
{
|
||||
public String handleCommand (
|
||||
SpeakService speakSvc, String command, String args, String[] history)
|
||||
{
|
||||
if (StringUtil.blank(args)) {
|
||||
return "m.usage_think";
|
||||
}
|
||||
// note the command to be stored in the history
|
||||
history[0] = command + " ";
|
||||
return deliverChat(speakSvc, args, ChatCodes.THINK_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
/** Our active chat context. */
|
||||
protected CrowdContext _ctx;
|
||||
|
||||
@@ -748,6 +1224,12 @@ public class ChatDirector extends BasicDirector
|
||||
protected ObserverList _chatterObservers =
|
||||
new ObserverList(ObserverList.FAST_UNSAFE_NOTIFY);
|
||||
|
||||
/** Registered chat command handlers. */
|
||||
protected static HashMap _handlers = new HashMap();
|
||||
|
||||
/** A history of chat commands. */
|
||||
protected static ArrayList _history = new ArrayList();
|
||||
|
||||
/** Used by {@link #nextRequestId}. */
|
||||
protected int _requestId;
|
||||
|
||||
@@ -759,4 +1241,7 @@ public class ChatDirector extends BasicDirector
|
||||
|
||||
/** The maximum number of chatter usernames to track. */
|
||||
protected static final int MAX_CHATTERS = 6;
|
||||
|
||||
/** The maximum number of commands to keep in the chat history. */
|
||||
protected static final int MAX_COMMAND_HISTORY = 10;
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ public class ChatView extends BContainer
|
||||
|
||||
} else {
|
||||
// request to send this text as a chat message
|
||||
_chatdtr.requestSpeak(text);
|
||||
_chatdtr.requestSpeak(null, text, ChatCodes.DEFAULT_MODE);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -231,7 +231,7 @@ public class ChatPanel
|
||||
|
||||
} else {
|
||||
// request to send this text as a chat message
|
||||
_chatdtr.requestSpeak(text);
|
||||
_chatdtr.requestSpeak(null, text, ChatCodes.DEFAULT_MODE);
|
||||
}
|
||||
|
||||
// clear out the input because we sent a request
|
||||
|
||||
Reference in New Issue
Block a user