/*
* Copyright 2007 Kevin Roe, Daniel Mccarney
* This file is part of Jriver.
*
* Jriver is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Jriver 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* A logon class for gathering user information
* -=Z=- Aug07
*/
package org.jriver.lib;
import java.io.*;
import java.net.*;
import java.util.*;
import org.jriver.core.interactive.*;
import org.jriver.core.*;
import org.jriver.lib.daemon.CommandD;
import org.jriver.lib.events.ConnectionEvent;
import org.jriver.lib.events.ConnectionEvent.ConnectionState;
import org.jriver.lib.interfaces.ConnectionListener;
import org.jriver.lib.interfaces.Talkable;
import org.jriver.telnet.TelnetProtocol;
public class Player extends Living implements Interactive, Talkable, Runnable, Serializable
{
public String username = null;
private String password = null;
private ArrayList<String> pendingMessages = new ArrayList<String>();
transient private boolean disconnectMe = false;
transient private BufferedReader input = null;
transient private PrintWriter output = null;
transient private Socket socket = null;
transient private MudDriver driver = null;
transient private boolean linkConnected = false;
transient private ArrayList<Player> listeners = null;
transient private ArrayList<ConnectionListener> connectionListeners = null;
public static String capString(String s)
{
char c = Character.toUpperCase(s.charAt(0));
return c + s.substring(1);
}
public static String processAlias(String str)
{
if(str.equals("l")) return "look";
else if(str.equals("i")) return "inventory";
else return str;
}
public Player()
{
init();
}
public void addConnectionListener(ConnectionListener listener)
{
connectionListeners.add(listener);
}
public void addSnooper(Player snooper)
{
listeners.add(snooper);
}
public void catchTell(String message, MessageLevel level, MudObject source)
{
String finalMessage = "["+ level.toString() +"] "+ message;
if(!linkConnected)
pendingMessages.add(finalMessage);
else
output.println(finalMessage);
//Spew it to our snoopers as well
for(Player p : listeners)
{
p.catchTell(username+ ": \""+ finalMessage +"\"",
MessageLevel.SNOOP, this);
}
}
public void disconnect()
{
disconnectMe = true;
}
public void fireConnectionEvent(ConnectionState newState)
{
ConnectionEvent e = new ConnectionEvent(this, newState);
//Notify the listeners with the event
for(ConnectionListener c : connectionListeners)
c.connectionStateChanged(e);
}
public void fireConnectionEvent(ConnectionState newState, String message)
{
ConnectionEvent e = new ConnectionEvent(this, newState, message);
//Notify the listeners with the event
for(ConnectionListener c : connectionListeners)
c.connectionStateChanged(e);
}
public Socket getSocket() {
return socket;
}
public boolean isLinkDead() {
return !linkConnected;
}
public boolean isPlayer() { return true; }
public void linkDeath()
{
disconnectMe = true;
linkConnected = false;
socket = null;
output = null;
input = null;
//Create a connection event
ConnectionEvent e = new ConnectionEvent(this, ConnectionState.DISCONNECTED,
username + " loses link.");
//Notify the listeners with the event
for(ConnectionListener c : connectionListeners)
c.connectionStateChanged(e);
System.out.println(username + " loses link.\n");
shout(username + " loses link.\n", MessageLevel.INFO);
}
// this is an md5Hash, not the actual password
public String queryPassword() { return password; }
public ArrayList<Player> querySnoopers()
{
return listeners;
}
public void recoverLink(Socket socket)
{
try
{
this.socket = socket;
input = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
output = new PrintWriter(socket.getOutputStream(), true);
//Create a connection event
ConnectionEvent e = new ConnectionEvent(this, ConnectionState.RECONNECTED,
username + " regains link.");
//Notify the listeners with the event
if(connectionListeners != null)
for(ConnectionListener c : connectionListeners)
c.connectionStateChanged(e);
output.println("You regain link.");
shout(username + " regains link.", MessageLevel.INFO);
System.out.println(username + " regains link.");
spewMessages();
linkConnected = true;
} catch(IOException e) {
e.printStackTrace();
}
}
public void removeConnectionListener(ConnectionListener listener)
{
if(!connectionListeners.contains(listener))
return;
connectionListeners.remove(listener);
}
public void removeSnooper(Player snooper)
{
listeners.remove(snooper);
}
public void run()
{
String theInput = "";
try
{
while (linkConnected && !disconnectMe && ((theInput = input.readLine()) != null))
{
theInput = TelnetProtocol.sanitize(theInput);
theInput = processAlias(theInput);
if (theInput.equals("who"))
who();
else
{
if (theInput.equals("exit"))
{
try
{
output.println("Goodbye.\r");
input.close();
output.close();
driver.removePlayer(this);
socket.close();
this.getRootNode().removeFromParent();
HeartBeat.getHeartBeat().removeLiving((Living) this);
//Create a connection event
ConnectionEvent e = new ConnectionEvent(this, ConnectionState.DISCONNECTED,
username + " quits.");
//Notify the listeners with the event
for(ConnectionListener c : connectionListeners)
c.connectionStateChanged(e);
shout("DISCONNECT", MessageLevel.INFO);
System.out.println("Connection [" + socket.getInetAddress()
+ "] terminated.");
System.out.println("User " + username + " logged out.");
return;
} catch (IOException e) {
e.printStackTrace();
}
} else {
CommandD.parseCommand(theInput, this);
}
showPrompt(output);
}
}
if(disconnectMe) //We have been requested to disconnect
return;
if(theInput == null) //EOF read
linkDeath();
} catch (IOException e) {
System.out.println("IO Exception forced linkdeath for "+ username);
linkDeath();
return;
}
}
public void savePlayer(File outputFile)
{
try
{
FileOutputStream fileOut = new FileOutputStream(outputFile);
ObjectOutputStream objOut = new ObjectOutputStream(fileOut);
objOut.writeObject(this);
objOut.close();
} catch(IOException e) {
System.err.println("Unable to serialize player: "+
username == null ? "noname" : username);
e.printStackTrace();
}
}
public void setInteractivity(Socket socket)
{
try
{
input = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
output = new PrintWriter(socket.getOutputStream(), true);
this.socket = socket;
linkConnected = true;
return;
} catch (IOException e) {
e.printStackTrace();
}
}
//Need to add some security here in the future
public void setPassword(String hash) { password = hash; }
public void shout(String message, MessageLevel level)
{
Player user;
Iterator<Player> e = driver.getUsers().iterator();
while (e.hasNext())
{
user = e.next();
if (message.equals("DISCONNECT"))
{
message = username + " has left.\r";
}
else
{
if (message.equals("CONNECT"))
message = username + " arrives.\r";
else if(level == MessageLevel.GENERAL)
message = "[shout] "+username+": "+message+"\r";
}
user.catchTell(message, level, this);
}
return;
}
public void showPrompt(PrintWriter out)
{
out.print("Jriver> ");
out.flush();
}
public void tellObject(String message) {
this.catchTell(message, MessageLevel.GENERAL, this);
}
public void who()
{
output.println(" Jriver v0.1\r");
output.println("=------------------------=\r");
Iterator<Player> userIterator = driver.getUsers().iterator();
int numUsers;
numUsers = driver.getUsers().size();
while(userIterator.hasNext())
{
Player p = userIterator.next();
if((p.queryName() != null))
output.println(" " + p.queryName() + "\r");
}
output.println("=------------------------=\r");
output.println(numUsers + " users online\r");
}
private void init()
{
driver = MudDriver.getMudDriver();
driver.registerPlayer(this);
listeners = new ArrayList<Player>();
connectionListeners = new ArrayList<ConnectionListener>();
}
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException
{
in.defaultReadObject();
init();
}
private void spewMessages()
{
if(!linkConnected) return;
String displayProperty = driver.configuration.getProperty("displayLinkDeathText");
if(displayProperty.equals("true"))
{
output.println("[\"While you were gone\"...]");
Iterator<String> pendingMessageIterator = pendingMessages.iterator();
while(pendingMessageIterator.hasNext())
output.println(pendingMessageIterator.next());
pendingMessages.clear();
output.println("[End of \"While you were gone\"...]");
}
}
}