org.starhope.appius.game
Class AppiusClaudiusCaecus

java.lang.Object
  extended by java.lang.Thread
      extended by org.starhope.appius.game.AppiusClaudiusCaecus
All Implemented Interfaces:
Comparable<AppiusClaudiusCaecus>, Runnable, Thread.UncaughtExceptionHandler, AcceptsMetronomeTicks, HasName, org.starhope.util.types.CanProcessCommands

public class AppiusClaudiusCaecus
extends Thread
implements AcceptsMetronomeTicks, Thread.UncaughtExceptionHandler, org.starhope.util.types.CanProcessCommands, Comparable<AppiusClaudiusCaecus>

Appius Claudius Caecus is a game server application framework. Extensible using Java classes loaded from its extensive library of configurable options, Appius provides a network server for real-time data exchange.

Originally titled “Braque,” this application was developed for use with the videogame (work in progress) “Sideres.” It was designed to operate with a messaging protocol called “Cubist,” which has since been defined in terms of a simple JSON + \0 wire protocol.

Since that time, I have repurposed the game engine for use with Tootsville™ (http://www.Tootsville.com/) and set up a new series of communications supports designed to be compatible with the ActionScript 3 libraries for Smart Fox Server Pro.

The server should still be able to operate on its own with minimal changes. Most of the Tootsville-specific code has been isolated into the com.tootsville.* package, but replacement code for some methods might be needed to create a stand-alone game. Also, some default configuration values are Tootsville-oriented.

This class in general operates as both the main game thread and socket listener — in the static methods and variables — and the client-connected user thread — in the instance methods and variables. The “metronome” timer thread also runs using the static methods of this class.

Author:
brpocock

Nested Class Summary
 
Nested classes/interfaces inherited from class java.lang.Thread
Thread.State, Thread.UncaughtExceptionHandler
 
Field Summary
private static PreparedStatement blatherStatement
          Prepared statement used to inject log entries into the log
private static long bootTime
          The time at which the server was started
private  boolean busyState
          While we are processing a transaction for the user (during command processing), this flag is brought high to block idle timeouts due to overlong transactions
(package private) static Charon charon
          Global reaper thread to reap zombies
private  float clientProtocolLanguage
          The language variant that the client is speaking.
private  boolean debug
          Boolean flag indicating whether the server is in debugging mode or not.
static int DELAY_MS
          The delay before the first run of the NPC manager task.
private  ConcurrentLinkedQueue<AppiusDatagram> futureDatagrams
          The queue of all datagrams pending for this user.
private static int highWaterUsers
          The most users who have been online since the server started
private  long idleWarned
          At what time was the user warned about being idle for too long?
private  BufferedReader in
          The buffered input stream from the user.
private  boolean isDone
          This indicates whether the thread has voluntarily decided to exit, e.g.
(package private) static SimpleDateFormat isoDate
          The date format used for system messages.
private static Connection journalDB
          The connection to the journal database, used by blather
private static boolean keepListening
          If this ever transitions to “false,” stop listening for new connections.
private  boolean keepRunning
          This variable controls the main loop of the server thread.
private static boolean knowWhyJournalDBOut
          If the journaling database goes offline, this variable will keep from getting a flood of bug reports.
private  long lastInputTime
          The time at which we last received input from the remote user.
private static long lastMetronomeTick
          The time at which the global metronome thread last ticked.
private  String letsPlayWithFlash
          This is a crazy XML looking string that we have to pump out to make the Flash plug-in happy.
private  boolean loggedIn
          If the user has been logged in, this flag will be true
(package private) static Zone loginZone
          Users logging in are directed first the this landing zone, and then choose a Zone server to which they wish to connect (if multiple Zones have been established).
private  int maxInputSize
          The maximum number of characters (or is it bytes? I'm unclear on my own implementation there!) that can be accepted from the client in a single packet
(package private) static Timer metronome
          The timer driving the global metronome.
private static ConcurrentSkipListSet<AcceptsMetronomeTicks> metronomeListeners
          Collection of arbitrary objects who wish to receive Metronome ticks
(package private) static Thread metronomeThread
          The global metronome thread.
private static String motd
          Message of the day
private  User myUser
          The user account that is logged in on this thread
(package private)  PrintWriter out
          The output stream connected to the client
private static int port
          The listening port for the server
private  int preloginCountdown
          The number of prelogin commands that can be accepted before the user is dropped for failing to log in
private  String randomKey
          random key used for SHA1 sum in login
private static long serialVersionUID
          The version of the serialized form of this class.
private static ConcurrentHashMap<String,AppiusClaudiusCaecus> serverThreads
          All live server threads
private  Socket socket
          The socket connected to the client session
private static boolean started
          A global boolean flag to indicate that the server has started up successfully
private  int state
          The state of the conversation that we are having with the client
private static long tGameStatePump
          Time at which the Game State pump last ran
private  long tLastNudge
          Time at which users were last nudged to check their online status
private static long tStats
          Time at which the server statistics were last “reportBug” mailed
private static int TWENTY_ONE_YEARS_MSEC
          The number of milliseconds in 21 years.
private  Zone zone
          The Zone in which this thread is connecting
private static ConcurrentHashMap<String,Zone> zones
          All zones active on this server
 
Fields inherited from class java.lang.Thread
MAX_PRIORITY, MIN_PRIORITY, NORM_PRIORITY
 
Constructor Summary
AppiusClaudiusCaecus(Socket newSocket, Zone newZone)
          Create a new thread connected to a given client, in a certain zone.
AppiusClaudiusCaecus(String string)
          Just for uncaught exception handler faux-thread
 
Method Summary
static void add(AcceptsMetronomeTicks listener)
          Add a thread to the Metronome tick event schedule without that thread being
static void add(Zone zone)
          Add a Zone to the global Zones list
static void addZone(String zoneName, Zone zone)
          Deprecated. 
private  void areYouThere()
          Send a packet to the user to see if they're still there
static void blather(String message)
          Print a debugging message at low urgency, from a random place
static void blather(String user, String room, String address, String message, boolean urgent)
          Write out a log message to the log file and/or database
static void bugDuplex(String subject, String message)
          Write out an error message to the log file and/or mail, as appropriate
 void close()
          Close the socket, terminate the connection
 void commandJSON(String cmd, org.json.JSONObject jso, Class<?> klass)
          Process a command from a JSON source
 int compareTo(AppiusClaudiusCaecus other)
          Compare two AppiusClaudiusCaecus server threads
static void configUpdated()
           
 void disconnectDuplicate()
          Disconnect *this* user, with a notification that they have logged in from someplace else.
private  void dropSocketConnection()
          Drop the I/O socket for this user.
private  void end()
          Indicate that this thread should cease to breathe.
 void enterZone(String zoneName)
          Enter into a Zone (set the local zone indicator)
 void enterZone(Zone whichZone)
          Enter into a Zone (set the local zone indicator)
 boolean equals(AppiusClaudiusCaecus other)
          This is an overriding method.
 boolean equals(Object other)
          This is an overriding method.
 void failLogin()
          This method is called when login fails.
static Error fatalBug(Exception e)
          The exception passed in will be reported, as per reportBug, and then re-thrown as an Error, killing the process responsible
static Error fatalBug(String string)
          Report a bug, and throw a fatal Error.
static Error fatalBug(String string, Throwable t)
          Report a bug, and throw a fatal Error.
private  void genRandomKey()
          Generate a new random key, avoiding characters that won't work with Smart Fox Server clients
private static int getAccurateHeadcount()
          Get the accurate number of users in all zones.
static Collection<AbstractUser> getAllUsers()
          Get a collection of all users in all zones.
static LinkedList<AbstractZone> getAllZones()
           
 String getApple(String pass)
          Get the “apple” (CHAP authentication string SHA1 digest encoded in hex) for the login system
static long getBootTime()
          Get the server start time
(package private) static Charon getCharon()
          Get the Charon reaper thread
static int getHighWaterUsers()
          Get the high-water mark count of users
 String getIpAddress()
          Deprecated. Smart Fox Server Pro misspelling of getIPAddress()
 String getIPAddress()
           
static long getLastMetronomeTick()
           
static String getMOTD()
           
 String getRandomKey()
           
static String getRev()
          Get the revision number of this file
static String getServerHostname()
          Get the hostname on which the server process is running
static int getServerPort()
          The server (TCP) port number
 float getSFSVersion()
           
private  Socket getSocket()
          Get the socket connection to the client
private static String getStackTrace(Throwable throwable)
           This extracts a stack backtrace from a Throwable into a string format for a bug report.
private static String getStackTrace(Throwable throwable, String prefix)
          This extracts a stack backtrace from a Throwable into a string format for a bug report.
static int getThreadCount()
          Returns the number of server threads running.
 User getUser()
           
 Zone getZone()
          Get the zone in which this client is acting
static Zone getZone(String zoneName)
          Find a Zone object for a given zone name
private  String grabInput()
          Get input from the client stream
 int hashCode()
          This is an overriding method.
 boolean isDebug()
          Determine whether the server is in debugging mode
 boolean isLoggedIn()
           
protected  void kickDuplicates(User user, String nick)
          Kick offline any duplicates of the given user as s/he logs in
protected  void kickDuplicates(User user, String nick, String password)
          Deprecated. use kickDuplicates(User, String)
private static void listenForever()
           Listen for incoming connections, and sit here until we get one.
static void logEvent(String verb, String zoneName, String userName, String targetName, HashMap<String,String> details)
          Record an event to the journal
 boolean logIn(Zone z, String bigNick, String password)
          Process a login request from the user
static void main(String[] argv)
          This is the main routine to run Appius as a stand-alone server.
 void migrate(String hostName, int portNumber, String zoneName)
          Order this user to migrate to another Appius Claudius Caecus server.
static void migrateAll(String otherHost, int otherPort)
           Migrate all users to another host.
 String processInput(String theInput)
          Process and dispatch input from the client.
private  String processJSONInput(String theInput)
          Process a JSON string from the client
private  String processJSONLogin(String theInput)
          Process a JSON command sequence from the client
private  String processJSONPreLogin(String theInput)
          Process a prelogin JSON command
private  String processPreLogin(String theInput)
          Process a prelogin input sequence
private  String processXMLInput(String theInput)
           
static void remove(AcceptsMetronomeTicks listener)
          Remove a metronome listener
static void remove(Zone whichZone)
          Remove a zone from the server
static void reportBug(String string)
           Report a bug.
static void reportBug(String reason, Throwable throwable)
          Report a bug to the automatic bug-tracking systems.
static void reportBug(Throwable e)
           
static void reportClientBug(String string)
          Report a bug from the client application.
static void reportClientBug(String string, AppiusClaudiusCaecus thread)
          Report a bug from the client application.
static void restart()
          This should restart the server, but it doesn't.
static void restartMetronome()
          Attempt to restart the global metronome — this probably won't work as currently implemented (?)
 void run()
          Run the server thread connected to a client
 void sendAdminDisconnect(String message, String title, String label, String disconnectCause)
          Send a disconnection message, and drop the user on the next client cycle.
 void sendAdminMessage(String message, boolean remote)
          Send an administrative message to the user.
 void sendAdminMessage(String message, String title, String hatLabel, boolean remote)
          Send an administrative message.
private  void sendDeferredDatagrams()
          Send all deferred (future) datagrams pending in the queue
 void sendError_RAW(String errorSource, String message)
          Send a raw error message back to the client as a JSON response
private  boolean sendFutureDatagrams()
          Send any future datagrams that are pending for this user
 void sendGameActionMessage(AbstractUser sender, org.json.JSONObject data)
          Send a game action event message to the client
protected  void sendLoginPacket(String zoneName, String nick, String password)
          Send the bucketfuls of information that we force-feed the client at login...
protected  void sendLogKO()
          Send a login failure message to the client, using the default (generic) message
protected  void sendLogKO(String messageText)
          Send a “KO” message to the client, informing them that they are not permitted to log in.
 void sendPrivateMessage(AbstractUser from, String message)
          Send a private (“whisper”) message to the user
 void sendPublicMessage(AbstractUser from, String message)
          Send a public message
 void sendRawMessage(String reply, boolean remote)
           
private  void sendRawMessageLater(String reply)
          Send a message to the user in future
 void sendResponse(org.json.JSONObject result)
          Send a response as a future (deferred remote) datagram without a room specified
 void sendResponse(org.json.JSONObject result, int room, boolean remote)
          Send a response to the client in JSON form.
 void sendResponse(org.json.JSONObject result, Integer room)
          Send a response as a future (deferred remote) datagram
 void sendResponse(org.json.JSONObject result, Integer room, AbstractUser u)
           
 void sendResponseRemote(org.json.JSONObject result, Integer room, AbstractUser u)
          Deprecated. use sendResponse(JSONObject, Integer)
 void sendRoomEnteredByUser(AbstractRoom room, AbstractUser user)
          Send notification that an user has joined a room
 void sendRoomList()
          Send the user a room list for their current zone
 void sendRoomList(AbstractZone forZone, boolean remote)
          Send the user a room list for an arbitrary zone
 void sendRoomPartedBy(AbstractRoom room, AbstractUser user)
          Send a notification that an user has departed from a room
 void sendRoomUserCount(AbstractRoom room)
          Send the user count for the given room
 void sendRoomVar(int roomNum, String varName, String varValue)
           
 void sendSuccessReply(String source, org.json.JSONObject resultIn, AbstractUser u, int room)
          Send a JSON success packet back to the client
 void sendUserPart(AbstractUser user, AbstractRoom room)
          Deprecated. use #sendRoomPartedBy(Room, AbstractUser)
 void sendUserVariable(User user, String varName, String varValue)
          Send an update to an user variable
 void setBusyState(boolean b)
           
 void setDebug(boolean newDebug)
          Set the server's debugging mode on (true) or off (false)
 void setLastInputTime(long thatTime)
           
 void setLoggedIn(boolean amILoggedInNow)
           
static void setMOTD(String string)
          TODO: document this method (brpocock, Jan 5, 2010)
 void setSFSVersion(float smartFoxServerCommProtocolVersion)
           
private  void setup()
          Set up this thread to execute
static void startMetronome()
          Start the global metronome to ticking
private static void startTicking(AppiusClaudiusCaecus thread)
          Register an object (usually a server thread) who wishes to begin accepting metronome ticks.
static void stopMetronome()
           
private static void stopTicking(AcceptsMetronomeTicks thread)
          Stop sending metronome ticks to a particular object.
static String stringify(StackTraceElement[] stackTrace)
          Create a pure string version of a stack backtrace
static String stringify(Throwable e)
           
 void tattle(String message)
          tattle a non-urgent message
 void tattle(String tattle, boolean urgent)
          Print a log entry to STDERR with a great deal of identifiable detail
private  boolean tick_checkIdleKick(long tIdle)
          Check whether the user has been idle for too long, and kick them offline if so
private  boolean tick_checkIdleWarnTime(long tIdle)
          Check how long the user has been idle, and send a warning if the time idle has exceeded a limit
static void tick()
          The main metronome single-threaded tick
 void tick(long t, long dT)
          Propagate a metronome tick
 String toString()
          This returns a plethora of debugging-useful information about this particular server thread.
static void traceThis()
          Force a stack backtrace without an exception being thrown.
static void traceThis(String string)
          Force a stack backtrace without an exception being thrown.
 void uncaughtException(Thread t, Throwable e)
          This is an overriding method.
static void updateHighWaterMark()
          Update the high water mark, if necessary
private  String userDebug(String string)
          Create a message string informing the user that an error has occurred, and instructing them to contact Customer Service.
 
Methods inherited from class java.lang.Thread
activeCount, checkAccess, countStackFrames, currentThread, destroy, dumpStack, enumerate, getAllStackTraces, getContextClassLoader, getDefaultUncaughtExceptionHandler, getId, getName, getPriority, getStackTrace, getState, getThreadGroup, getUncaughtExceptionHandler, holdsLock, interrupt, interrupted, isAlive, isDaemon, isInterrupted, join, join, join, resume, setContextClassLoader, setDaemon, setDefaultUncaughtExceptionHandler, setName, setPriority, setUncaughtExceptionHandler, sleep, sleep, start, stop, stop, suspend, yield
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 
Methods inherited from interface org.starhope.appius.util.HasName
getName
 

Field Detail

blatherStatement

private static PreparedStatement blatherStatement
Prepared statement used to inject log entries into the log


bootTime

private static final long bootTime
The time at which the server was started


charon

static final Charon charon
Global reaper thread to reap zombies


DELAY_MS

public static final int DELAY_MS
The delay before the first run of the NPC manager task.

See Also:
Constant Field Values

highWaterUsers

private static int highWaterUsers
The most users who have been online since the server started


isoDate

static final SimpleDateFormat isoDate
The date format used for system messages. A constant.


journalDB

private static Connection journalDB
The connection to the journal database, used by blather


keepListening

private static boolean keepListening
If this ever transitions to “false,” stop listening for new connections. Global kill switch.


knowWhyJournalDBOut

private static boolean knowWhyJournalDBOut
If the journaling database goes offline, this variable will keep from getting a flood of bug reports. On every successful write to the journal database, this flag is reset to false. However, after any failure, it flip-flops to true, allowing only one bug report to be mailed at a time.


lastMetronomeTick

private static long lastMetronomeTick
The time at which the global metronome thread last ticked.


loginZone

static Zone loginZone
Users logging in are directed first the this landing zone, and then choose a Zone server to which they wish to connect (if multiple Zones have been established).


metronome

static Timer metronome
The timer driving the global metronome.


metronomeListeners

private static ConcurrentSkipListSet<AcceptsMetronomeTicks> metronomeListeners
Collection of arbitrary objects who wish to receive Metronome ticks


metronomeThread

static Thread metronomeThread
The global metronome thread.


motd

private static String motd
Message of the day


port

private static int port
The listening port for the server


serialVersionUID

private static final long serialVersionUID
The version of the serialized form of this class.

See Also:
Constant Field Values

serverThreads

private static ConcurrentHashMap<String,AppiusClaudiusCaecus> serverThreads
All live server threads


started

private static boolean started
A global boolean flag to indicate that the server has started up successfully


tGameStatePump

private static long tGameStatePump
Time at which the Game State pump last ran


tStats

private static long tStats
Time at which the server statistics were last “reportBug” mailed


TWENTY_ONE_YEARS_MSEC

private static final int TWENTY_ONE_YEARS_MSEC
The number of milliseconds in 21 years. Used for autovivification of accounts.

See Also:
Constant Field Values

zones

private static ConcurrentHashMap<String,Zone> zones
All zones active on this server


busyState

private boolean busyState
While we are processing a transaction for the user (during command processing), this flag is brought high to block idle timeouts due to overlong transactions


clientProtocolLanguage

private float clientProtocolLanguage
The language variant that the client is speaking.


debug

private boolean debug
Boolean flag indicating whether the server is in debugging mode or not. True if we are in debugging mode. Defaults to true, until the configuration has been read


futureDatagrams

private final ConcurrentLinkedQueue<AppiusDatagram> futureDatagrams
The queue of all datagrams pending for this user. The user should receive these as soon as possible.


idleWarned

private long idleWarned
At what time was the user warned about being idle for too long?


in

private BufferedReader in
The buffered input stream from the user.


isDone

private boolean isDone
This indicates whether the thread has voluntarily decided to exit, e.g. because the user has properly disconnected and so forth.


keepRunning

private boolean keepRunning
This variable controls the main loop of the server thread. When it transitions to “false,” the thread will die.


lastInputTime

private long lastInputTime
The time at which we last received input from the remote user.


letsPlayWithFlash

private final String letsPlayWithFlash
This is a crazy XML looking string that we have to pump out to make the Flash plug-in happy.


loggedIn

private boolean loggedIn
If the user has been logged in, this flag will be true


maxInputSize

private final int maxInputSize
The maximum number of characters (or is it bytes? I'm unclear on my own implementation there!) that can be accepted from the client in a single packet


myUser

private User myUser
The user account that is logged in on this thread


out

PrintWriter out
The output stream connected to the client


preloginCountdown

private int preloginCountdown
The number of prelogin commands that can be accepted before the user is dropped for failing to log in


randomKey

private transient String randomKey
random key used for SHA1 sum in login


socket

private Socket socket
The socket connected to the client session


state

private int state
The state of the conversation that we are having with the client


tLastNudge

private long tLastNudge
Time at which users were last nudged to check their online status


zone

private Zone zone
The Zone in which this thread is connecting

Constructor Detail

AppiusClaudiusCaecus

public AppiusClaudiusCaecus(Socket newSocket,
                            Zone newZone)
Create a new thread connected to a given client, in a certain zone.

Parameters:
newSocket - The socket connected to the client
newZone - The login zone into which the client is connected

AppiusClaudiusCaecus

public AppiusClaudiusCaecus(String string)
Just for uncaught exception handler faux-thread

Parameters:
string - thread name
Method Detail

add

public static void add(AcceptsMetronomeTicks listener)
Add a thread to the Metronome tick event schedule without that thread being

Parameters:
listener - The listener who wishes to receive Metronome ticks.

add

public static void add(Zone zone)
Add a Zone to the global Zones list

Parameters:
zone - the Zone to be added

addZone

@Deprecated
public static void addZone(String zoneName,
                                      Zone zone)
Deprecated. 

Parameters:
zoneName - The zone's name
zone - The Zone object

blather

public static void blather(String message)
Print a debugging message at low urgency, from a random place

Parameters:
message - The message

blather

public static void blather(String user,
                           String room,
                           String address,
                           String message,
                           boolean urgent)
Write out a log message to the log file and/or database

Parameters:
user - The user name and ID responsible
room - The room and zone in which the action occurred
address - The user's remote socket's IP address and port number
message - The event which occurred
urgent - Urgent messages are written even when debugging is disabled

bugDuplex

public static void bugDuplex(String subject,
                             String message)
Write out an error message to the log file and/or mail, as appropriate

Parameters:
subject - The subject
message - The error message

configUpdated

public static void configUpdated()

fatalBug

public static Error fatalBug(Exception e)
The exception passed in will be reported, as per reportBug, and then re-thrown as an Error, killing the process responsible

Parameters:
e - An exception to report
Returns:
the error (in theory), which can't be used, but makes the source code more legible.
Throws:
Error - (based upon the exception) every time. Since the Error is also thrown, it's never actually received by the caller, but it makes the compiler happy to write it into a throw clause, so it realizes that the flow will not continue.

fatalBug

public static Error fatalBug(String string)
Report a bug, and throw a fatal Error.

Parameters:
string - The error to report
Returns:
a copy of the Error. Since the Error is also thrown, it's never actually received by the caller, but it makes the compiler happy to write it into a throw clause, so it realizes that the flow will not continue.

fatalBug

public static Error fatalBug(String string,
                             Throwable t)
Report a bug, and throw a fatal Error.

Parameters:
string - A description of the error to report
t - An exception to escalate to the Error
Returns:
a copy of the Error. Since the Error is also thrown, it's never actually received by the caller, but it makes the compiler happy to write it into a throw clause, so it realizes that the flow will not continue.

getAccurateHeadcount

private static int getAccurateHeadcount()
Get the accurate number of users in all zones.

Returns:
The number of users in all zones.

getAllUsers

public static Collection<AbstractUser> getAllUsers()
Get a collection of all users in all zones.

Returns:
all users in all Zones

getAllZones

public static LinkedList<AbstractZone> getAllZones()
Returns:
All active zones in the multiverse

getBootTime

public static long getBootTime()
Get the server start time

Returns:
The time at which the server started in milliseconds since epoch

getCharon

static Charon getCharon()
Get the Charon reaper thread

Returns:
Charon

getHighWaterUsers

public static int getHighWaterUsers()
Get the high-water mark count of users

Returns:
the highest number of simultaneous users who have been online since server boot

getLastMetronomeTick

public static long getLastMetronomeTick()
Returns:
the time at which the metronome last ticked

getMOTD

public static String getMOTD()
Returns:
the motd

getRev

public static String getRev()
Get the revision number of this file

Returns:
The revision number of this file

getServerHostname

public static String getServerHostname()
Get the hostname on which the server process is running

Returns:
the hostname of the local host

getServerPort

public static int getServerPort()
The server (TCP) port number

Returns:
the port on which the server should listen

getStackTrace

private static String getStackTrace(Throwable throwable)

This extracts a stack backtrace from a Throwable into a string format for a bug report. Each line is tagged with a leading "/#" string, followed by a space, the stack backtrace element, and a newline.

This is the same as calling getStackTrace(Throwable, String) with a prefix of "\n/#"

Parameters:
throwable - The Throwable containing stack backtrace data
Returns:
A string representation of the backtrace, suitable for logging or bug reports.

getStackTrace

private static String getStackTrace(Throwable throwable,
                                    String prefix)
This extracts a stack backtrace from a Throwable into a string format for a bug report. Each line is preceded by the supplied prefix string, followed the stack backtrace element. The string does not end with a newline.

Parameters:
throwable - A Throwable from which to extract a stack trace
prefix - The string with which to separate lines of the trace.
Returns:
The stacktrace as a string

getThreadCount

public static int getThreadCount()
Returns the number of server threads running. There are more threads in the VM, because there are also bookkeeping threads, the main listener, the metronome, etc.

Returns:
The number of server threads running

getZone

public static Zone getZone(String zoneName)
Find a Zone object for a given zone name

Parameters:
zoneName - The name of the zone to be found
Returns:
The Zone object, if the selected Zone exists; else, null

listenForever

private static void listenForever()

Listen for incoming connections, and sit here until we get one.

If keepListening is altered from elsewhere, then the loop will restart; probably because the port number has been changed.


logEvent

public static void logEvent(String verb,
                            String zoneName,
                            String userName,
                            String targetName,
                            HashMap<String,String> details)
Record an event to the journal

Parameters:
verb - The verb describing the event
zoneName - The zone in which it occurred
userName - The user causing the action
targetName - The target of the action, if any
details - Additional details

main

public static void main(String[] argv)
                 throws IOException
This is the main routine to run Appius as a stand-alone server.

Parameters:
argv - Any command-line arguments
Throws:
IOException - if there's an I/O exception

migrateAll

public static void migrateAll(String otherHost,
                              int otherPort)

Migrate all users to another host. They will attempt to connect to identical zones as the ones which are currently active.

This is a low-level function and does nothing to ensure that the given zone actually exists on the other server

Parameters:
otherHost - the other host to which users should migrate
otherPort - the other host's listening port to which users should migrate

remove

public static void remove(AcceptsMetronomeTicks listener)
Remove a metronome listener

Parameters:
listener - The listener to deregister

remove

public static void remove(Zone whichZone)
Remove a zone from the server

Parameters:
whichZone - The zone to be removed

reportBug

public static void reportBug(String string)

Report a bug.

This is used to catch either ‘impossible things’ or things that are so bad that immediate programmer intervention is needed.

Bug reports should eventually be funneled into the bug-tracking system or similar automatically, and forwarded to the systems programmers via eMail and SMS.

Parameters:
string - Bug report

reportBug

public static void reportBug(String reason,
                             Throwable throwable)
Report a bug to the automatic bug-tracking systems. This is an exception which "should never" be thrown, being caught and referred back for programmer intervention.

Parameters:
reason - The reason this is a bug, if known.
throwable - The "impossible" exception.

reportBug

public static void reportBug(Throwable e)
Parameters:
e - An exception to report

reportClientBug

public static void reportClientBug(String string)
Report a bug from the client application.

Parameters:
string - The client application's bug

reportClientBug

public static void reportClientBug(String string,
                                   AppiusClaudiusCaecus thread)
Report a bug from the client application.

Parameters:
string - The client application's bug
thread - The associated server thread, whose information will be prepended, if present. (null is a valid answer, for backward compatibility)

restart

public static void restart()
This should restart the server, but it doesn't.


restartMetronome

public static void restartMetronome()
Attempt to restart the global metronome — this probably won't work as currently implemented (?)


setMOTD

public static void setMOTD(String string)
TODO: document this method (brpocock, Jan 5, 2010)

Parameters:
string - new message of the day

startMetronome

public static void startMetronome()
Start the global metronome to ticking


startTicking

private static void startTicking(AppiusClaudiusCaecus thread)
Register an object (usually a server thread) who wishes to begin accepting metronome ticks.

Parameters:
thread - the thread who wants to accept ticks now

stopMetronome

public static void stopMetronome()

stopTicking

private static void stopTicking(AcceptsMetronomeTicks thread)
Stop sending metronome ticks to a particular object. If that object happens to be a Thread as well, also sends the Thread an Thread.interrupt(). If it's an instance of AppiusClaudiusCaecus, it will first call the thread's end() method.

Parameters:
thread - the object who doesn't want to get any more ticks

stringify

public static String stringify(StackTraceElement[] stackTrace)
Create a pure string version of a stack backtrace

Parameters:
stackTrace - An array of StackTraceElements:s
Returns:
A string version of the entire stack backtrace

stringify

public static String stringify(Throwable e)
Parameters:
e - A Throwable to be stringified into a backtrace
Returns:
The string form

tick

public static void tick()
The main metronome single-threaded tick


traceThis

public static void traceThis()
Force a stack backtrace without an exception being thrown. For debugging purposes, principally.


traceThis

public static void traceThis(String string)
Force a stack backtrace without an exception being thrown. For debugging purposes, principally.

Parameters:
string - A string to be used as a label on the trace

updateHighWaterMark

public static void updateHighWaterMark()
Update the high water mark, if necessary


areYouThere

private void areYouThere()
                  throws UserDeadException
Send a packet to the user to see if they're still there

Throws:
UserDeadException - if the user is disconnected

close

public void close()
Close the socket, terminate the connection


commandJSON

public void commandJSON(String cmd,
                        org.json.JSONObject jso,
                        Class<?> klass)
Process a command from a JSON source

Parameters:
cmd - The command to be processed
jso - The JSON data object to be passed into the relevant command
klass - The dispatcher class responsible for handling this command

compareTo

public int compareTo(AppiusClaudiusCaecus other)
Compare two AppiusClaudiusCaecus server threads

Specified by:
compareTo in interface Comparable<AppiusClaudiusCaecus>
Parameters:
other - another server thread
Returns:
true, if the threads are the same
See Also:
Comparable.compareTo(java.lang.Object)

disconnectDuplicate

public void disconnectDuplicate()
Disconnect *this* user, with a notification that they have logged in from someplace else.


dropSocketConnection

private void dropSocketConnection()
Drop the I/O socket for this user.


end

private void end()
Indicate that this thread should cease to breathe. This sets the keepRunning flag to “false” to terminate the main loop, and throws an arbitrary interrupt to smash whatever might be going on already.


enterZone

public void enterZone(String zoneName)
Enter into a Zone (set the local zone indicator)

Parameters:
zoneName - the name of the Zone

enterZone

public void enterZone(Zone whichZone)
Enter into a Zone (set the local zone indicator)

Parameters:
whichZone - The zone being entered

equals

public boolean equals(AppiusClaudiusCaecus other)
This is an overriding method.

Parameters:
other - the other thread against which to compare
Returns:
true, if these are the same thread
See Also:
Object.equals(java.lang.Object)

equals

public boolean equals(Object other)
This is an overriding method.

Overrides:
equals in class Object
See Also:
Object.equals(java.lang.Object)

failLogin

public void failLogin()
This method is called when login fails. At present, it just closes the connection.


genRandomKey

private void genRandomKey()
Generate a new random key, avoiding characters that won't work with Smart Fox Server clients


getApple

public String getApple(String pass)
Get the “apple” (CHAP authentication string SHA1 digest encoded in hex) for the login system

Parameters:
pass - The plaintext password to be used
Returns:
the “apple” string

getIpAddress

@Deprecated
public String getIpAddress()
Deprecated. Smart Fox Server Pro misspelling of getIPAddress()

Returns:
The string form of the client's IP address (may be IPv4 or IPv6)

getIPAddress

public String getIPAddress()
Returns:
The string form of the client's IP address (may be IPv4 or IPv6)

getRandomKey

public String getRandomKey()
Returns:
the random key sequence generated for this client thread

getSFSVersion

public float getSFSVersion()
Returns:
the sfVersion

getSocket

private Socket getSocket()
Get the socket connection to the client

Returns:
the socket connection to the client

getUser

public User getUser()
Returns:
the user connected to this server thread

getZone

public Zone getZone()
Get the zone in which this client is acting

Returns:
the zone in which this client is acting

grabInput

private String grabInput()
                  throws UserDeadException
Get input from the client stream

Returns:
the input from the client
Throws:
UserDeadException - if the user disconnects

hashCode

public int hashCode()
This is an overriding method.

Overrides:
hashCode in class Object
See Also:
Object.hashCode()

isDebug

public boolean isDebug()
Determine whether the server is in debugging mode

Returns:
true, if the server is in debug mode

isLoggedIn

public boolean isLoggedIn()
Returns:
true, if a user has logged in on this thread

kickDuplicates

protected void kickDuplicates(User user,
                              String nick)
Kick offline any duplicates of the given user as s/he logs in

Parameters:
user - The user logging in (whose duplicates should be disconnected)
nick - The user's nickname (login name)

kickDuplicates

@Deprecated
protected void kickDuplicates(User user,
                                         String nick,
                                         String password)
Deprecated. use kickDuplicates(User, String)

Kick offline any duplicates of the given user as s/he logs in

Parameters:
user - The user logging in (whose duplicates should be disconnected)
nick - The user's nickname (login name)
password - The user's password (ignored)

logIn

public boolean logIn(Zone z,
                     String bigNick,
                     String password)
Process a login request from the user

Parameters:
z - The zone into which the user is trying to log in
bigNick - The user's requested nickname (attempted user name)
password - This is a bit of a misnomer. We actually are checking for the secret key (CHAP cookie) for the current channel, to which has been appended the user's actual password, as presented as a hex-coded SHA1 digest. (In brief: pseudocode of sha1( cookie + password ).toHex )
Returns:
true, if the user can log in; false, if they were refused

migrate

public void migrate(String hostName,
                    int portNumber,
                    String zoneName)
Order this user to migrate to another Appius Claudius Caecus server.

Parameters:
hostName - The alternate server's public host name or IP address string
portNumber - The listening port on the alternate server
zoneName - The zone name to which the user should connect

processInput

public String processInput(String theInput)
Process and dispatch input from the client.

Parameters:
theInput - The input packet from the client
Returns:
An output packet for the client, or a zero-length string if there is no input; or, a null pointer if the client should be disconnected.

processJSONInput

private String processJSONInput(String theInput)
Process a JSON string from the client

Parameters:
theInput - The JSON string containing the command and data
Returns:
A sequence to return to the client

processJSONLogin

private String processJSONLogin(String theInput)
Process a JSON command sequence from the client

Parameters:
theInput - the JSON data in string form
Returns:
output for the client

processJSONPreLogin

private String processJSONPreLogin(String theInput)
Process a prelogin JSON command

Parameters:
theInput - The input string, which must contain a properly-formatted JSON command sequence
Returns:
a result string to be returned to the client

processPreLogin

private String processPreLogin(String theInput)
                        throws UserDeadException
Process a prelogin input sequence

Parameters:
theInput - the prelogin input sequence
Returns:
the output string to return to the client
Throws:
UserDeadException - if the user disconnects

processXMLInput

private String processXMLInput(String theInput)
Parameters:
theInput - The input stream, expected to be in Smart Fox Server Pro XML format
Returns:
A result string to be returned to the user

run

public void run()
Run the server thread connected to a client

Specified by:
run in interface Runnable
Overrides:
run in class Thread
See Also:
Thread.run()

sendAdminDisconnect

public void sendAdminDisconnect(String message,
                                String title,
                                String label,
                                String disconnectCause)
Send a disconnection message, and drop the user on the next client cycle. This is an asynchronous drop, but it clears all other pending output, ensuring that the client will receive this message next in queue and then disconnect.

Parameters:
message - User-visible message explaining the disconnection
title - Title to display in message box
label - Label to display in corner of message box
disconnectCause - Cause code to return to client application giving general cause for disconnection; e.g. "kick" or "ban" usually

sendAdminMessage

public void sendAdminMessage(String message,
                             boolean remote)
                      throws UserDeadException
Send an administrative message to the user.

Parameters:
message - administrative message to send
remote - if true, this is being written to another user
Throws:
UserDeadException - if the user has been disconnected
See Also:
sendAdminMessage(String, String, String, boolean)

sendAdminMessage

public void sendAdminMessage(String message,
                             String title,
                             String hatLabel,
                             boolean remote)
                      throws UserDeadException
Send an administrative message. Attempts to use the JSON protocol for all clients now. If the data can't be successfully encoded into

Parameters:
message - The actual message text
title - The title, which displays in the same font above the message, but does not scroll
hatLabel - A short label which identifies the general source of the message, for dialog box decoration
remote - Whether to send this message remotely (true = deferred delivery) or immediately (false)
Throws:
UserDeadException - if the user isn't there to receive the message

sendDeferredDatagrams

private void sendDeferredDatagrams()
                            throws UserDeadException
Send all deferred (future) datagrams pending in the queue

Throws:
UserDeadException - if the user has disconnected

sendError_RAW

public void sendError_RAW(String errorSource,
                          String message)
Send a raw error message back to the client as a JSON response

Specified by:
sendError_RAW in interface org.starhope.util.types.CanProcessCommands
Parameters:
errorSource - The method that is the source of the error
message - The error message to be returned

sendFutureDatagrams

private boolean sendFutureDatagrams()
Send any future datagrams that are pending for this user

Returns:
true, if the user has been disconnected

sendGameActionMessage

public void sendGameActionMessage(AbstractUser sender,
                                  org.json.JSONObject data)
                           throws org.json.JSONException,
                                  UserDeadException
Send a game action event message to the client

Parameters:
sender - The user sending the game action
data - Arbitrary data associated with the game action
Throws:
org.json.JSONException - if the data can't be represented as JSON
UserDeadException - if the user has been disconnected

sendLoginPacket

protected void sendLoginPacket(String zoneName,
                               String nick,
                               String password)
Send the bucketfuls of information that we force-feed the client at login...

Parameters:
zoneName - The name of the zone into which the user has logged in
nick - The user's nickname
password - The user's password (SHA1 encoded with the local random key)

sendLogKO

protected void sendLogKO()
Send a login failure message to the client, using the default (generic) message


sendLogKO

protected void sendLogKO(String messageText)
Send a “KO” message to the client, informing them that they are not permitted to log in.

Parameters:
messageText - The user-visible message given to the user

sendPrivateMessage

public void sendPrivateMessage(AbstractUser from,
                               String message)
                        throws UserDeadException
Send a private (“whisper”) message to the user

Parameters:
from - The user sending the message
message - The message being whispered
Throws:
UserDeadException - if the user has been disconnected

sendPublicMessage

public void sendPublicMessage(AbstractUser from,
                              String message)
                       throws UserDeadException
Send a public message

Parameters:
from - sender of the message (speaker)
message - The public message
Throws:
UserDeadException - if the user has been disconnected

sendRawMessage

public void sendRawMessage(String reply,
                           boolean remote)
                    throws UserDeadException
Parameters:
reply - The string to be transmitted to another user
remote - True, if being written to another user
Throws:
UserDeadException - if the user has been disconnected

sendRawMessageLater

private void sendRawMessageLater(String reply)
                          throws UserDeadException
Send a message to the user in future

Parameters:
reply - The message to be sent in future
Throws:
UserDeadException - if the user is already gone

sendResponse

public void sendResponse(org.json.JSONObject result)
                  throws UserDeadException
Send a response as a future (deferred remote) datagram without a room specified

Specified by:
sendResponse in interface org.starhope.util.types.CanProcessCommands
Parameters:
result - WRITEME
Throws:
UserDeadException - if the user disconnects
See Also:
sendResponse(JSONObject, int, boolean), CanProcessCommands.sendResponse(org.json.JSONObject)

sendResponse

public void sendResponse(org.json.JSONObject result,
                         int room,
                         boolean remote)
                  throws UserDeadException
Send a response to the client in JSON form. In Smart Fox Server Pro compatibility mode, this goes through as a JSON Extension Response packet. In Cubist JSON form, the JSON object is returned “intact,” with the room number (if supplied as a positive number) added into it as the “r” key.

Parameters:
result - the JSON object to be returned to the client
room - The room number from which the response is being sent.
remote - Whether to send the message as a remote (deferred future datagram) message
Throws:
UserDeadException - if the user has been disconnected

sendResponse

public void sendResponse(org.json.JSONObject result,
                         Integer room)
                  throws UserDeadException
Send a response as a future (deferred remote) datagram

Parameters:
result - the JSON result object to be sent (“extension response”)
room - The room in which the event occurred.
Throws:
UserDeadException - if the user has disconnected
See Also:
sendResponse(JSONObject, int, boolean)

sendResponse

public void sendResponse(org.json.JSONObject result,
                         Integer room,
                         AbstractUser u)
                  throws UserDeadException
Parameters:
result - the JSON result object to be sent (“extension response”)
room - The room in which the event occurred.
u - The user to whom the message is being sent
Throws:
UserDeadException - if the user has disconnected

sendResponseRemote

@Deprecated
public void sendResponseRemote(org.json.JSONObject result,
                                          Integer room,
                                          AbstractUser u)
                        throws UserDeadException
Deprecated. use sendResponse(JSONObject, Integer)

Send a response as a future datagram, presumably to a remote user's thread

Parameters:
result - the JSON object to send to the client
room - the room number in which the event occurred
u - ignored…
Throws:
UserDeadException - if the user has already disconnected

sendRoomEnteredByUser

public void sendRoomEnteredByUser(AbstractRoom room,
                                  AbstractUser user)
                           throws UserDeadException
Send notification that an user has joined a room

Parameters:
room - the room that has been joined by an user
user - the user joining the room
Throws:
UserDeadException - if the user has been disconnected

sendRoomList

public void sendRoomList()
                  throws UserDeadException
Send the user a room list for their current zone

Throws:
UserDeadException - if the user has been disconnected

sendRoomList

public void sendRoomList(AbstractZone forZone,
                         boolean remote)
                  throws UserDeadException
Send the user a room list for an arbitrary zone

Parameters:
forZone - The zone for which the user will receive a room list
remote - If true, writing to a remote user
Throws:
UserDeadException - if the user has been disconnected

sendRoomPartedBy

public void sendRoomPartedBy(AbstractRoom room,
                             AbstractUser user)
                      throws UserDeadException
Send a notification that an user has departed from a room

Parameters:
room - The room from which someone has departed
user - The user who has departed from the room
Throws:
UserDeadException - if the user has been disconnected

sendRoomUserCount

public void sendRoomUserCount(AbstractRoom room)
                       throws UserDeadException
Send the user count for the given room

Parameters:
room - The room whose user count is being updated
Throws:
UserDeadException - if the user has been disconnected

sendRoomVar

public void sendRoomVar(int roomNum,
                        String varName,
                        String varValue)
                 throws UserDeadException
Parameters:
roomNum - The room number for which the variable is being set
varName - The name of the room variable
varValue - The new value of the variable
Throws:
UserDeadException - if the user has been disconnected

sendSuccessReply

public void sendSuccessReply(String source,
                             org.json.JSONObject resultIn,
                             AbstractUser u,
                             int room)
                      throws org.json.JSONException
Send a JSON success packet back to the client

Parameters:
source - the method returning success
resultIn - additional information to be returned to the client
u - the user responsible for the successful reply (ignored)
room - the room in which the user is standing (ignored)
Throws:
org.json.JSONException - if the success reply can't be encoded in JSON form

sendUserPart

@Deprecated
public void sendUserPart(AbstractUser user,
                                    AbstractRoom room)
                  throws UserDeadException
Deprecated. use #sendRoomPartedBy(Room, AbstractUser)

Send notification that a user has departed from the room

Parameters:
user - The user departing from the room
room - The room from which the user has departed
Throws:
UserDeadException - if the user has been disconnected

sendUserVariable

public void sendUserVariable(User user,
                             String varName,
                             String varValue)
                      throws UserDeadException
Send an update to an user variable

Parameters:
user - The user whose variable has been updated
varName - The name of the user variable
varValue - The new value of the user variable
Throws:
UserDeadException - if the user has been disconnected

setBusyState

public void setBusyState(boolean b)
Specified by:
setBusyState in interface org.starhope.util.types.CanProcessCommands
Parameters:
b - true, if the thread is in a busy state and should not be interrupted for idle timeout

setDebug

public void setDebug(boolean newDebug)
Set the server's debugging mode on (true) or off (false)

Parameters:
newDebug - True, if the server should be in debug mode; else, false

setLastInputTime

public void setLastInputTime(long thatTime)
Specified by:
setLastInputTime in interface org.starhope.util.types.CanProcessCommands
Parameters:
thatTime - the time of last input from the client

setLoggedIn

public void setLoggedIn(boolean amILoggedInNow)
Parameters:
amILoggedInNow - True, if the thread represents a logged-in user

setSFSVersion

public void setSFSVersion(float smartFoxServerCommProtocolVersion)
Parameters:
smartFoxServerCommProtocolVersion - The protocol version to be used. This version of the server supports Smart Fox Server Pro version 1.58, or Cubist JSON form using the value Double.POSITIVE_INFINITY

setup

private void setup()
            throws IOException,
                   UserDeadException
Set up this thread to execute

Throws:
IOException - if the I/O streams can't be initialized
UserDeadException - if the user disconnects before setup is complete

tattle

public void tattle(String message)
tattle a non-urgent message

Parameters:
message - message

tattle

public void tattle(String tattle,
                   boolean urgent)
Print a log entry to STDERR with a great deal of identifiable detail

Parameters:
tattle - the log entry to be printed
urgent - Display this message even in non-debug mode (in logs)

tick

public void tick(long t,
                 long dT)
          throws UserDeadException
Propagate a metronome tick

Specified by:
tick in interface AcceptsMetronomeTicks
Parameters:
t - The value of System.currentTimeMillis at the start of this tick
dT - The delta-T since the prior tick
Throws:
UserDeadException - if the user has been disconnected

tick_checkIdleKick

private boolean tick_checkIdleKick(long tIdle)
                            throws UserDeadException
Check whether the user has been idle for too long, and kick them offline if so

Parameters:
tIdle - Time that the user has been idle (milliseconds)
Returns:
true, if the user was kicked off
Throws:
UserDeadException - if the user is disconnected

tick_checkIdleWarnTime

private boolean tick_checkIdleWarnTime(long tIdle)
                                throws UserDeadException
Check how long the user has been idle, and send a warning if the time idle has exceeded a limit

Parameters:
tIdle - The time that this connection or user has been idle
Returns:
true, if the user was warned; false, if not
Throws:
UserDeadException - if the user went away

toString

public String toString()
This returns a plethora of debugging-useful information about this particular server thread.

Overrides:
toString in class Thread
See Also:
Thread.toString()

uncaughtException

public void uncaughtException(Thread t,
                              Throwable e)
This is an overriding method.

Specified by:
uncaughtException in interface Thread.UncaughtExceptionHandler
See Also:
Thread.UncaughtExceptionHandler.uncaughtException(java.lang.Thread, java.lang.Throwable)

userDebug

private String userDebug(String string)
Create a message string informing the user that an error has occurred, and instructing them to contact Customer Service.

Parameters:
string - The debugging code to append to the message
Returns:
A string to return to the user