/* * ChatBot.java * * Created on September 14, 2010, 3:30 PM ************************************************************************* * Copyright 2010 Kevin Kendall * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ao.chat; import ao.protocol.packets.utils.SimplePacketFactory; import ao.protocol.*; import ao.protocol.packets.Packet; import ao.protocol.packets.bi.*; import ao.protocol.packets.toclient.*; import ao.protocol.packets.toserver.*; import ao.protocol.packets.utils.PacketFactory; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import ao.event.EventListenerList; import java.util.ArrayList; public class ChatClient implements Client { //Base variables private PacketFactory m_packetFactory; private Thread m_thread = null; private ClientState m_state = ClientState.DISCONNECTED; private int m_dimension = 0; private String m_loginSeed = null; private CharacterInfo m_character = null; private int m_pingDelay; private boolean m_debug = false; //Connection private Socket m_socket = null; private DataInputStream m_in = null; private DataOutputStream m_out = null; private boolean timingout = false; //Events private final EventListenerList m_listeners = new EventListenerList(); // used for synchronization private final Object m_readLock = new Object(); private final Object m_writeLock = new Object(); private final Object m_stateLock = new Object(); private ArrayList<Object[]> lookupQueue = new ArrayList<Object[]>(); private PacketQueue packetQueue; //Chat related private CharacterIDTable chartable = new CharacterIDTable(); private GroupTable grouptable = new GroupTable(); //Tell information private int lastTellOut = -1; private int lastTellIn = -1; //Org information private String orgName = null; private byte[] orgID = null; public enum Queue { TELL, FADD, FREM, FDEL, INVITE, KICK; } // end enum ClientState /** Creates a new instance of ChatClient */ public ChatClient() { this(60000, new SimplePacketFactory(), false); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(SimplePacketFactory packetFactory) { this(60000, packetFactory, false); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(boolean debug) { this(60000, new SimplePacketFactory(), debug); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(int pingDelay) { this(pingDelay, new SimplePacketFactory(), false); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(int pingDelay, SimplePacketFactory packetFactory) { this(pingDelay, packetFactory, false); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(SimplePacketFactory packetFactory, boolean debug) { this(60000, packetFactory, debug); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(int pingDelay, boolean debug) { this(pingDelay, new SimplePacketFactory(), debug); } // end ChatClient /** Creates a new instance of ChatClient */ public ChatClient(int pingDelay, PacketFactory packetFactory, boolean debug) { m_pingDelay = pingDelay; m_packetFactory = packetFactory; m_debug = debug; packetQueue = new PacketQueue(this); Thread thread = new Thread(packetQueue); thread.start(); } // end ChatClient public ClientState getState() { synchronized (m_stateLock) { return m_state; } } // end getState() public CharacterInfo getCharacter() { return m_character; } public Packet nextPacket() throws IOException { synchronized (m_readLock) { if (m_state == ClientState.DISCONNECTED) { throw new ClientStateException( "This bot is not currently connected to a server. It must be connected before packets can be read.", m_state, ClientState.CONNECTED); } else { try { // Read and parse a packet from the input stream short type = m_in.readShort(); short length = m_in.readShort(); byte[] data = new byte[length]; m_in.readFully(data); Packet packet = m_packetFactory.toPacket(type, data); // DEBUG: display that a packet was recieved if (m_debug) { System.out.println("IN: " + packet); } // Return the packet return packet; } catch (SocketTimeoutException ex) { if (m_state != ClientState.DISCONNECTED) { if (!timingout) { timingout = true; // Send a ping sendPacket(new PingPacket("Java AOChat API ping", Packet.Direction.TO_SERVER)); // Read a ping Packet packet = nextPacket(); return packet; } else { if (m_socket == null || m_socket.isClosed()) { println("Connection Lost..."); disconnect(); return null; } else { println("Connection Lost..."); disconnect(); throw ex; } // end else } } else { return null; } } catch (SocketException ex) { if (m_socket == null || m_socket.isClosed()) { println("Connection Lost..."); disconnect(); return null; } else { println("Connection Lost..."); disconnect(); throw ex; } // end else } catch (EOFException ex) { if (m_socket == null || m_socket.isClosed()) { println("Connection Lost..."); disconnect(); return null; } else { println("Connection Lost..."); disconnect(); throw ex; } // end else } // end catch } // end synchronized } // end else } // end nextPacket() public void sendPacket(Packet packet) throws IOException { synchronized (m_writeLock) { if (m_state == ClientState.DISCONNECTED) { throw new ClientStateException( "This bot is not currently connected to a server. It must be connected before packets can be sent.", m_state, ClientState.CONNECTED); } else if (packet instanceof PrivateMessagePacket && !packetQueue.canSend()) { packetQueue.add(packet); } else if (packet instanceof ChannelMessagePacket && !packetQueue.canSend()) { packetQueue.add(packet); } else { try { short type = packet.getType(); byte[] data = packet.getData(); m_out.writeShort(type); m_out.writeShort(data.length); m_out.write(data, 0, data.length); m_out.flush(); // DEBUG: display that a packet was sent if (m_debug) { System.out.println("OUT: " + packet); } } catch (SocketException ex) { if (m_socket.isClosed()) { println("Connection Lost..."); disconnect(); return; } else { throw ex; } // end else } // end catch } // end else } // end synchronized } // end sendPacket() public void connect(int dimension) throws IOException { connect(DimensionAddress.values()[dimension - 1]); } // end connect() public void connect(DimensionAddress server) throws IOException { connect(server.getURL(), server.getPort(), Game.AO); } // end connect() public void connect(String server, int port) throws IOException { connect(server, port, Game.AO); } // end connect() public void connect(String server, int port, Game game) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.DISCONNECTED) { throw new ClientStateException( "This bot is already connected to a server. It must be disconnected before you can connect it to a server.", m_state, ClientState.DISCONNECTED); } else { try { m_dimension = 0; for (DimensionAddress d : DimensionAddress.values()) { if (d.getURL().compareTo(server) == 0) { m_dimension = d.getID(); } } m_socket = new Socket(server, port); m_in = new DataInputStream(m_socket.getInputStream()); m_out = new DataOutputStream(m_socket.getOutputStream()); m_socket.setSoTimeout(m_pingDelay); m_state = ClientState.CONNECTED; if (game == Game.AO) { Packet packet = nextPacket(); if (packet instanceof LoginSeedPacket) { m_loginSeed = ((LoginSeedPacket) packet).getLoginSeed(); println("Connected"); fireConnected(); } else { println("Failed to connect"); disconnect(); } // end else firePacket(packet); } else { println("Connected"); fireConnected(); } } catch (IOException ex) { println("Failed to connect"); disconnect(); throw ex; } // end catch } // end else } // end synchronized } // end connect() public void authenticate(String accountName, String password) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.CONNECTED) { throw new ClientStateException( "This bot is either already authenticated or disconnected. " + "It must connected and unauthenticated to be authenticated.", m_state, ClientState.CONNECTED); } else { try { String key = LoginKeyGenerator.generateLoginKey(m_loginSeed, accountName, password); Packet packet = new LoginRequestPacket(LoginKeyGenerator.PROTOCOL_VERSION, accountName, key); sendPacket(packet); packet = nextPacket(); if (packet instanceof CharacterListPacket) { m_state = ClientState.AUTHENTICATED; println("Authenticated"); fireAuthenticated(); } else { println("Failed to authenticate"); disconnect(); } // end else firePacket(packet); } catch (IOException ex) { println("Failed to authenticate"); disconnect(); throw ex; } // end catch } // end else } // end synchronized } // end authenticate() public void login(CharacterInfo character) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.AUTHENTICATED) { throw new ClientStateException( "This bot is either already logged in or unauthenticated. " + "It must connected, authenticated, and logged out in order to be logged in.", m_state, ClientState.AUTHENTICATED); } else if (character == null) { throw new CharNotFoundException( "Character is null, unable to login", -1); } else { try { Packet packet = new LoginSelectPacket(character.getID()); sendPacket(packet); packet = nextPacket(); if (packet instanceof LoginOkPacket) { m_state = ClientState.LOGGED_IN; m_character = character; // Encourage garbage collection System.runFinalization(); System.gc(); println("Logged in"); fireLoggedIn(); } else { println("Failed to log in"); disconnect(); } // end else firePacket(packet); } catch (IOException ex) { println("Failed to log in"); disconnect(); throw ex; } // end catch } // end else } // end synchronized } // end login() public void disconnect() throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.DISCONNECTED) { if (m_socket != null) { if (m_thread != null) { stopThread();//m_thread.stop(); } m_socket.close(); m_in.close(); m_out.close(); m_socket = null; m_in = null; m_out = null; timingout = false; } else { System.err.println("Socket is null when disconnecting"); }// end if m_loginSeed = null; m_character = null; chartable.reset(); grouptable.reset(); m_state = ClientState.DISCONNECTED; // Encourage garbage collection System.runFinalization(); System.gc(); println("Disconnected"); fireDisconnected(); } // end if } // end synchronized } // end disconnect() @Override protected void finalize() throws Throwable { super.finalize(); disconnect(); } // end finalize() public void start() { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can be started.", m_state, ClientState.LOGGED_IN); } else { if (m_thread == null || !m_thread.isAlive()) { m_thread = new Thread(this); m_thread.start(); println("Started"); fireStarted(); } // end if } // end else } // end synchronized } // end start() private void stopThread() { if (m_thread != null) { Thread temp = m_thread; m_thread = null; temp.interrupt(); } } public void run() { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException("This bot is not currently logged in.", getState(), ClientState.LOGGED_IN); } // Start listening for packets while (getState() == ClientState.LOGGED_IN) { try { Packet packet = nextPacket(); if (packet instanceof PingPacket) { PingPacket ping = (PingPacket) packet; if (ping.getDirection() == Packet.Direction.TO_CLIENT) { timingout = false; } } else if (packet instanceof CharacterUpdatePacket) { CharacterUpdatePacket namePacket = (CharacterUpdatePacket) packet; chartable.add(namePacket.getCharacterID(), namePacket.getCharacterName()); } else if (packet instanceof CharacterLookupPacket) { CharacterLookupPacket lookPacket = (CharacterLookupPacket) packet; chartable.add(lookPacket.getCharacterID(), lookPacket.getCharacterName()); searchQueue(chartable.getID(lookPacket.getCharacterName()), lookPacket.getCharacterName()); if (lookPacket.getCharacterID() == -1) { println("Character " + lookPacket.getCharacterName() + " does not exist"); } } else if (packet instanceof ChannelUpdatePacket) { ChannelUpdatePacket updatePacket = (ChannelUpdatePacket) packet; grouptable.add(updatePacket.getGroupID(), updatePacket.getGroupName()); if (updatePacket.getGroupID()[0] == 0x03) { orgName = updatePacket.getGroupName(); orgID = updatePacket.getGroupID(); } } else if (packet instanceof PrivateMessagePacket) { PrivateMessagePacket msgPacket = (PrivateMessagePacket) packet; lastTellIn = msgPacket.getCharID(); } firePacket(packet); try { Thread.sleep(1); } catch (InterruptedException ex) { } } catch (Exception e) { fireException(e); } } } /** * Actions */ private synchronized void enQueue(Queue type, String name, String msg) { Object[] temp = new Object[3]; temp[0] = type; temp[1] = name; temp[2] = msg; lookupQueue.add(temp); } /** * Searches the lookup queue for a match * @param id * @param name * @throws CharNotFoundException */ private synchronized void searchQueue(int id, String name) { for (int i = 0; i < lookupQueue.size(); i++) { Object[] current = lookupQueue.get(i); Queue type = (Queue) current[0]; String lname = (String) current[1]; String msg = (String) current[2]; if (id != -1 && lname.compareTo(name) == 0) {//Verify that the name is valid try { switch (type) { case TELL: sendTell(lname, msg, false); break; case FADD: addFriend(lname, false); break; case FREM: removeFriend(lname, false); break; case FDEL: deleteFriend(lname, false); break; case INVITE: inviteUser(lname, false); break; case KICK: kickUser(lname, false); break; default: System.out.println("Error: invalid lookup in queue"); break; } } catch (Exception e) { fireException(e); } lookupQueue.remove(i); i--; } } } public void sendChannelMessage(String channel, String msg) throws IOException { byte[] id = grouptable.getID(channel); if (id != null) { sendChannelMessage(id, msg); } } public void sendChannelMessage(byte[] channel, String msg) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else if (msg.compareTo("") != 0) { Packet packet = new ChannelMessagePacket(channel, msg); sendPacket(packet); } } } public void sendPrivateChannelMessage(String channel, String msg) throws IOException { if (chartable.getID(channel) != null && chartable.getID(channel) != -1) { sendPrivateChannelMessage(chartable.getID(channel), msg); } } public void sendPrivateChannelMessage(int channel, String msg) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else if (msg.compareTo("") != 0) { Packet packet = new PrivateChannelMessagePacket(channel, msg); sendPacket(packet); } } } public void lookup(String name) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can perform a lookup.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new CharacterLookupPacket(name); sendPacket(packet); } } } public void sendTell(String name, String msg, boolean lookup) throws IOException { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { sendTell(id, msg); } else if (lookup) { enQueue(Queue.TELL, name, msg); lookup(name); } } public void sendTell(int id, String msg) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else if (id == -1) { throw new CharNotFoundException("Could not send tell to character", id); } else if (msg.compareTo("") != 0) { Packet packet = new PrivateMessagePacket(id, msg); sendPacket(packet); firePacket(packet); lastTellOut = id; } } } public void joinChannel(String channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else { if (chartable.getID(channel) != null && chartable.getID(channel) != -1) { Packet packet = new PrivateChannelAcceptPacket(chartable.getID(channel)); sendPacket(packet); } } } } public void joinChannel(int channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new PrivateChannelAcceptPacket(channel); sendPacket(packet); } } } public void leaveChannel(String channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else { if (chartable.getID(channel) != null && chartable.getID(channel) != -1) { Packet packet = new PrivateChannelLeavePacket(chartable.getID(channel)); sendPacket(packet); } } } } public void leaveChannel(int channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send messages.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new PrivateChannelLeavePacket(channel); sendPacket(packet); } } } public void addFriend(String name, boolean lookup) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before you can add a friend.", m_state, ClientState.LOGGED_IN); } else { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { Packet packet = new FriendUpdatePacket(id, true); sendPacket(packet); } else if (lookup) { Packet packet = new CharacterLookupPacket(name); //Add to friend update queue enQueue(Queue.FADD, name, ""); sendPacket(packet); } } } } public void removeFriend(String name, boolean lookup) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before you can remove a friend.", m_state, ClientState.LOGGED_IN); } else { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { Packet packet = new FriendUpdatePacket(id, false); sendPacket(packet); } else if (lookup) { Packet packet = new CharacterLookupPacket(name); //Add to friend update queue enQueue(Queue.FREM, name, ""); sendPacket(packet); } } } } public void deleteFriend(String name, boolean lookup) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can delete a friend.", m_state, ClientState.LOGGED_IN); } else { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { Packet packet = new FriendRemovePacket(id); sendPacket(packet); } else if (lookup) { Packet packet = new CharacterLookupPacket(name); //Add to friend remove queue enQueue(Queue.FDEL, name, ""); sendPacket(packet); } } } } public void clearFriends() throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before you can clear your temporary friends.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new ChatCommandPacket("rem buddy ?"); sendPacket(packet); } } } public void inviteUser(String name, boolean lookup) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { Packet packet = new PrivateChannelInvitePacket(id); sendPacket(packet); firePacket(packet); } else if (lookup) { Packet packet = new CharacterLookupPacket(name); //Add to group invite queue enQueue(Queue.INVITE, name, ""); sendPacket(packet); } } } } public void inviteUser(int id) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new PrivateChannelInvitePacket(id); sendPacket(packet); firePacket(packet); } } } public void kickUser(String name, boolean lookup) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { int id = -1; if (chartable.getID(name) != null) { id = chartable.getID(name); } if (id != -1) { Packet packet = new PrivateChannelKickPacket(id); sendPacket(packet); firePacket(packet); } else if (lookup) { Packet packet = new CharacterLookupPacket(name); //Add to group kick queue enQueue(Queue.KICK, name, ""); sendPacket(packet); } } } } public void kickUser(int id) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new PrivateChannelKickPacket(id); sendPacket(packet); firePacket(packet); } } } public void kickAll() throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { Packet packet = new PrivateChannelKickAllPacket(); sendPacket(packet); firePacket(packet); } } } public void acceptInvite(String channel) throws IOException { if (chartable.getID(channel) != null && chartable.getID(channel) != -1) { acceptInvite(chartable.getID(channel)); } } public void acceptInvite(int channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { if (channel != -1) { Packet packet = new PrivateChannelAcceptPacket(channel); sendPacket(packet); } } } } public void denyInvite(String channel) throws IOException { if (chartable.getID(channel) != null && chartable.getID(channel) != -1) { denyInvite(chartable.getID(channel)); } } public void denyInvite(int channel) throws IOException { synchronized (m_stateLock) { if (m_state != ClientState.LOGGED_IN) { throw new ClientStateException( "This bot is not currently logged in, it must be logged in before it can send tells.", m_state, ClientState.LOGGED_IN); } else { if (channel != -1) { Packet packet = new PrivateChannelLeavePacket(channel); sendPacket(packet); } } } } public CharacterIDTable getCharTable() { return chartable; } public GroupTable getGroupTable() { return grouptable; } public int getLastTellIn() { return lastTellIn; } public int getLastTellOut() { return lastTellOut; } public int getDimensionID() { return m_dimension; } public String getOrgName() { return orgName; } public byte[] getOrgID() { return orgID; } /** * Listeners */ public void addListener(ClientListener l) { m_listeners.add(ClientListener.class, l); } // end addListener public void removeListener(ClientListener l) { m_listeners.remove(ClientListener.class, l); } // end removeListener() public void addPacketListener(PacketListener l) { m_listeners.add(PacketListener.class, l); } // end addListener public void removePacketListener(PacketListener l) { m_listeners.remove(PacketListener.class, l); } // end removeListener() /** * Fires a connected event to all listeners * @see ao.protocol.ClientListener#connected(ao.protocol.Client) */ protected void fireConnected() { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.connected(this); } // end for } // end fireConnected() /** * Fires a authenticated event to all listeners * @see ao.protocol.ClientListener#authenticated(ao.protocol.Client) */ protected void fireAuthenticated() { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.authenticated(this); } // end for } // end fireAuthenticated() /** * Fires a logged in event to all listeners * @see ao.protocol.ClientListener#loggedIn(ao.protocol.Client) */ protected void fireLoggedIn() { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.loggedIn(this); } // end for } // end fireLoggedIn() /** * Fires a started event to all listeners * @see ao.protocol.ClientListener#started(ao.protocol.Client) */ protected void fireStarted() { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.started(this); } // end for } // end fireStarted() /** * Fires a disconnected event to all listeners * @see ao.protocol.ClientListener#disconnected(ao.protocol.Client) */ protected void fireDisconnected() { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.disconnected(this); } // end for } // end fireDisconnected() protected void firePacket(Packet packet) { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.packet(this, packet); } // end for PacketListener[] plisteners = m_listeners.getListeners(PacketListener.class); for (PacketListener l : plisteners) { l.packet(this, packet); } // end for } protected void fireException(Exception e) { ClientListener[] listeners = m_listeners.getListeners(ClientListener.class); for (ClientListener l : listeners) { l.exception(this, e); } // end for } /** * Loggers */ public void addLogger(ClientLogger l) { m_listeners.add(ClientLogger.class, l); } // end addListener public void removeLogger(ClientLogger l) { m_listeners.remove(ClientLogger.class, l); } // end removeListener() /** * Tells all loggers to print a string * @see ao.protocol.ClientLogger#print(ao.protocol.Client, String) */ protected void println(String msg) { ClientLogger[] listeners = m_listeners.getListeners(ClientLogger.class); for (ClientLogger l : listeners) { l.print(this, msg); } // end for } // end println() @Override public String toString() { if (m_character != null) { return m_character.getName(); } else { return null; } } } // end class ChatClient