package com.aelfengard.i3;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import com.aelfengard.i3.packet.ChanListReplyPacket;
import com.aelfengard.i3.packet.ChannelEPacket;
import com.aelfengard.i3.packet.ChannelListenPacket;
import com.aelfengard.i3.packet.ChannelMPacket;
import com.aelfengard.i3.packet.ChannelTPacket;
import com.aelfengard.i3.packet.EmoteToPacket;
import com.aelfengard.i3.packet.ErrorPacket;
import com.aelfengard.i3.packet.I3Packet;
import com.aelfengard.i3.packet.MudListPacket;
import com.aelfengard.i3.packet.StartupReq3Packet;
import com.aelfengard.i3.packet.TellPacket;
import com.aelfengard.i3.packet.WhoReplyPacket;
import com.aelfengard.i3.packet.WhoReqPacket;
public class I3Client {
private static final String DEFAULT_ROUTER_NAME = "*gjs";
private static final int DEFAULT_PASSWORD = -1;
private final Map<String,MudInfo> mudMap = new TreeMap<String,MudInfo>();
private final Map<String,ChanInfo> channelMap = new TreeMap<String,ChanInfo>();
private final Map<String,Set<I3ChannelListener>> channelListeners = new HashMap<String,Set<I3ChannelListener>>();
private final Set<I3EventListener> listeners = new HashSet<I3EventListener>();
private long lastReconnect = 0;
private String host = I3Connection.DEFAULT_HOST;
private int port = I3Connection.DEFAULT_PORT;
private String routerName = DEFAULT_ROUTER_NAME;
private int password = DEFAULT_PASSWORD;
private String mudName;
private String adminEmail;
private int playerPort = -1;
private String mudLib = "";
private String baseMudLib = "";
private String driver = "";
private String mudType = "";
private String openStatus = "";
private I3Connection conn;
public void autoconnect() {
synchronized(I3Client.this) {
if (conn != null) {
return; }
}
long now = System.currentTimeMillis();
long nextReconnect = lastReconnect + 300000; if (nextReconnect < now) {
nextReconnect = now;
}
lastReconnect = nextReconnect;
final long delay = nextReconnect - now;
new Thread() {
public void run() {
try {
if (delay > 0) {
Thread.sleep(delay);
}
}
catch (InterruptedException ex) {
ex.printStackTrace();
return;
}
try {
connect();
}
catch (IOException ex) {
autoconnect();
}
}
}.start();
}
public void connect() throws IOException {
synchronized(this) {
if (conn != null) {
return; }
}
if (mudName == null) {
throw new IllegalStateException("mudName is null");
}
if (adminEmail == null) {
throw new IllegalStateException("adminEmail is null");
}
if (System.getProperty("i3.debug") != null) {
System.err.println("I3 Connecting...");
}
I3Connection lconn = new I3Connection(host, port);
if (System.getProperty("i3.debug") != null) {
System.err.println("I3 Connected.");
}
synchronized(this) {
conn = lconn;
conn.addPacketListener(new MyPacketListener());
StartupReq3Packet packet = new StartupReq3Packet();
packet.setTargetMudName(new LPCMixed(routerName));
packet.setPassword(new LPCMixed(password));
packet.setOriginatorMudName(new LPCMixed(mudName));
MudInfo info = packet.getMudInfo();
info.setAdminEmail(new LPCMixed(adminEmail));
info.setPlayerPort(new LPCMixed(playerPort));
info.setMudLib(new LPCMixed(mudLib));
info.setBaseMudLib(new LPCMixed(baseMudLib));
info.setDriver(new LPCMixed(driver));
info.setMudType(new LPCMixed(mudType));
info.setOpenStatus(new LPCMixed(openStatus));
Map<LPCMixed,LPCMixed> services = new LinkedHashMap<LPCMixed,LPCMixed>();
services.put(new LPCMixed(MudInfo.SERVICE_TYPE_TELL), new LPCMixed(1));
services.put(new LPCMixed(MudInfo.SERVICE_TYPE_EMOTE_TO), new LPCMixed(1));
services.put(new LPCMixed(MudInfo.SERVICE_TYPE_CHANNEL), new LPCMixed(1));
services.put(new LPCMixed(MudInfo.SERVICE_TYPE_WHO), new LPCMixed(1));
info.setServices(new LPCMixed(services));
conn.send(packet);
}
for (String channel : channelListeners.keySet()) {
modifyChannelDirect(channel, true);
}
}
public String getAdminEmail() {
return adminEmail;
}
public void setAdminEmail(String adminEmail) {
this.adminEmail = adminEmail;
}
public String getBaseMudLib() {
return baseMudLib;
}
public void setBaseMudLib(String baseMudLib) {
this.baseMudLib = baseMudLib;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public String getMudLib() {
return mudLib;
}
public void setMudLib(String mudLib) {
this.mudLib = mudLib;
}
public String getMudType() {
return mudType;
}
public void setMudType(String mudType) {
this.mudType = mudType;
}
public String getOpenStatus() {
return openStatus;
}
public void setOpenStatus(String openStatus) {
this.openStatus = openStatus;
}
public int getPlayerPort() {
return playerPort;
}
public void setPlayerPort(int playerPort) {
this.playerPort = playerPort;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getMudName() {
return mudName;
}
public void setMudName(String mudName) {
this.mudName = mudName;
}
public int getPassword() {
return password;
}
public void setPassword(int password) {
this.password = password;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public String getRouterName() {
return routerName;
}
public void setRouterName(String routerName) {
this.routerName = routerName;
}
public synchronized void addChannelListener(String channel, I3ChannelListener who) {
Set<I3ChannelListener> set = channelListeners.get(channel);
if (set == null) {
set = new HashSet<I3ChannelListener>();
channelListeners.put(channel, set);
modifyChannelDirect(channel, true);
}
set.add(who);
}
public synchronized void removeChannelListener(String channel, I3ChannelListener who) {
Set<I3ChannelListener> set = channelListeners.get(channel);
if (set != null) {
set.remove(who);
if (set.isEmpty()) {
channelListeners.remove(channel);
modifyChannelDirect(channel, false); }
}
}
public synchronized void addEventListener(I3EventListener l) {
listeners.add(l);
}
public synchronized void removeEventListener(I3EventListener l) {
listeners.remove(l);
}
public void modifyChannelDirect(String channel, boolean isOn) {
ChannelListenPacket packet = new ChannelListenPacket();
packet.setOriginatorMudName(new LPCMixed(mudName));
packet.setTargetMudName(new LPCMixed(routerName));
packet.setChannelName(new LPCMixed(channel));
packet.setIsOn(new LPCMixed(isOn ? 1 : 0));
synchronized(this) {
if (conn != null) {
conn.send(packet);
if (System.getProperty("i3.debug") != null) {
System.err.println("I3Client: Channel " + channel + " is now " + (isOn ? "ON" : "OFF"));
}
}
}
}
private synchronized void handleMudList(MudListPacket packet) {
Map<LPCMixed,LPCMixed> mudList = packet.getInfoMapping().asMap();
if (mudList == null) {
return;
}
for (Map.Entry<LPCMixed,LPCMixed> entry : mudList.entrySet()) {
String name = entry.getKey().asString();
List<LPCMixed> list = entry.getValue().asList();
if (list == null) {
mudMap.remove(name);
}
else {
mudMap.put(name, new MudInfo(list));
}
}
}
private synchronized void handleChanList(ChanListReplyPacket packet) {
Map<LPCMixed,LPCMixed> channelList = packet.getChannelList().asMap(true);
for (Map.Entry<LPCMixed,LPCMixed> entry : channelList.entrySet()) {
String name = entry.getKey().asString();
List<LPCMixed> list = entry.getValue().asList();
if (list == null) {
channelMap.remove(name);
}
else {
channelMap.put(name, new ChanInfo(list));
}
}
}
private void handleChannelM(ChannelMPacket packet) {
String channel = packet.getChannelName().asString();
Set<I3ChannelListener> set = getChannelListeners(channel);
if (set != null) {
for (I3ChannelListener l : set) {
l.i3Message(packet);
}
}
}
private synchronized Set<I3ChannelListener> getChannelListeners(String channel) {
Set<I3ChannelListener> set = channelListeners.get(channel);
if (set == null) {
System.err.println("WARNING: Found unused channel: " + channel);
ChannelListenPacket listenPacket = new ChannelListenPacket();
listenPacket.setOriginatorMudName(new LPCMixed(mudName));
listenPacket.setChannelName(new LPCMixed(channel));
listenPacket.setTargetMudName(new LPCMixed(routerName));
listenPacket.setIsOn(new LPCMixed(false));
if (conn != null) {
conn.send(listenPacket);
}
return null;
}
else {
return new HashSet<I3ChannelListener>(set);
}
}
private void handleTell(final TellPacket packet) {
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.tell(packet, new ErrorCallback() {
public void returnError(String reason) {
tellFailed(packet, reason);
}
});
}
}
private void handleEmoteTo(final EmoteToPacket packet) {
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.emoteTo(packet, new ErrorCallback() {
public void returnError(String reason) {
emoteToFailed(packet, reason);
}
});
}
}
private void handleChannelE(ChannelEPacket packet) {
String channel = packet.getChannelName().asString();
Set<I3ChannelListener> set = getChannelListeners(channel);
if (set != null) {
for (I3ChannelListener l : set) {
l.i3Message(packet);
}
}
}
private void handleChannelT(ChannelTPacket packet) {
String channel = packet.getChannelName().asString();
Set<I3ChannelListener> set = getChannelListeners(channel);
if (set != null) {
for (I3ChannelListener l : set) {
l.i3Message(packet);
}
}
}
private void handleWhoReply(WhoReplyPacket packet) {
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
List<LPCMixed> whoData = packet.getWhoData().asList();
if (whoData != null) {
for (I3EventListener l : set) {
l.whoReply(packet.getTargetUsername(), packet.getOriginatorMudName(), whoData);
}
}
}
private void handleWhoReq(final WhoReqPacket packet) {
new Thread() {
public void run() {
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
List<LPCMixed> list = l.whoRequest();
if (list == null) {
continue;
}
WhoReplyPacket reply = new WhoReplyPacket();
reply.setOriginatorMudName(new LPCMixed(mudName));
reply.setTargetMudName(packet.getOriginatorMudName());
reply.setTargetUsername(packet.getOriginatorUsername());
reply.setWhoData(new LPCMixed(list));
synchronized(this) {
if (conn != null) {
conn.send(reply);
}
}
break; }
}
}.start();
}
private void handleError(ErrorPacket packet) {
String errcode = packet.getErrorCode().asString();
if (errcode.equals(ErrorPacket.ROUTER_ERROR_CODE_OPERATION_NOT_ALLOWED)) {
I3Packet tmp = packet.getErrorPacket();
if (tmp != null) {
switch (tmp.getType()) {
case CHANNEL_LISTEN: {
ChannelListenPacket listenPacket = (ChannelListenPacket) tmp;
String channel = listenPacket.getChannelName().asString();
Set<I3ChannelListener> set;
synchronized(this) {
set = channelListeners.get(channel);
if (set != null) {
set = new HashSet<I3ChannelListener>(set);
}
}
if (set != null) {
Iterator<I3ChannelListener> iter = set.iterator();
while (iter.hasNext()) {
I3ChannelListener l = iter.next();
l.channelRemoved(channel, packet.getErrorMessage());
iter.remove();
}
channelListeners.remove(channel);
}
return; }
}
}
}
else if (errcode.equals(ErrorPacket.ROUTER_ERROR_CODE_UNKNOWN_DESTINATION_MUD)) {
I3Packet tmp = packet.getErrorPacket();
if (tmp != null) {
switch (tmp.getType()) {
case TELL: {
TellPacket tellPacket = (TellPacket) tmp;
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.tellFailed(packet.getTargetUsername(), tellPacket.getTargetMudName(), tellPacket.getTargetUsername(), packet.getErrorMessage());
}
return; }
case WHO_REQ: {
WhoReqPacket whoPacket = (WhoReqPacket) tmp;
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.whoFailed(packet.getTargetUsername(), whoPacket.getTargetMudName(), packet.getErrorMessage());
}
return; }
}
}
}
else if (errcode.equals(ErrorPacket.MUD_ERROR_CODE_UNKNOWN_TARGET_USER)) {
I3Packet tmp = packet.getErrorPacket();
if (tmp != null) {
switch (tmp.getType()) {
case TELL: {
TellPacket tellPacket = (TellPacket) tmp;
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.tellFailed(packet.getTargetUsername(), tellPacket.getTargetMudName(), tellPacket.getTargetUsername(), packet.getErrorMessage());
}
return; }
}
}
}
Set<I3EventListener> set;
synchronized(this) {
set = new HashSet<I3EventListener>(listeners);
}
for (I3EventListener l : set) {
l.i3Error(packet);
}
}
public synchronized boolean isConnected() {
return conn != null;
}
public synchronized Map<String,MudInfo> getMudList() {
return new LinkedHashMap<String,MudInfo>(mudMap);
}
public synchronized Map<String,ChanInfo> getChannelList() {
return new LinkedHashMap<String,ChanInfo>(channelMap);
}
private class MyPacketListener implements I3PacketListener {
public void packetReceived(I3Connection c, I3Packet packet) {
switch(packet.getType()) {
case MUD_LIST:
handleMudList((MudListPacket) packet); break;
case CHANLIST_REPLY:
handleChanList((ChanListReplyPacket) packet); break;
case CHANNEL_M:
handleChannelM((ChannelMPacket) packet); break;
case CHANNEL_E:
handleChannelE((ChannelEPacket) packet); break;
case CHANNEL_T:
handleChannelT((ChannelTPacket) packet); break;
case EMOTE_TO:
handleEmoteTo((EmoteToPacket) packet); break;
case TELL:
handleTell((TellPacket) packet); break;
case WHO_REQ:
handleWhoReq((WhoReqPacket) packet); break;
case WHO_REPLY:
handleWhoReply((WhoReplyPacket) packet); break;
case ERROR:
handleError((ErrorPacket) packet); break;
case STARTUP_REPLY:
break;
default:
System.err.println("Warning: I3Client doesn't know how to handle this packet: " + packet);
}
}
public void unrecognizedPacket(I3Connection c, String packet) {
System.err.println("Warning: Unrecognized packet: " + packet);
}
public void connectionClosed(I3Connection c) {
synchronized(I3Client.this) {
conn = null;
}
autoconnect();
}
}
public synchronized void send(TellPacket packet) throws I3NotConnectedException {
if (conn == null) {
throw new I3NotConnectedException();
}
packet.setOriginatorMudName(new LPCMixed(mudName));
conn.send(packet);
}
public synchronized void send(ChannelMPacket packet) throws I3NotConnectedException {
if (conn == null) {
throw new I3NotConnectedException();
}
packet.setOriginatorMudName(new LPCMixed(mudName));
conn.send(packet);
}
public synchronized void send(ChannelEPacket packet) throws I3NotConnectedException {
if (conn == null) {
throw new I3NotConnectedException();
}
packet.setOriginatorMudName(new LPCMixed(mudName));
conn.send(packet);
}
public synchronized void send(ChannelTPacket packet) throws I3NotConnectedException {
if (conn == null) {
throw new I3NotConnectedException();
}
packet.setOriginatorMudName(new LPCMixed(mudName));
conn.send(packet);
}
public synchronized boolean isValidChannel(String channel) {
return channelMap.containsKey(channel);
}
public synchronized void sendWho(LPCMixed username, LPCMixed targetmud) throws I3NotConnectedException {
if (conn == null) {
throw new I3NotConnectedException();
}
WhoReqPacket packet = new WhoReqPacket();
packet.setOriginatorMudName(new LPCMixed(mudName));
packet.setOriginatorUsername(username);
packet.setTargetMudName(targetmud);
conn.send(packet);
}
public synchronized MudInfo getMudInfo(String mudname) {
return mudMap.get(mudname);
}
private synchronized void tellFailed(TellPacket packet, String msg) {
if (conn == null) {
return;
}
ErrorPacket errorPacket = new ErrorPacket();
errorPacket.setErrorCode(new LPCMixed(ErrorPacket.MUD_ERROR_CODE_UNKNOWN_TARGET_USER));
errorPacket.setErrorMessage(new LPCMixed(msg));
errorPacket.setErrorPacket(packet);
errorPacket.setOriginatorMudName(new LPCMixed(mudName));
errorPacket.setTargetMudName(packet.getOriginatorMudName());
errorPacket.setTargetUsername(packet.getOriginatorUsername());
conn.send(errorPacket);
}
private synchronized void emoteToFailed(EmoteToPacket packet, String msg) {
if (conn == null) {
return;
}
ErrorPacket errorPacket = new ErrorPacket();
errorPacket.setErrorCode(new LPCMixed(ErrorPacket.MUD_ERROR_CODE_UNKNOWN_TARGET_USER));
errorPacket.setErrorMessage(new LPCMixed(msg));
errorPacket.setErrorPacket(packet);
errorPacket.setOriginatorMudName(new LPCMixed(mudName));
errorPacket.setTargetMudName(packet.getOriginatorMudName());
errorPacket.setTargetUsername(packet.getOriginatorUsername());
conn.send(errorPacket);
}
}