diff --git a/client/pom.xml b/client/pom.xml index 3d11303..7324c97 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 1.0 + 2.0 4.0.0 diff --git a/client/src/main/java/nl/andrewlalis/aos_client/Client.java b/client/src/main/java/nl/andrewlalis/aos_client/Client.java index 10837a4..62b3993 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/Client.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/Client.java @@ -2,18 +2,17 @@ package nl.andrewlalis.aos_client; import nl.andrewlalis.aos_client.view.GameFrame; import nl.andrewlalis.aos_client.view.GamePanel; +import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.model.PlayerControlState; import nl.andrewlalis.aos_core.model.World; -import nl.andrewlalis.aos_core.net.ChatMessage; +import nl.andrewlalis.aos_core.net.PlayerControlStateMessage; +import nl.andrewlalis.aos_core.net.chat.ChatMessage; +import nl.andrewlalis.aos_core.net.chat.PlayerChatMessage; import javax.swing.*; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.ObjectOutputStream; -import java.net.DatagramPacket; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; /** * The main class for the client, which connects to a server to join and play. @@ -21,15 +20,13 @@ import java.util.concurrent.ThreadLocalRandom; public class Client { public static final int MAX_CHAT_MESSAGES = 10; - private final int udpPort; - private DatagramReceiver datagramReceiver; private MessageTransceiver messageTransceiver; private int playerId; private PlayerControlState playerControlState; private World world; - private final List chatMessages; + private final List chatMessages; private boolean chatting = false; private final StringBuilder chatBuffer; @@ -37,8 +34,7 @@ public class Client { private final GamePanel gamePanel; private final SoundManager soundManager; - public Client(int udpPort) { - this.udpPort = udpPort; + public Client() { this.chatMessages = new LinkedList<>(); this.chatBuffer = new StringBuilder(); this.soundManager = new SoundManager(); @@ -47,13 +43,11 @@ public class Client { } public void connect(String serverHost, int serverPort, String username) throws IOException, ClassNotFoundException { - this.datagramReceiver = new DatagramReceiver(this, this.udpPort); - this.datagramReceiver.start(); this.messageTransceiver = new MessageTransceiver(this); - this.messageTransceiver.connectToServer(serverHost, serverPort, username, this.udpPort); + this.messageTransceiver.connectToServer(serverHost, serverPort, username); this.messageTransceiver.start(); - while (this.playerControlState == null) { + while (this.playerControlState == null || this.world == null) { try { System.out.println("Waiting for server response and player registration..."); Thread.sleep(100); @@ -62,6 +56,7 @@ public class Client { } } + System.out.println("Player and world data initialized."); GameFrame g = new GameFrame("Ace of Shades - " + serverHost + ":" + serverPort, this, this.gamePanel); g.setVisible(true); this.renderer.start(); @@ -94,27 +89,24 @@ public class Client { public void sendPlayerState() { try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(this.playerControlState); - byte[] buffer = bos.toByteArray(); - DatagramPacket packet = new DatagramPacket(buffer, buffer.length, this.messageTransceiver.getRemoteAddress(), this.messageTransceiver.getPort()); - this.datagramReceiver.getDatagramSocket().send(packet); + this.messageTransceiver.send(new PlayerControlStateMessage(this.playerControlState)); } catch (IOException e) { e.printStackTrace(); } } - public synchronized void addChatMessage(String text) { - this.chatMessages.add(text); - this.soundManager.play("chat.wav"); + public synchronized void addChatMessage(ChatMessage message) { + this.chatMessages.add(message); + if (message.getClass() == PlayerChatMessage.class) { + this.soundManager.play("chat.wav"); + } while (this.chatMessages.size() > MAX_CHAT_MESSAGES) { this.chatMessages.remove(0); } } - public String[] getLatestChatMessages() { - return this.chatMessages.toArray(new String[0]); + public ChatMessage[] getLatestChatMessages() { + return this.chatMessages.toArray(new ChatMessage[0]); } public boolean isChatting() { @@ -140,7 +132,7 @@ public class Client { public void sendChat() { try { - this.messageTransceiver.send(new ChatMessage(this.chatBuffer.toString())); + this.messageTransceiver.send(new PlayerChatMessage(this.playerId, this.chatBuffer.toString())); } catch (IOException e) { e.printStackTrace(); } @@ -152,7 +144,6 @@ public class Client { } public void shutdown() { - this.datagramReceiver.shutdown(); this.messageTransceiver.shutdown(); this.renderer.shutdown(); } @@ -160,21 +151,18 @@ public class Client { public static void main(String[] args) { - // Randomly choose a high-level UDP port that's probably open. - int udpPort = 20000 + ThreadLocalRandom.current().nextInt(0, 10000); +// String hostAndPort = JOptionPane.showInputDialog("Enter server host and port (host:port):"); +// if (hostAndPort == null) throw new IllegalArgumentException("A host and port is required."); +// String[] parts = hostAndPort.split(":"); +// if (parts.length != 2) throw new IllegalArgumentException("Invalid host:port."); +// String host = parts[0].trim(); +// int port = Integer.parseInt(parts[1]); +// String username = JOptionPane.showInputDialog("Enter a username:"); +// if (username == null || username.isBlank()) throw new IllegalArgumentException("Username is required."); - String hostAndPort = JOptionPane.showInputDialog("Enter server host and port (host:port):"); - if (hostAndPort == null) throw new IllegalArgumentException("A host and port is required."); - String[] parts = hostAndPort.split(":"); - if (parts.length != 2) throw new IllegalArgumentException("Invalid host:port."); - String host = parts[0].trim(); - int port = Integer.parseInt(parts[1]); - String username = JOptionPane.showInputDialog("Enter a username:"); - if (username == null || username.isBlank()) throw new IllegalArgumentException("Username is required."); - - Client client = new Client(udpPort); + Client client = new Client(); try { - client.connect(host, port, username); + client.connect("localhost", 8035, "andrew"); } catch (IOException | ClassNotFoundException e) { client.shutdown(); e.printStackTrace(); diff --git a/client/src/main/java/nl/andrewlalis/aos_client/DatagramReceiver.java b/client/src/main/java/nl/andrewlalis/aos_client/DatagramReceiver.java deleted file mode 100644 index 3b1bc62..0000000 --- a/client/src/main/java/nl/andrewlalis/aos_client/DatagramReceiver.java +++ /dev/null @@ -1,50 +0,0 @@ -package nl.andrewlalis.aos_client; - -import nl.andrewlalis.aos_core.model.World; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketException; - -public class DatagramReceiver extends Thread { - private final DatagramSocket datagramSocket; - private final Client client; - - private volatile boolean running; - - public DatagramReceiver(Client client, int port) throws SocketException { - this.datagramSocket = new DatagramSocket(port); - this.client = client; - } - - public DatagramSocket getDatagramSocket() { - return datagramSocket; - } - - public void shutdown() { - this.running = false; - this.datagramSocket.close(); - } - - @Override - public void run() { - this.running = true; - byte[] buffer = new byte[8192]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - while (this.running) { - try { - this.datagramSocket.receive(packet); - ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(packet.getData())); - Object obj = ois.readObject(); - if (obj instanceof World) { - this.client.setWorld((World) obj); - } - } catch (IOException | ClassNotFoundException e) { - // Ignore any receive exception.d - } - } - } -} diff --git a/client/src/main/java/nl/andrewlalis/aos_client/MessageTransceiver.java b/client/src/main/java/nl/andrewlalis/aos_client/MessageTransceiver.java index e8839ed..d4ac63a 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/MessageTransceiver.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/MessageTransceiver.java @@ -1,12 +1,15 @@ package nl.andrewlalis.aos_client; +import nl.andrewlalis.aos_core.model.World; import nl.andrewlalis.aos_core.net.*; +import nl.andrewlalis.aos_core.net.chat.ChatMessage; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.net.InetAddress; +import java.io.StreamCorruptedException; import java.net.Socket; +import java.net.SocketException; /** * This thread is responsible for handling TCP message communication with the @@ -25,11 +28,12 @@ public class MessageTransceiver extends Thread { this.client = client; } - public void connectToServer(String serverHost, int serverPort, String username, int udpPort) throws IOException, ClassNotFoundException { + public void connectToServer(String serverHost, int serverPort, String username) throws IOException { this.socket = new Socket(serverHost, serverPort); this.out = new ObjectOutputStream(this.socket.getOutputStream()); this.in = new ObjectInputStream(this.socket.getInputStream()); - this.send(new IdentMessage(username, udpPort)); + this.send(new IdentMessage(username)); + System.out.println("Sent identification packet."); } public void shutdown() { @@ -43,15 +47,8 @@ public class MessageTransceiver extends Thread { } } - public InetAddress getRemoteAddress() { - return this.socket != null ? this.socket.getInetAddress() : null; - } - - public int getPort() { - return this.socket.getPort(); - } - - public void send(Message message) throws IOException { + public synchronized void send(Message message) throws IOException { + this.out.reset(); this.out.writeObject(message); } @@ -64,10 +61,20 @@ public class MessageTransceiver extends Thread { PlayerRegisteredMessage prm = (PlayerRegisteredMessage) msg; this.client.initPlayerData(prm.getPlayerId()); } else if (msg.getType() == Type.CHAT) { - this.client.addChatMessage(((ChatMessage) msg).getText()); + this.client.addChatMessage((ChatMessage) msg); + } else if (msg.getType() == Type.WORLD_UPDATE) { + World world = ((WorldUpdateMessage) msg).getWorld(); + this.client.setWorld(world); + } + } catch (StreamCorruptedException e) { + e.printStackTrace(); + this.running = false; + } catch (SocketException e) { + if (!e.getMessage().equalsIgnoreCase("Socket closed")) { + e.printStackTrace(); } } catch (IOException | ClassNotFoundException e) { - // Ignore exceptions. + e.printStackTrace(); } } } diff --git a/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java b/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java index 5f073d6..3c4b313 100644 --- a/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java +++ b/client/src/main/java/nl/andrewlalis/aos_client/view/GamePanel.java @@ -2,6 +2,9 @@ package nl.andrewlalis.aos_client.view; import nl.andrewlalis.aos_client.Client; import nl.andrewlalis.aos_core.model.*; +import nl.andrewlalis.aos_core.net.chat.ChatMessage; +import nl.andrewlalis.aos_core.net.chat.PlayerChatMessage; +import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import javax.swing.*; import java.awt.*; @@ -44,7 +47,7 @@ public class GamePanel extends JPanel { World world = client.getWorld(); if (world != null) drawWorld(g2, world); - drawChat(g2, client.getLatestChatMessages()); + drawChat(g2, world); } private void drawWorld(Graphics2D g2, World world) { @@ -142,16 +145,35 @@ public class GamePanel extends JPanel { } } - private void drawChat(Graphics2D g2, String[] messages) { + private void drawChat(Graphics2D g2, World world) { int height = g2.getFontMetrics().getHeight(); int y = height; - g2.setColor(Color.WHITE); - for (String message : messages) { - g2.drawString(message, 5, y); + for (ChatMessage message : this.client.getLatestChatMessages()) { + Color color = Color.WHITE; + String text = message.getText(); + if (message instanceof SystemChatMessage sysMsg) { + if (sysMsg.getLevel() == SystemChatMessage.Level.INFO) { + color = Color.YELLOW; + } else if (sysMsg.getLevel() == SystemChatMessage.Level.WARNING) { + color = Color.ORANGE; + } else if (sysMsg.getLevel() == SystemChatMessage.Level.SEVERE) { + color = Color.RED; + } + } else if (message instanceof PlayerChatMessage pcm) { + String author = Integer.toString(pcm.getPlayerId()); + if (world != null) { + Player p = world.getPlayers().get(pcm.getPlayerId()); + if (p != null) author = p.getName(); + } + text = author + ": " + text; + } + g2.setColor(color); + g2.drawString(text, 5, y); y += height; } if (this.client.isChatting()) { + g2.setColor(Color.WHITE); g2.drawString("> " + this.client.getCurrentChatBuffer(), 5, height * 11); } } diff --git a/core/pom.xml b/core/pom.xml index 821b737..a6e3e74 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 1.0 + 2.0 4.0.0 diff --git a/core/src/main/java/module-info.java b/core/src/main/java/module-info.java index 3ce9b66..84ddf00 100644 --- a/core/src/main/java/module-info.java +++ b/core/src/main/java/module-info.java @@ -3,4 +3,5 @@ module aos_core { exports nl.andrewlalis.aos_core.net to aos_server, aos_client; exports nl.andrewlalis.aos_core.model to aos_server, aos_client; exports nl.andrewlalis.aos_core.geom to aos_server, aos_client; + exports nl.andrewlalis.aos_core.net.chat to aos_client, aos_server; } \ No newline at end of file diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/IdentMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/IdentMessage.java index a603834..647c865 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/net/IdentMessage.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/IdentMessage.java @@ -2,19 +2,13 @@ package nl.andrewlalis.aos_core.net; public class IdentMessage extends Message { private final String name; - private final int datagramPort; - public IdentMessage(String name, int datagramPort) { + public IdentMessage(String name) { super(Type.IDENT); this.name = name; - this.datagramPort = datagramPort; } public String getName() { return name; } - - public int getDatagramPort() { - return datagramPort; - } } diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/PlayerControlStateMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/PlayerControlStateMessage.java new file mode 100644 index 0000000..10f3a52 --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/PlayerControlStateMessage.java @@ -0,0 +1,16 @@ +package nl.andrewlalis.aos_core.net; + +import nl.andrewlalis.aos_core.model.PlayerControlState; + +public class PlayerControlStateMessage extends Message { + private final PlayerControlState playerControlState; + + public PlayerControlStateMessage(PlayerControlState pcs) { + super(Type.PLAYER_CONTROL_STATE); + this.playerControlState = pcs; + } + + public PlayerControlState getPlayerControlState() { + return playerControlState; + } +} diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/Type.java b/core/src/main/java/nl/andrewlalis/aos_core/net/Type.java index e226d1e..a298e47 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/net/Type.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/Type.java @@ -4,5 +4,7 @@ public enum Type { IDENT, ACK, PLAYER_REGISTERED, - CHAT + CHAT, + PLAYER_CONTROL_STATE, + WORLD_UPDATE } diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/WorldUpdateMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/WorldUpdateMessage.java new file mode 100644 index 0000000..6c29931 --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/WorldUpdateMessage.java @@ -0,0 +1,15 @@ +package nl.andrewlalis.aos_core.net; + +import nl.andrewlalis.aos_core.model.World; + +public class WorldUpdateMessage extends Message { + private final World world; + public WorldUpdateMessage(World world) { + super(Type.WORLD_UPDATE); + this.world = world; + } + + public World getWorld() { + return world; + } +} diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/ChatMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/ChatMessage.java similarity index 60% rename from core/src/main/java/nl/andrewlalis/aos_core/net/ChatMessage.java rename to core/src/main/java/nl/andrewlalis/aos_core/net/chat/ChatMessage.java index 3c3f333..8fc740b 100644 --- a/core/src/main/java/nl/andrewlalis/aos_core/net/ChatMessage.java +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/ChatMessage.java @@ -1,4 +1,7 @@ -package nl.andrewlalis.aos_core.net; +package nl.andrewlalis.aos_core.net.chat; + +import nl.andrewlalis.aos_core.net.Message; +import nl.andrewlalis.aos_core.net.Type; public class ChatMessage extends Message { private final String text; diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/chat/CommandMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/CommandMessage.java new file mode 100644 index 0000000..7a4758a --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/CommandMessage.java @@ -0,0 +1,7 @@ +package nl.andrewlalis.aos_core.net.chat; + +public class CommandMessage extends PlayerChatMessage { + public CommandMessage(int id, String text) { + super(id, text); + } +} diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/chat/PlayerChatMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/PlayerChatMessage.java new file mode 100644 index 0000000..67acafe --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/PlayerChatMessage.java @@ -0,0 +1,14 @@ +package nl.andrewlalis.aos_core.net.chat; + +public class PlayerChatMessage extends ChatMessage { + private final int playerId; + + public PlayerChatMessage(int id, String text) { + super(text); + this.playerId = id; + } + + public int getPlayerId() { + return playerId; + } +} diff --git a/core/src/main/java/nl/andrewlalis/aos_core/net/chat/SystemChatMessage.java b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/SystemChatMessage.java new file mode 100644 index 0000000..4e4ece8 --- /dev/null +++ b/core/src/main/java/nl/andrewlalis/aos_core/net/chat/SystemChatMessage.java @@ -0,0 +1,16 @@ +package nl.andrewlalis.aos_core.net.chat; + +public class SystemChatMessage extends ChatMessage { + public enum Level {INFO, WARNING, SEVERE} + + private final Level level; + + public SystemChatMessage(Level level, String text) { + super(text); + this.level = level; + } + + public Level getLevel() { + return level; + } +} diff --git a/pom.xml b/pom.xml index 591d6fc..97cfe50 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ nl.andrewlalis ace-of-shades pom - 1.0 + 2.0 server client diff --git a/server/pom.xml b/server/pom.xml index 9a53385..c61a47e 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -5,7 +5,7 @@ ace-of-shades nl.andrewlalis - 1.0 + 2.0 4.0.0 diff --git a/server/src/main/java/nl/andrewlalis/aos_server/ClientHandler.java b/server/src/main/java/nl/andrewlalis/aos_server/ClientHandler.java index 8fbff1d..3b6228a 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/ClientHandler.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/ClientHandler.java @@ -1,6 +1,7 @@ package nl.andrewlalis.aos_server; import nl.andrewlalis.aos_core.net.*; +import nl.andrewlalis.aos_core.net.chat.ChatMessage; import java.io.IOException; import java.io.ObjectInputStream; @@ -13,7 +14,6 @@ public class ClientHandler extends Thread { private final ObjectOutputStream out; private final ObjectInputStream in; - private int datagramPort = -1; private int playerId; private volatile boolean running = true; @@ -25,14 +25,6 @@ public class ClientHandler extends Thread { this.in = new ObjectInputStream(socket.getInputStream()); } - public Socket getSocket() { - return socket; - } - - public int getDatagramPort() { - return datagramPort; - } - public int getPlayerId() { return playerId; } @@ -41,7 +33,8 @@ public class ClientHandler extends Thread { this.running = false; } - public void send(Message message) throws IOException { + public synchronized void send(Message message) throws IOException { + this.out.reset(); this.out.writeObject(message); } @@ -53,12 +46,11 @@ public class ClientHandler extends Thread { Message msg = (Message) this.in.readObject(); if (msg.getType() == Type.IDENT) { IdentMessage ident = (IdentMessage) msg; - int id = this.server.registerNewPlayer(ident.getName()); - this.playerId = id; - this.datagramPort = ident.getDatagramPort(); - this.send(new PlayerRegisteredMessage(id)); + this.playerId = this.server.registerNewPlayer(ident.getName(), this); } else if (msg.getType() == Type.CHAT) { this.server.broadcastPlayerChat(this.playerId, (ChatMessage) msg); + } else if (msg.getType() == Type.PLAYER_CONTROL_STATE) { + this.server.updatePlayerState(((PlayerControlStateMessage) msg).getPlayerControlState()); } } catch (ClassNotFoundException e) { e.printStackTrace(); @@ -67,7 +59,6 @@ public class ClientHandler extends Thread { } catch (IOException e) { // Ignore this exception, consider the client disconnected. } - this.datagramPort = -1; try { this.socket.close(); } catch (IOException e) { diff --git a/server/src/main/java/nl/andrewlalis/aos_server/DatagramCommunicationThread.java b/server/src/main/java/nl/andrewlalis/aos_server/DatagramCommunicationThread.java deleted file mode 100644 index bb7dd09..0000000 --- a/server/src/main/java/nl/andrewlalis/aos_server/DatagramCommunicationThread.java +++ /dev/null @@ -1,41 +0,0 @@ -package nl.andrewlalis.aos_server; - -import nl.andrewlalis.aos_core.model.PlayerControlState; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketException; - -public class DatagramCommunicationThread extends Thread { - private final Server server; - private final DatagramSocket socket; - - public DatagramCommunicationThread(Server server, int port) throws SocketException { - this.server = server; - this.socket = new DatagramSocket(port); - } - - public DatagramSocket getSocket() { - return socket; - } - - @Override - public void run() { - byte[] buffer = new byte[8192]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - while (true) { - try { - this.socket.receive(packet); - Object obj = new ObjectInputStream(new ByteArrayInputStream(buffer)).readObject(); - if (obj instanceof PlayerControlState) { - this.server.updatePlayerState((PlayerControlState) obj); - } - } catch (IOException | ClassNotFoundException e) { - e.printStackTrace(); - } - } - } -} diff --git a/server/src/main/java/nl/andrewlalis/aos_server/Server.java b/server/src/main/java/nl/andrewlalis/aos_server/Server.java index 2cab056..ed58659 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/Server.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/Server.java @@ -2,19 +2,20 @@ package nl.andrewlalis.aos_server; import nl.andrewlalis.aos_core.geom.Vec2; import nl.andrewlalis.aos_core.model.*; -import nl.andrewlalis.aos_core.net.ChatMessage; import nl.andrewlalis.aos_core.net.Message; +import nl.andrewlalis.aos_core.net.PlayerRegisteredMessage; +import nl.andrewlalis.aos_core.net.WorldUpdateMessage; +import nl.andrewlalis.aos_core.net.chat.ChatMessage; +import nl.andrewlalis.aos_core.net.chat.PlayerChatMessage; +import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import java.awt.*; -import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.ObjectOutputStream; -import java.net.DatagramPacket; import java.net.ServerSocket; import java.net.Socket; -import java.util.ArrayList; import java.util.List; import java.util.Scanner; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ThreadLocalRandom; public class Server { @@ -22,14 +23,12 @@ public class Server { private final List clientHandlers; private final ServerSocket serverSocket; - private final DatagramCommunicationThread datagramCommunicationThread; private final World world; private final WorldUpdater worldUpdater; public Server(int port) throws IOException { - this.clientHandlers = new ArrayList<>(); + this.clientHandlers = new CopyOnWriteArrayList<>(); this.serverSocket = new ServerSocket(port); - this.datagramCommunicationThread = new DatagramCommunicationThread(this, port); this.world = new World(new Vec2(50, 70)); world.getBarricades().add(new Barricade(10, 10, 30, 5)); @@ -42,19 +41,17 @@ public class Server { world.getTeams().add(new Team("Blue", Color.BLUE, new Vec2(world.getSize().x() - 3, world.getSize().y() - 3), new Vec2(0, -1))); this.worldUpdater = new WorldUpdater(this, this.world); - System.out.println("Started AOS-Server TCP/UDP on port " + port); + System.out.println("Started AOS-Server TCP on port " + port); } public void acceptClientConnection() throws IOException { Socket socket = this.serverSocket.accept(); var t = new ClientHandler(this, socket); t.start(); - synchronized (this.clientHandlers) { - this.clientHandlers.add(t); - } + this.clientHandlers.add(t); } - public int registerNewPlayer(String name) { + public int registerNewPlayer(String name, ClientHandler handler) { int id = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE); Team team = null; for (Team t : this.world.getTeams()) { @@ -65,48 +62,46 @@ public class Server { } } Player p = new Player(id, name, team); - System.out.println("Client connected: " + p.getId() + ", " + p.getName()); - this.broadcastMessage(new ChatMessage(name + " connected.")); this.world.getPlayers().put(p.getId(), p); + try { + handler.send(new PlayerRegisteredMessage(id)); + } catch (IOException e) { + e.printStackTrace(); + } + String message = p.getName() + " connected."; + this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message)); + System.out.println(message); p.setPosition(new Vec2(this.world.getSize().x() / 2.0, this.world.getSize().y() / 2.0)); if (team != null) { team.getPlayers().add(p); p.setPosition(team.getSpawnPoint()); p.setOrientation(team.getOrientation()); - this.broadcastMessage(new ChatMessage(name + " joined team " + team.getName())); - System.out.println("Player joined team " + team.getName()); + message = name + " joined team " + team.getName() + "."; + this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message)); } return id; } public void clientDisconnected(ClientHandler clientHandler) { Player player = this.world.getPlayers().get(clientHandler.getPlayerId()); - synchronized (this.clientHandlers) { - this.clientHandlers.remove(clientHandler); - clientHandler.shutdown(); - } + this.clientHandlers.remove(clientHandler); + clientHandler.shutdown(); this.world.getPlayers().remove(player.getId()); if (player.getTeam() != null) { player.getTeam().getPlayers().remove(player); } - this.broadcastMessage(new ChatMessage(player.getName() + " disconnected.")); - System.out.println("Client disconnected: " + player.getId() + ", " + player.getName()); + String message = player.getName() + " disconnected."; + this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message)); + System.out.println(message); } public void sendWorldToClients() { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - new ObjectOutputStream(bos).writeObject(this.world); - byte[] data = bos.toByteArray(); - DatagramPacket packet = new DatagramPacket(data, data.length); - for (ClientHandler handler : this.clientHandlers) { - if (handler.getDatagramPort() == -1) continue; - packet.setAddress(handler.getSocket().getInetAddress()); - packet.setPort(handler.getDatagramPort()); - this.datagramCommunicationThread.getSocket().send(packet); + for (ClientHandler handler : this.clientHandlers) { + try { + handler.send(new WorldUpdateMessage(this.world)); + } catch (IOException e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } @@ -130,7 +125,7 @@ public class Server { public void broadcastPlayerChat(int playerId, ChatMessage msg) { Player p = this.world.getPlayers().get(playerId); if (p == null) return; - this.broadcastMessage(new ChatMessage(p.getName() + ": " + msg.getText())); + this.broadcastMessage(new PlayerChatMessage(p.getId(), msg.getText())); } @@ -150,12 +145,9 @@ public class Server { } Server server = new Server(port); - server.datagramCommunicationThread.start(); server.worldUpdater.start(); while (true) { server.acceptClientConnection(); } } - - } diff --git a/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java b/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java index 18868d7..02e9c48 100644 --- a/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java +++ b/server/src/main/java/nl/andrewlalis/aos_server/WorldUpdater.java @@ -5,7 +5,7 @@ import nl.andrewlalis.aos_core.model.Barricade; import nl.andrewlalis.aos_core.model.Bullet; import nl.andrewlalis.aos_core.model.Player; import nl.andrewlalis.aos_core.model.World; -import nl.andrewlalis.aos_core.net.ChatMessage; +import nl.andrewlalis.aos_core.net.chat.SystemChatMessage; import java.util.ArrayList; import java.util.List; @@ -155,7 +155,7 @@ public class WorldUpdater extends Thread { double dist = p.getPosition().dist(new Vec2(x1 + n * (x2 - x1), y1 + n * (y2 - y1))); if (dist < Player.RADIUS) { Player killer = this.world.getPlayers().get(b.getPlayerId()); - this.server.broadcastMessage(new ChatMessage(p.getName() + " was shot by " + killer.getName() + ".")); + this.server.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.SEVERE, p.getName() + " was shot by " + killer.getName() + ".")); world.getSoundsToPlay().add("death.wav"); if (p.getTeam() != null) { p.setPosition(p.getTeam().getSpawnPoint());