Added lots of javadoc.
This commit is contained in:
		
							parent
							
								
									96fd07b3fe
								
							
						
					
					
						commit
						2d8a0967dc
					
				| 
						 | 
					@ -29,6 +29,7 @@ public class ConcordClient implements Runnable {
 | 
				
			||||||
	private final Socket socket;
 | 
						private final Socket socket;
 | 
				
			||||||
	private final DataInputStream in;
 | 
						private final DataInputStream in;
 | 
				
			||||||
	private final DataOutputStream out;
 | 
						private final DataOutputStream out;
 | 
				
			||||||
 | 
						private final Serializer serializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	@Getter
 | 
						@Getter
 | 
				
			||||||
	private final ClientModel model;
 | 
						private final ClientModel model;
 | 
				
			||||||
| 
						 | 
					@ -42,16 +43,8 @@ public class ConcordClient implements Runnable {
 | 
				
			||||||
		this.socket = new Socket(host, port);
 | 
							this.socket = new Socket(host, port);
 | 
				
			||||||
		this.in = new DataInputStream(this.socket.getInputStream());
 | 
							this.in = new DataInputStream(this.socket.getInputStream());
 | 
				
			||||||
		this.out = new DataOutputStream(this.socket.getOutputStream());
 | 
							this.out = new DataOutputStream(this.socket.getOutputStream());
 | 
				
			||||||
		Serializer.writeMessage(new Identification(nickname), this.out);
 | 
							this.serializer = new Serializer();
 | 
				
			||||||
		Message reply = Serializer.readMessage(this.in);
 | 
							this.model = this.initializeConnectionToServer(nickname);
 | 
				
			||||||
		if (reply instanceof ServerWelcome welcome) {
 | 
					 | 
				
			||||||
			this.model = new ClientModel(welcome.getClientId(), nickname, welcome.getCurrentChannelId(), welcome.getMetaData());
 | 
					 | 
				
			||||||
			// Start fetching initial data for the channel we were initially put into.
 | 
					 | 
				
			||||||
			this.sendMessage(new ChannelUsersRequest(this.model.getCurrentChannelId()));
 | 
					 | 
				
			||||||
			this.sendMessage(new ChatHistoryRequest(this.model.getCurrentChannelId(), ""));
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			throw new IOException("Unexpected response from the server after sending identification message.");
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Add event listeners.
 | 
							// Add event listeners.
 | 
				
			||||||
		this.eventManager.addHandler(MoveToChannel.class, new ChannelMovedHandler());
 | 
							this.eventManager.addHandler(MoveToChannel.class, new ChannelMovedHandler());
 | 
				
			||||||
| 
						 | 
					@ -61,12 +54,38 @@ public class ConcordClient implements Runnable {
 | 
				
			||||||
		this.eventManager.addHandler(ServerMetaData.class, new ServerMetaDataHandler());
 | 
							this.eventManager.addHandler(ServerMetaData.class, new ServerMetaDataHandler());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Initializes the communication with the server by sending an {@link Identification}
 | 
				
			||||||
 | 
						 * message, and waiting for a {@link ServerWelcome} response from the
 | 
				
			||||||
 | 
						 * server. After that, we request some information about the channel we were
 | 
				
			||||||
 | 
						 * placed in by the server.
 | 
				
			||||||
 | 
						 * @param nickname The nickname to send to the server that it should know
 | 
				
			||||||
 | 
						 *                 us by.
 | 
				
			||||||
 | 
						 * @return The client model that contains the server's metadata and other
 | 
				
			||||||
 | 
						 * information that should be kept up-to-date at runtime.
 | 
				
			||||||
 | 
						 * @throws IOException If an error occurs while reading or writing the
 | 
				
			||||||
 | 
						 * messages, or if the server sends an unexpected response.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private ClientModel initializeConnectionToServer(String nickname) throws IOException {
 | 
				
			||||||
 | 
							this.serializer.writeMessage(new Identification(nickname), this.out);
 | 
				
			||||||
 | 
							Message reply = this.serializer.readMessage(this.in);
 | 
				
			||||||
 | 
							if (reply instanceof ServerWelcome welcome) {
 | 
				
			||||||
 | 
								var model = new ClientModel(welcome.getClientId(), nickname, welcome.getCurrentChannelId(), welcome.getMetaData());
 | 
				
			||||||
 | 
								// Start fetching initial data for the channel we were initially put into.
 | 
				
			||||||
 | 
								this.sendMessage(new ChannelUsersRequest(this.model.getCurrentChannelId()));
 | 
				
			||||||
 | 
								this.sendMessage(new ChatHistoryRequest(this.model.getCurrentChannelId(), ""));
 | 
				
			||||||
 | 
								return model;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								throw new IOException("Unexpected response from the server after sending identification message.");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void sendMessage(Message message) throws IOException {
 | 
						public void sendMessage(Message message) throws IOException {
 | 
				
			||||||
		Serializer.writeMessage(message, this.out);
 | 
							this.serializer.writeMessage(message, this.out);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void sendChat(String message) throws IOException {
 | 
						public void sendChat(String message) throws IOException {
 | 
				
			||||||
		Serializer.writeMessage(new Chat(this.model.getId(), this.model.getNickname(), System.currentTimeMillis(), message), this.out);
 | 
							this.serializer.writeMessage(new Chat(this.model.getId(), this.model.getNickname(), System.currentTimeMillis(), message), this.out);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public void shutdown() {
 | 
						public void shutdown() {
 | 
				
			||||||
| 
						 | 
					@ -85,7 +104,7 @@ public class ConcordClient implements Runnable {
 | 
				
			||||||
		this.running = true;
 | 
							this.running = true;
 | 
				
			||||||
		while (this.running) {
 | 
							while (this.running) {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				Message msg = Serializer.readMessage(this.in);
 | 
									Message msg = this.serializer.readMessage(this.in);
 | 
				
			||||||
				this.eventManager.handle(msg);
 | 
									this.eventManager.handle(msg);
 | 
				
			||||||
			} catch (IOException e) {
 | 
								} catch (IOException e) {
 | 
				
			||||||
				e.printStackTrace();
 | 
									e.printStackTrace();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,12 @@ import nl.andrewl.concord_core.msg.types.ChannelUsersRequest;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
 | 
					import nl.andrewl.concord_core.msg.types.ChatHistoryRequest;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.types.MoveToChannel;
 | 
					import nl.andrewl.concord_core.msg.types.MoveToChannel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * When the client receives a {@link MoveToChannel} message, it means that the
 | 
				
			||||||
 | 
					 * server has told the client that it has been moved to the indicated channel.
 | 
				
			||||||
 | 
					 * Thus, the client must now update its model and request the relevant info from
 | 
				
			||||||
 | 
					 * the server about the new channel it's in.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class ChannelMovedHandler implements MessageHandler<MoveToChannel> {
 | 
					public class ChannelMovedHandler implements MessageHandler<MoveToChannel> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void handle(MoveToChannel msg, ConcordClient client) throws Exception {
 | 
						public void handle(MoveToChannel msg, ConcordClient client) throws Exception {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,10 @@ import nl.andrewl.concord_client.ConcordClient;
 | 
				
			||||||
import nl.andrewl.concord_client.event.MessageHandler;
 | 
					import nl.andrewl.concord_client.event.MessageHandler;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
 | 
					import nl.andrewl.concord_core.msg.types.ChannelUsersResponse;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * When the client receives information about the list of known users, it will
 | 
				
			||||||
 | 
					 * update its model to show the new list.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
public class ChannelUsersResponseHandler implements MessageHandler<ChannelUsersResponse> {
 | 
					public class ChannelUsersResponseHandler implements MessageHandler<ChannelUsersResponse> {
 | 
				
			||||||
	@Override
 | 
						@Override
 | 
				
			||||||
	public void handle(ChannelUsersResponse msg, ConcordClient client) throws Exception {
 | 
						public void handle(ChannelUsersResponse msg, ConcordClient client) throws Exception {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,9 +11,23 @@ import java.util.Map;
 | 
				
			||||||
 * This class is responsible for reading and writing messages from streams.
 | 
					 * This class is responsible for reading and writing messages from streams.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class Serializer {
 | 
					public class Serializer {
 | 
				
			||||||
	private static final Map<Byte, Class<? extends Message>> messageTypes = new HashMap<>();
 | 
						/**
 | 
				
			||||||
	private static final Map<Class<? extends Message>, Byte> inverseMessageTypes = new HashMap<>();
 | 
						 * The mapping which defines each supported message type and the byte value
 | 
				
			||||||
	static {
 | 
						 * used to identify it when reading and writing messages.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private final Map<Byte, Class<? extends Message>> messageTypes = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * An inverse of {@link Serializer#messageTypes} which is used to look up a
 | 
				
			||||||
 | 
						 * message's byte value when you know the class of the message.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private final Map<Class<? extends Message>, Byte> inverseMessageTypes = new HashMap<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new serializer instance, with a standard set of supported
 | 
				
			||||||
 | 
						 * message types.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Serializer() {
 | 
				
			||||||
		registerType(0, Identification.class);
 | 
							registerType(0, Identification.class);
 | 
				
			||||||
		registerType(1, ServerWelcome.class);
 | 
							registerType(1, ServerWelcome.class);
 | 
				
			||||||
		registerType(2, Chat.class);
 | 
							registerType(2, Chat.class);
 | 
				
			||||||
| 
						 | 
					@ -26,12 +40,29 @@ public class Serializer {
 | 
				
			||||||
		registerType(9, Error.class);
 | 
							registerType(9, Error.class);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private static void registerType(int id, Class<? extends Message> clazz) {
 | 
						/**
 | 
				
			||||||
		messageTypes.put((byte) id, clazz);
 | 
						 * Helper method which registers a message type to be supported by the
 | 
				
			||||||
		inverseMessageTypes.put(clazz, (byte) id);
 | 
						 * serializer, by adding it to the normal and inverse mappings.
 | 
				
			||||||
 | 
						 * @param id The byte which will be used to identify messages of the given
 | 
				
			||||||
 | 
						 *           class. The value should from 0 to 127.
 | 
				
			||||||
 | 
						 * @param messageClass The class of message which is registered with the
 | 
				
			||||||
 | 
						 *                     given byte identifier.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						private synchronized void registerType(int id, Class<? extends Message> messageClass) {
 | 
				
			||||||
 | 
							messageTypes.put((byte) id, messageClass);
 | 
				
			||||||
 | 
							inverseMessageTypes.put(messageClass, (byte) id);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static Message readMessage(InputStream i) throws IOException {
 | 
						/**
 | 
				
			||||||
 | 
						 * Reads a message from the given input stream and returns it, or throws an
 | 
				
			||||||
 | 
						 * exception if an error occurred while reading from the stream.
 | 
				
			||||||
 | 
						 * @param i The input stream to read from.
 | 
				
			||||||
 | 
						 * @return The message which was read.
 | 
				
			||||||
 | 
						 * @throws IOException If an error occurs while reading, such as trying to
 | 
				
			||||||
 | 
						 * read an unsupported message type, or if a message object could not be
 | 
				
			||||||
 | 
						 * constructed for the incoming data.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public Message readMessage(InputStream i) throws IOException {
 | 
				
			||||||
		DataInputStream d = new DataInputStream(i);
 | 
							DataInputStream d = new DataInputStream(i);
 | 
				
			||||||
		byte type = d.readByte();
 | 
							byte type = d.readByte();
 | 
				
			||||||
		var clazz = messageTypes.get(type);
 | 
							var clazz = messageTypes.get(type);
 | 
				
			||||||
| 
						 | 
					@ -48,7 +79,14 @@ public class Serializer {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public static void writeMessage(Message msg, OutputStream o) throws IOException {
 | 
						/**
 | 
				
			||||||
 | 
						 * Writes a message to the given output stream.
 | 
				
			||||||
 | 
						 * @param msg The message to write.
 | 
				
			||||||
 | 
						 * @param o The output stream to write to.
 | 
				
			||||||
 | 
						 * @throws IOException If an error occurs while writing, or if the message
 | 
				
			||||||
 | 
						 * to write is not supported by this serializer.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						public void writeMessage(Message msg, OutputStream o) throws IOException {
 | 
				
			||||||
		DataOutputStream d = new DataOutputStream(o);
 | 
							DataOutputStream d = new DataOutputStream(o);
 | 
				
			||||||
		Byte type = inverseMessageTypes.get(msg.getClass());
 | 
							Byte type = inverseMessageTypes.get(msg.getClass());
 | 
				
			||||||
		if (type == null) {
 | 
							if (type == null) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,12 @@ import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static nl.andrewl.concord_core.msg.MessageUtils.*;
 | 
					import static nl.andrewl.concord_core.msg.MessageUtils.*;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * This message is sent from the server to the client when the information about
 | 
				
			||||||
 | 
					 * the users in the channel that a client is in has changed. For example, when
 | 
				
			||||||
 | 
					 * a user leaves a channel, all others in that channel will be sent this message
 | 
				
			||||||
 | 
					 * to indicate that update.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
@Data
 | 
					@Data
 | 
				
			||||||
@NoArgsConstructor
 | 
					@NoArgsConstructor
 | 
				
			||||||
@AllArgsConstructor
 | 
					@AllArgsConstructor
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,6 +19,12 @@ import static nl.andrewl.concord_core.msg.MessageUtils.*;
 | 
				
			||||||
@NoArgsConstructor
 | 
					@NoArgsConstructor
 | 
				
			||||||
@AllArgsConstructor
 | 
					@AllArgsConstructor
 | 
				
			||||||
public class Error implements Message {
 | 
					public class Error implements Message {
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * The error level gives an indication as to the severity of the error.
 | 
				
			||||||
 | 
						 * Warnings indicate that a user has attempted to do something which they
 | 
				
			||||||
 | 
						 * shouldn't, or some scenario which is not ideal but recoverable from.
 | 
				
			||||||
 | 
						 * Errors indicate actual issues with the software which should be addressed.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public enum Level {WARNING, ERROR}
 | 
						public enum Level {WARNING, ERROR}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private Level level;
 | 
						private Level level;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * This package contains all the components needed by both the server and the
 | 
					 * This package contains all the components needed by both the server and the
 | 
				
			||||||
 * client.
 | 
					 * client. What that entails is mostly the communication infrastructure which
 | 
				
			||||||
 | 
					 * they both share.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
package nl.andrewl.concord_core;
 | 
					package nl.andrewl.concord_core;
 | 
				
			||||||
| 
						 | 
					@ -37,6 +37,10 @@ public class Channel {
 | 
				
			||||||
		this.initCollection();
 | 
							this.initCollection();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Initializes this channel's nitrite database collection, which involves
 | 
				
			||||||
 | 
						 * creating any indexes that don't yet exist.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	private void initCollection() {
 | 
						private void initCollection() {
 | 
				
			||||||
		if (!this.messageCollection.hasIndex("timestamp")) {
 | 
							if (!this.messageCollection.hasIndex("timestamp")) {
 | 
				
			||||||
			System.out.println("Adding index on \"timestamp\" field to collection " + this.messageCollection.getName());
 | 
								System.out.println("Adding index on \"timestamp\" field to collection " + this.messageCollection.getName());
 | 
				
			||||||
| 
						 | 
					@ -59,6 +63,11 @@ public class Channel {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Adds a client to this channel. Also sends an update to all clients,
 | 
				
			||||||
 | 
						 * including the new one, telling them that a user has joined.
 | 
				
			||||||
 | 
						 * @param clientThread The client to add.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public void addClient(ClientThread clientThread) {
 | 
						public void addClient(ClientThread clientThread) {
 | 
				
			||||||
		this.connectedClients.add(clientThread);
 | 
							this.connectedClients.add(clientThread);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
| 
						 | 
					@ -68,6 +77,11 @@ public class Channel {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Removes a client from this channel. Also sends an update to all the
 | 
				
			||||||
 | 
						 * clients that are still connected, telling them that a user has left.
 | 
				
			||||||
 | 
						 * @param clientThread The client to remove.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public void removeClient(ClientThread clientThread) {
 | 
						public void removeClient(ClientThread clientThread) {
 | 
				
			||||||
		this.connectedClients.remove(clientThread);
 | 
							this.connectedClients.remove(clientThread);
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
| 
						 | 
					@ -79,19 +93,25 @@ public class Channel {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Sends a message to all clients that are currently connected to this
 | 
						 * Sends a message to all clients that are currently connected to this
 | 
				
			||||||
	 * channel.
 | 
						 * channel. Makes use of the server's serializer to preemptively serialize
 | 
				
			||||||
 | 
						 * the data once, so that clients need only write a byte array to their
 | 
				
			||||||
 | 
						 * respective output streams.
 | 
				
			||||||
	 * @param msg The message to send.
 | 
						 * @param msg The message to send.
 | 
				
			||||||
	 * @throws IOException If an error occurs.
 | 
						 * @throws IOException If an error occurs.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void sendMessage(Message msg) throws IOException {
 | 
						public void sendMessage(Message msg) throws IOException {
 | 
				
			||||||
		ByteArrayOutputStream baos = new ByteArrayOutputStream(msg.getByteCount() + 1);
 | 
							ByteArrayOutputStream baos = new ByteArrayOutputStream(msg.getByteCount() + 1);
 | 
				
			||||||
		Serializer.writeMessage(msg, baos);
 | 
							this.server.getSerializer().writeMessage(msg, baos);
 | 
				
			||||||
		byte[] data = baos.toByteArray();
 | 
							byte[] data = baos.toByteArray();
 | 
				
			||||||
		for (var client : this.connectedClients) {
 | 
							for (var client : this.connectedClients) {
 | 
				
			||||||
			client.sendToClient(data);
 | 
								client.sendToClient(data);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Gets a list of information about each user in this channel.
 | 
				
			||||||
 | 
						 * @return A list of {@link UserData} objects.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public List<UserData> getUserData() {
 | 
						public List<UserData> getUserData() {
 | 
				
			||||||
		List<UserData> users = new ArrayList<>(this.connectedClients.size());
 | 
							List<UserData> users = new ArrayList<>(this.connectedClients.size());
 | 
				
			||||||
		for (var clientThread : this.getConnectedClients()) {
 | 
							for (var clientThread : this.getConnectedClients()) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,6 @@ package nl.andrewl.concord_server;
 | 
				
			||||||
import lombok.Getter;
 | 
					import lombok.Getter;
 | 
				
			||||||
import lombok.Setter;
 | 
					import lombok.Setter;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.Message;
 | 
					import nl.andrewl.concord_core.msg.Message;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.Serializer;
 | 
					 | 
				
			||||||
import nl.andrewl.concord_core.msg.types.Identification;
 | 
					import nl.andrewl.concord_core.msg.types.Identification;
 | 
				
			||||||
import nl.andrewl.concord_core.msg.types.UserData;
 | 
					import nl.andrewl.concord_core.msg.types.UserData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -37,6 +36,13 @@ public class ClientThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private volatile boolean running;
 | 
						private volatile boolean running;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Constructs a new client thread.
 | 
				
			||||||
 | 
						 * @param socket The socket to use to communicate with the client.
 | 
				
			||||||
 | 
						 * @param server The server to which this thread belongs.
 | 
				
			||||||
 | 
						 * @throws IOException If we cannot obtain the input and output streams from
 | 
				
			||||||
 | 
						 * the socket.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public ClientThread(Socket socket, ConcordServer server) throws IOException {
 | 
						public ClientThread(Socket socket, ConcordServer server) throws IOException {
 | 
				
			||||||
		this.socket = socket;
 | 
							this.socket = socket;
 | 
				
			||||||
		this.server = server;
 | 
							this.server = server;
 | 
				
			||||||
| 
						 | 
					@ -44,14 +50,24 @@ public class ClientThread extends Thread {
 | 
				
			||||||
		this.out = new DataOutputStream(socket.getOutputStream());
 | 
							this.out = new DataOutputStream(socket.getOutputStream());
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sends the given message to the client. Note that this method is
 | 
				
			||||||
 | 
						 * synchronized, such that multiple messages cannot be sent simultaneously.
 | 
				
			||||||
 | 
						 * @param message The message to send.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public synchronized void sendToClient(Message message) {
 | 
						public synchronized void sendToClient(Message message) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Serializer.writeMessage(message, this.out);
 | 
								this.server.getSerializer().writeMessage(message, this.out);
 | 
				
			||||||
		} catch (IOException e) {
 | 
							} catch (IOException e) {
 | 
				
			||||||
			e.printStackTrace();
 | 
								e.printStackTrace();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Sends the given bytes to the client. This is a shortcut for {@link ClientThread#sendToClient(Message)}
 | 
				
			||||||
 | 
						 * which can be used to optimize message sending in certain instances.
 | 
				
			||||||
 | 
						 * @param bytes The bytes to send.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public synchronized void sendToClient(byte[] bytes) {
 | 
						public synchronized void sendToClient(byte[] bytes) {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			this.out.write(bytes);
 | 
								this.out.write(bytes);
 | 
				
			||||||
| 
						 | 
					@ -61,6 +77,11 @@ public class ClientThread extends Thread {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Shuts down this client thread, closing the underlying socket and setting
 | 
				
			||||||
 | 
						 * {@link ClientThread#running} to false so that the main thread loop will
 | 
				
			||||||
 | 
						 * exit shortly.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
	public void shutdown() {
 | 
						public void shutdown() {
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			this.socket.close();
 | 
								this.socket.close();
 | 
				
			||||||
| 
						 | 
					@ -80,7 +101,7 @@ public class ClientThread extends Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		while (this.running) {
 | 
							while (this.running) {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				var msg = Serializer.readMessage(this.in);
 | 
									var msg = this.server.getSerializer().readMessage(this.in);
 | 
				
			||||||
				this.server.getEventManager().handle(msg, this);
 | 
									this.server.getEventManager().handle(msg, this);
 | 
				
			||||||
			} catch (IOException e) {
 | 
								} catch (IOException e) {
 | 
				
			||||||
				this.running = false;
 | 
									this.running = false;
 | 
				
			||||||
| 
						 | 
					@ -110,7 +131,7 @@ public class ClientThread extends Thread {
 | 
				
			||||||
		int attempts = 0;
 | 
							int attempts = 0;
 | 
				
			||||||
		while (attempts < 5) {
 | 
							while (attempts < 5) {
 | 
				
			||||||
			try {
 | 
								try {
 | 
				
			||||||
				var msg = Serializer.readMessage(this.in);
 | 
									var msg = this.server.getSerializer().readMessage(this.in);
 | 
				
			||||||
				if (msg instanceof Identification id) {
 | 
									if (msg instanceof Identification id) {
 | 
				
			||||||
					this.server.registerClient(id, this);
 | 
										this.server.registerClient(id, this);
 | 
				
			||||||
					return true;
 | 
										return true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,14 @@ public class ConcordServer implements Runnable {
 | 
				
			||||||
	private volatile boolean running;
 | 
						private volatile boolean running;
 | 
				
			||||||
	private final ServerSocket serverSocket;
 | 
						private final ServerSocket serverSocket;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * A utility serializer that's mostly used when preparing a message to
 | 
				
			||||||
 | 
						 * broadcast to a set of users, which is more efficient than having each
 | 
				
			||||||
 | 
						 * individual client thread serialize the same message before sending it.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@Getter
 | 
				
			||||||
 | 
						private final Serializer serializer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Server configuration data. This is used to define channels, discovery
 | 
						 * Server configuration data. This is used to define channels, discovery
 | 
				
			||||||
	 * server addresses, and more.
 | 
						 * server addresses, and more.
 | 
				
			||||||
| 
						 | 
					@ -83,6 +91,7 @@ public class ConcordServer implements Runnable {
 | 
				
			||||||
		this.eventManager = new EventManager(this);
 | 
							this.eventManager = new EventManager(this);
 | 
				
			||||||
		this.channelManager = new ChannelManager(this);
 | 
							this.channelManager = new ChannelManager(this);
 | 
				
			||||||
		this.serverSocket = new ServerSocket(this.config.getPort());
 | 
							this.serverSocket = new ServerSocket(this.config.getPort());
 | 
				
			||||||
 | 
							this.serializer = new Serializer();
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
| 
						 | 
					@ -164,13 +173,14 @@ public class ConcordServer implements Runnable {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Sends a message to every connected client.
 | 
						 * Sends a message to every connected client, ignoring any channels. All
 | 
				
			||||||
 | 
						 * clients connected to this server will receive this message.
 | 
				
			||||||
	 * @param message The message to send.
 | 
						 * @param message The message to send.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	public void broadcast(Message message) {
 | 
						public void broadcast(Message message) {
 | 
				
			||||||
		ByteArrayOutputStream baos = new ByteArrayOutputStream(message.getByteCount());
 | 
							ByteArrayOutputStream baos = new ByteArrayOutputStream(message.getByteCount());
 | 
				
			||||||
		try {
 | 
							try {
 | 
				
			||||||
			Serializer.writeMessage(message, baos);
 | 
								this.serializer.writeMessage(message, baos);
 | 
				
			||||||
			byte[] data = baos.toByteArray();
 | 
								byte[] data = baos.toByteArray();
 | 
				
			||||||
			for (var client : this.clients.values()) {
 | 
								for (var client : this.clients.values()) {
 | 
				
			||||||
				client.sendToClient(data);
 | 
									client.sendToClient(data);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue