AceOfShades/client/src/main/java/nl/andrewlalis/aos_client/MessageTransceiver.java

134 lines
4.4 KiB
Java

package nl.andrewlalis.aos_client;
import nl.andrewlalis.aos_core.net.*;
import nl.andrewlalis.aos_core.net.chat.ChatMessage;
import javax.swing.*;
import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This thread is responsible for handling TCP message communication with the
* server.
*/
public class MessageTransceiver extends Thread {
private final Client client;
private final Socket socket;
private final DataTransceiver dataTransceiver;
private final ObjectOutputStream out;
private final ObjectInputStream in;
private final ExecutorService writeService = Executors.newFixedThreadPool(1);
private volatile boolean running = true;
public MessageTransceiver(Client client, String serverHost, int serverPort, String username) throws IOException {
this.client = client;
this.socket = new Socket(serverHost, serverPort);
this.dataTransceiver = new DataTransceiver(client, this.socket.getInetAddress(), this.socket.getPort());
this.out = new ObjectOutputStream(this.socket.getOutputStream());
this.in = new ObjectInputStream(this.socket.getInputStream());
this.initializeConnection(username);
}
/**
* Initializes the TCP connection to the server. This involves sending an
* IDENT packet containing some data the server needs about this client, and
* waiting for a {@link PlayerRegisteredMessage} response from the server,
* which contains the basic data we need to start the game.
* @param username The username for this client.
* @throws IOException If the connection could not be initialized.
*/
private void initializeConnection(String username) throws IOException {
boolean established = false;
int attempts = 0;
while (!established && attempts < 100) {
this.send(new IdentMessage(username, this.dataTransceiver.getLocalPort()));
try {
Object obj = this.in.readObject();
if (obj instanceof PlayerRegisteredMessage msg) {
this.client.setPlayer(msg.getPlayer());
this.client.setWorld(msg.getWorld());
established = true;
} else if (obj instanceof ConnectionRejectedMessage msg) {
throw new IOException(msg.getMessage());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
attempts++;
}
if (!established) {
throw new IOException("Could not initialize connection to server in " + attempts + " attempts.");
}
}
public void shutdown() {
this.running = false;
this.dataTransceiver.shutdown();
this.writeService.shutdown();
try {
this.out.close();
this.in.close();
this.socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(Message message) {
if (this.socket.isClosed()) return;
this.writeService.submit(() -> {
try {
this.out.reset();
this.out.writeObject(message);
} catch (IOException e) {
e.printStackTrace();
}
});
}
public void sendData(byte type, int playerId, byte[] data) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1 + Integer.BYTES + data.length);
buffer.put(type);
buffer.putInt(playerId);
buffer.put(data);
this.dataTransceiver.send(buffer.array());
}
@Override
public void run() {
this.dataTransceiver.start();
while (this.running) {
try {
Message msg = (Message) this.in.readObject();
if (msg.getType() == Type.CHAT) {
this.client.getChatManager().addChatMessage((ChatMessage) msg);
} else if (msg.getType() == Type.PLAYER_JOINED && this.client.getWorld() != null) {
PlayerUpdateMessage pum = (PlayerUpdateMessage) msg;
this.client.getWorld().getPlayers().put(pum.getPlayer().getId(), pum.getPlayer());
} else if (msg.getType() == Type.PLAYER_LEFT && this.client.getWorld() != null) {
PlayerUpdateMessage pum = (PlayerUpdateMessage) msg;
this.client.getWorld().getPlayers().remove(pum.getPlayer().getId());
} else if (msg.getType() == Type.SERVER_SHUTDOWN) {
this.client.shutdown();
JOptionPane.showMessageDialog(null, "Server has been shut down.", "Server Shutdown", JOptionPane.WARNING_MESSAGE);
}
} catch (StreamCorruptedException | EOFException e) {
this.shutdown();
} catch (SocketException e) {
if (!e.getMessage().equalsIgnoreCase("Socket closed")) {
e.printStackTrace();
}
this.shutdown();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}