Removed UDP stuff, upgrade to 2.0, chat improvements too.
This commit is contained in:
parent
7ed19f3576
commit
95ba8c8746
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>ace-of-shades</artifactId>
|
<artifactId>ace-of-shades</artifactId>
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewlalis</groupId>
|
||||||
<version>1.0</version>
|
<version>2.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
|
@ -2,18 +2,17 @@ package nl.andrewlalis.aos_client;
|
||||||
|
|
||||||
import nl.andrewlalis.aos_client.view.GameFrame;
|
import nl.andrewlalis.aos_client.view.GameFrame;
|
||||||
import nl.andrewlalis.aos_client.view.GamePanel;
|
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.PlayerControlState;
|
||||||
import nl.andrewlalis.aos_core.model.World;
|
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 javax.swing.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main class for the client, which connects to a server to join and play.
|
* 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 class Client {
|
||||||
public static final int MAX_CHAT_MESSAGES = 10;
|
public static final int MAX_CHAT_MESSAGES = 10;
|
||||||
|
|
||||||
private final int udpPort;
|
|
||||||
private DatagramReceiver datagramReceiver;
|
|
||||||
private MessageTransceiver messageTransceiver;
|
private MessageTransceiver messageTransceiver;
|
||||||
|
|
||||||
private int playerId;
|
private int playerId;
|
||||||
private PlayerControlState playerControlState;
|
private PlayerControlState playerControlState;
|
||||||
private World world;
|
private World world;
|
||||||
|
|
||||||
private final List<String> chatMessages;
|
private final List<ChatMessage> chatMessages;
|
||||||
private boolean chatting = false;
|
private boolean chatting = false;
|
||||||
private final StringBuilder chatBuffer;
|
private final StringBuilder chatBuffer;
|
||||||
|
|
||||||
|
@ -37,8 +34,7 @@ public class Client {
|
||||||
private final GamePanel gamePanel;
|
private final GamePanel gamePanel;
|
||||||
private final SoundManager soundManager;
|
private final SoundManager soundManager;
|
||||||
|
|
||||||
public Client(int udpPort) {
|
public Client() {
|
||||||
this.udpPort = udpPort;
|
|
||||||
this.chatMessages = new LinkedList<>();
|
this.chatMessages = new LinkedList<>();
|
||||||
this.chatBuffer = new StringBuilder();
|
this.chatBuffer = new StringBuilder();
|
||||||
this.soundManager = new SoundManager();
|
this.soundManager = new SoundManager();
|
||||||
|
@ -47,13 +43,11 @@ public class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void connect(String serverHost, int serverPort, String username) throws IOException, ClassNotFoundException {
|
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 = new MessageTransceiver(this);
|
||||||
this.messageTransceiver.connectToServer(serverHost, serverPort, username, this.udpPort);
|
this.messageTransceiver.connectToServer(serverHost, serverPort, username);
|
||||||
this.messageTransceiver.start();
|
this.messageTransceiver.start();
|
||||||
|
|
||||||
while (this.playerControlState == null) {
|
while (this.playerControlState == null || this.world == null) {
|
||||||
try {
|
try {
|
||||||
System.out.println("Waiting for server response and player registration...");
|
System.out.println("Waiting for server response and player registration...");
|
||||||
Thread.sleep(100);
|
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);
|
GameFrame g = new GameFrame("Ace of Shades - " + serverHost + ":" + serverPort, this, this.gamePanel);
|
||||||
g.setVisible(true);
|
g.setVisible(true);
|
||||||
this.renderer.start();
|
this.renderer.start();
|
||||||
|
@ -94,27 +89,24 @@ public class Client {
|
||||||
|
|
||||||
public void sendPlayerState() {
|
public void sendPlayerState() {
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
this.messageTransceiver.send(new PlayerControlStateMessage(this.playerControlState));
|
||||||
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);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void addChatMessage(String text) {
|
public synchronized void addChatMessage(ChatMessage message) {
|
||||||
this.chatMessages.add(text);
|
this.chatMessages.add(message);
|
||||||
this.soundManager.play("chat.wav");
|
if (message.getClass() == PlayerChatMessage.class) {
|
||||||
|
this.soundManager.play("chat.wav");
|
||||||
|
}
|
||||||
while (this.chatMessages.size() > MAX_CHAT_MESSAGES) {
|
while (this.chatMessages.size() > MAX_CHAT_MESSAGES) {
|
||||||
this.chatMessages.remove(0);
|
this.chatMessages.remove(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String[] getLatestChatMessages() {
|
public ChatMessage[] getLatestChatMessages() {
|
||||||
return this.chatMessages.toArray(new String[0]);
|
return this.chatMessages.toArray(new ChatMessage[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isChatting() {
|
public boolean isChatting() {
|
||||||
|
@ -140,7 +132,7 @@ public class Client {
|
||||||
|
|
||||||
public void sendChat() {
|
public void sendChat() {
|
||||||
try {
|
try {
|
||||||
this.messageTransceiver.send(new ChatMessage(this.chatBuffer.toString()));
|
this.messageTransceiver.send(new PlayerChatMessage(this.playerId, this.chatBuffer.toString()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -152,7 +144,6 @@ public class Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
this.datagramReceiver.shutdown();
|
|
||||||
this.messageTransceiver.shutdown();
|
this.messageTransceiver.shutdown();
|
||||||
this.renderer.shutdown();
|
this.renderer.shutdown();
|
||||||
}
|
}
|
||||||
|
@ -160,21 +151,18 @@ public class Client {
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// Randomly choose a high-level UDP port that's probably open.
|
// String hostAndPort = JOptionPane.showInputDialog("Enter server host and port (host:port):");
|
||||||
int udpPort = 20000 + ThreadLocalRandom.current().nextInt(0, 10000);
|
// 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):");
|
Client client = new Client();
|
||||||
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);
|
|
||||||
try {
|
try {
|
||||||
client.connect(host, port, username);
|
client.connect("localhost", 8035, "andrew");
|
||||||
} catch (IOException | ClassNotFoundException e) {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
client.shutdown();
|
client.shutdown();
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,12 +1,15 @@
|
||||||
package nl.andrewlalis.aos_client;
|
package nl.andrewlalis.aos_client;
|
||||||
|
|
||||||
|
import nl.andrewlalis.aos_core.model.World;
|
||||||
import nl.andrewlalis.aos_core.net.*;
|
import nl.andrewlalis.aos_core.net.*;
|
||||||
|
import nl.andrewlalis.aos_core.net.chat.ChatMessage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.ObjectOutputStream;
|
import java.io.ObjectOutputStream;
|
||||||
import java.net.InetAddress;
|
import java.io.StreamCorruptedException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This thread is responsible for handling TCP message communication with the
|
* This thread is responsible for handling TCP message communication with the
|
||||||
|
@ -25,11 +28,12 @@ public class MessageTransceiver extends Thread {
|
||||||
this.client = client;
|
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.socket = new Socket(serverHost, serverPort);
|
||||||
this.out = new ObjectOutputStream(this.socket.getOutputStream());
|
this.out = new ObjectOutputStream(this.socket.getOutputStream());
|
||||||
this.in = new ObjectInputStream(this.socket.getInputStream());
|
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() {
|
public void shutdown() {
|
||||||
|
@ -43,15 +47,8 @@ public class MessageTransceiver extends Thread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetAddress getRemoteAddress() {
|
public synchronized void send(Message message) throws IOException {
|
||||||
return this.socket != null ? this.socket.getInetAddress() : null;
|
this.out.reset();
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort() {
|
|
||||||
return this.socket.getPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void send(Message message) throws IOException {
|
|
||||||
this.out.writeObject(message);
|
this.out.writeObject(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,10 +61,20 @@ public class MessageTransceiver extends Thread {
|
||||||
PlayerRegisteredMessage prm = (PlayerRegisteredMessage) msg;
|
PlayerRegisteredMessage prm = (PlayerRegisteredMessage) msg;
|
||||||
this.client.initPlayerData(prm.getPlayerId());
|
this.client.initPlayerData(prm.getPlayerId());
|
||||||
} else if (msg.getType() == Type.CHAT) {
|
} 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) {
|
} catch (IOException | ClassNotFoundException e) {
|
||||||
// Ignore exceptions.
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@ package nl.andrewlalis.aos_client.view;
|
||||||
|
|
||||||
import nl.andrewlalis.aos_client.Client;
|
import nl.andrewlalis.aos_client.Client;
|
||||||
import nl.andrewlalis.aos_core.model.*;
|
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 javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
@ -44,7 +47,7 @@ public class GamePanel extends JPanel {
|
||||||
|
|
||||||
World world = client.getWorld();
|
World world = client.getWorld();
|
||||||
if (world != null) drawWorld(g2, world);
|
if (world != null) drawWorld(g2, world);
|
||||||
drawChat(g2, client.getLatestChatMessages());
|
drawChat(g2, world);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawWorld(Graphics2D g2, World 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 height = g2.getFontMetrics().getHeight();
|
||||||
int y = height;
|
int y = height;
|
||||||
g2.setColor(Color.WHITE);
|
for (ChatMessage message : this.client.getLatestChatMessages()) {
|
||||||
for (String message : messages) {
|
Color color = Color.WHITE;
|
||||||
g2.drawString(message, 5, y);
|
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;
|
y += height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.client.isChatting()) {
|
if (this.client.isChatting()) {
|
||||||
|
g2.setColor(Color.WHITE);
|
||||||
g2.drawString("> " + this.client.getCurrentChatBuffer(), 5, height * 11);
|
g2.drawString("> " + this.client.getCurrentChatBuffer(), 5, height * 11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>ace-of-shades</artifactId>
|
<artifactId>ace-of-shades</artifactId>
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewlalis</groupId>
|
||||||
<version>1.0</version>
|
<version>2.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,5 @@ module aos_core {
|
||||||
exports nl.andrewlalis.aos_core.net to aos_server, aos_client;
|
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.model to aos_server, aos_client;
|
||||||
exports nl.andrewlalis.aos_core.geom 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;
|
||||||
}
|
}
|
|
@ -2,19 +2,13 @@ package nl.andrewlalis.aos_core.net;
|
||||||
|
|
||||||
public class IdentMessage extends Message {
|
public class IdentMessage extends Message {
|
||||||
private final String name;
|
private final String name;
|
||||||
private final int datagramPort;
|
|
||||||
|
|
||||||
public IdentMessage(String name, int datagramPort) {
|
public IdentMessage(String name) {
|
||||||
super(Type.IDENT);
|
super(Type.IDENT);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.datagramPort = datagramPort;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getDatagramPort() {
|
|
||||||
return datagramPort;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,5 +4,7 @@ public enum Type {
|
||||||
IDENT,
|
IDENT,
|
||||||
ACK,
|
ACK,
|
||||||
PLAYER_REGISTERED,
|
PLAYER_REGISTERED,
|
||||||
CHAT
|
CHAT,
|
||||||
|
PLAYER_CONTROL_STATE,
|
||||||
|
WORLD_UPDATE
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
public class ChatMessage extends Message {
|
||||||
private final String text;
|
private final String text;
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
2
pom.xml
2
pom.xml
|
@ -7,7 +7,7 @@
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewlalis</groupId>
|
||||||
<artifactId>ace-of-shades</artifactId>
|
<artifactId>ace-of-shades</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<version>1.0</version>
|
<version>2.0</version>
|
||||||
<modules>
|
<modules>
|
||||||
<module>server</module>
|
<module>server</module>
|
||||||
<module>client</module>
|
<module>client</module>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>ace-of-shades</artifactId>
|
<artifactId>ace-of-shades</artifactId>
|
||||||
<groupId>nl.andrewlalis</groupId>
|
<groupId>nl.andrewlalis</groupId>
|
||||||
<version>1.0</version>
|
<version>2.0</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package nl.andrewlalis.aos_server;
|
package nl.andrewlalis.aos_server;
|
||||||
|
|
||||||
import nl.andrewlalis.aos_core.net.*;
|
import nl.andrewlalis.aos_core.net.*;
|
||||||
|
import nl.andrewlalis.aos_core.net.chat.ChatMessage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
|
@ -13,7 +14,6 @@ public class ClientHandler extends Thread {
|
||||||
private final ObjectOutputStream out;
|
private final ObjectOutputStream out;
|
||||||
private final ObjectInputStream in;
|
private final ObjectInputStream in;
|
||||||
|
|
||||||
private int datagramPort = -1;
|
|
||||||
private int playerId;
|
private int playerId;
|
||||||
|
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
|
@ -25,14 +25,6 @@ public class ClientHandler extends Thread {
|
||||||
this.in = new ObjectInputStream(socket.getInputStream());
|
this.in = new ObjectInputStream(socket.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Socket getSocket() {
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDatagramPort() {
|
|
||||||
return datagramPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPlayerId() {
|
public int getPlayerId() {
|
||||||
return playerId;
|
return playerId;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +33,8 @@ public class ClientHandler extends Thread {
|
||||||
this.running = false;
|
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);
|
this.out.writeObject(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,12 +46,11 @@ public class ClientHandler extends Thread {
|
||||||
Message msg = (Message) this.in.readObject();
|
Message msg = (Message) this.in.readObject();
|
||||||
if (msg.getType() == Type.IDENT) {
|
if (msg.getType() == Type.IDENT) {
|
||||||
IdentMessage ident = (IdentMessage) msg;
|
IdentMessage ident = (IdentMessage) msg;
|
||||||
int id = this.server.registerNewPlayer(ident.getName());
|
this.playerId = this.server.registerNewPlayer(ident.getName(), this);
|
||||||
this.playerId = id;
|
|
||||||
this.datagramPort = ident.getDatagramPort();
|
|
||||||
this.send(new PlayerRegisteredMessage(id));
|
|
||||||
} else if (msg.getType() == Type.CHAT) {
|
} else if (msg.getType() == Type.CHAT) {
|
||||||
this.server.broadcastPlayerChat(this.playerId, (ChatMessage) msg);
|
this.server.broadcastPlayerChat(this.playerId, (ChatMessage) msg);
|
||||||
|
} else if (msg.getType() == Type.PLAYER_CONTROL_STATE) {
|
||||||
|
this.server.updatePlayerState(((PlayerControlStateMessage) msg).getPlayerControlState());
|
||||||
}
|
}
|
||||||
} catch (ClassNotFoundException e) {
|
} catch (ClassNotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -67,7 +59,6 @@ public class ClientHandler extends Thread {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Ignore this exception, consider the client disconnected.
|
// Ignore this exception, consider the client disconnected.
|
||||||
}
|
}
|
||||||
this.datagramPort = -1;
|
|
||||||
try {
|
try {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,19 +2,20 @@ package nl.andrewlalis.aos_server;
|
||||||
|
|
||||||
import nl.andrewlalis.aos_core.geom.Vec2;
|
import nl.andrewlalis.aos_core.geom.Vec2;
|
||||||
import nl.andrewlalis.aos_core.model.*;
|
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.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.awt.*;
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.ThreadLocalRandom;
|
import java.util.concurrent.ThreadLocalRandom;
|
||||||
|
|
||||||
public class Server {
|
public class Server {
|
||||||
|
@ -22,14 +23,12 @@ public class Server {
|
||||||
|
|
||||||
private final List<ClientHandler> clientHandlers;
|
private final List<ClientHandler> clientHandlers;
|
||||||
private final ServerSocket serverSocket;
|
private final ServerSocket serverSocket;
|
||||||
private final DatagramCommunicationThread datagramCommunicationThread;
|
|
||||||
private final World world;
|
private final World world;
|
||||||
private final WorldUpdater worldUpdater;
|
private final WorldUpdater worldUpdater;
|
||||||
|
|
||||||
public Server(int port) throws IOException {
|
public Server(int port) throws IOException {
|
||||||
this.clientHandlers = new ArrayList<>();
|
this.clientHandlers = new CopyOnWriteArrayList<>();
|
||||||
this.serverSocket = new ServerSocket(port);
|
this.serverSocket = new ServerSocket(port);
|
||||||
this.datagramCommunicationThread = new DatagramCommunicationThread(this, port);
|
|
||||||
|
|
||||||
this.world = new World(new Vec2(50, 70));
|
this.world = new World(new Vec2(50, 70));
|
||||||
world.getBarricades().add(new Barricade(10, 10, 30, 5));
|
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)));
|
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);
|
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 {
|
public void acceptClientConnection() throws IOException {
|
||||||
Socket socket = this.serverSocket.accept();
|
Socket socket = this.serverSocket.accept();
|
||||||
var t = new ClientHandler(this, socket);
|
var t = new ClientHandler(this, socket);
|
||||||
t.start();
|
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);
|
int id = ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE);
|
||||||
Team team = null;
|
Team team = null;
|
||||||
for (Team t : this.world.getTeams()) {
|
for (Team t : this.world.getTeams()) {
|
||||||
|
@ -65,48 +62,46 @@ public class Server {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Player p = new Player(id, name, team);
|
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);
|
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));
|
p.setPosition(new Vec2(this.world.getSize().x() / 2.0, this.world.getSize().y() / 2.0));
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
team.getPlayers().add(p);
|
team.getPlayers().add(p);
|
||||||
p.setPosition(team.getSpawnPoint());
|
p.setPosition(team.getSpawnPoint());
|
||||||
p.setOrientation(team.getOrientation());
|
p.setOrientation(team.getOrientation());
|
||||||
this.broadcastMessage(new ChatMessage(name + " joined team " + team.getName()));
|
message = name + " joined team " + team.getName() + ".";
|
||||||
System.out.println("Player joined team " + team.getName());
|
this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message));
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clientDisconnected(ClientHandler clientHandler) {
|
public void clientDisconnected(ClientHandler clientHandler) {
|
||||||
Player player = this.world.getPlayers().get(clientHandler.getPlayerId());
|
Player player = this.world.getPlayers().get(clientHandler.getPlayerId());
|
||||||
synchronized (this.clientHandlers) {
|
this.clientHandlers.remove(clientHandler);
|
||||||
this.clientHandlers.remove(clientHandler);
|
clientHandler.shutdown();
|
||||||
clientHandler.shutdown();
|
|
||||||
}
|
|
||||||
this.world.getPlayers().remove(player.getId());
|
this.world.getPlayers().remove(player.getId());
|
||||||
if (player.getTeam() != null) {
|
if (player.getTeam() != null) {
|
||||||
player.getTeam().getPlayers().remove(player);
|
player.getTeam().getPlayers().remove(player);
|
||||||
}
|
}
|
||||||
this.broadcastMessage(new ChatMessage(player.getName() + " disconnected."));
|
String message = player.getName() + " disconnected.";
|
||||||
System.out.println("Client disconnected: " + player.getId() + ", " + player.getName());
|
this.broadcastMessage(new SystemChatMessage(SystemChatMessage.Level.INFO, message));
|
||||||
|
System.out.println(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendWorldToClients() {
|
public void sendWorldToClients() {
|
||||||
try {
|
for (ClientHandler handler : this.clientHandlers) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
try {
|
||||||
new ObjectOutputStream(bos).writeObject(this.world);
|
handler.send(new WorldUpdateMessage(this.world));
|
||||||
byte[] data = bos.toByteArray();
|
} catch (IOException e) {
|
||||||
DatagramPacket packet = new DatagramPacket(data, data.length);
|
e.printStackTrace();
|
||||||
for (ClientHandler handler : this.clientHandlers) {
|
|
||||||
if (handler.getDatagramPort() == -1) continue;
|
|
||||||
packet.setAddress(handler.getSocket().getInetAddress());
|
|
||||||
packet.setPort(handler.getDatagramPort());
|
|
||||||
this.datagramCommunicationThread.getSocket().send(packet);
|
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +125,7 @@ public class Server {
|
||||||
public void broadcastPlayerChat(int playerId, ChatMessage msg) {
|
public void broadcastPlayerChat(int playerId, ChatMessage msg) {
|
||||||
Player p = this.world.getPlayers().get(playerId);
|
Player p = this.world.getPlayers().get(playerId);
|
||||||
if (p == null) return;
|
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 server = new Server(port);
|
||||||
server.datagramCommunicationThread.start();
|
|
||||||
server.worldUpdater.start();
|
server.worldUpdater.start();
|
||||||
while (true) {
|
while (true) {
|
||||||
server.acceptClientConnection();
|
server.acceptClientConnection();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import nl.andrewlalis.aos_core.model.Barricade;
|
||||||
import nl.andrewlalis.aos_core.model.Bullet;
|
import nl.andrewlalis.aos_core.model.Bullet;
|
||||||
import nl.andrewlalis.aos_core.model.Player;
|
import nl.andrewlalis.aos_core.model.Player;
|
||||||
import nl.andrewlalis.aos_core.model.World;
|
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.ArrayList;
|
||||||
import java.util.List;
|
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)));
|
double dist = p.getPosition().dist(new Vec2(x1 + n * (x2 - x1), y1 + n * (y2 - y1)));
|
||||||
if (dist < Player.RADIUS) {
|
if (dist < Player.RADIUS) {
|
||||||
Player killer = this.world.getPlayers().get(b.getPlayerId());
|
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");
|
world.getSoundsToPlay().add("death.wav");
|
||||||
if (p.getTeam() != null) {
|
if (p.getTeam() != null) {
|
||||||
p.setPosition(p.getTeam().getSpawnPoint());
|
p.setPosition(p.getTeam().getSpawnPoint());
|
||||||
|
|
Loading…
Reference in New Issue