149 lines
6.1 KiB
Java
149 lines
6.1 KiB
Java
package ca.cosc3p91.a4.util.network;
|
|
|
|
import ca.cosc3p91.a4.userinterface.GameDisplay;
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
import java.io.DataInputStream;
|
|
import java.io.IOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetAddress;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.Map;
|
|
|
|
public class Client implements Runnable {
|
|
private GameDisplay view = new GameDisplay();
|
|
|
|
private DatagramSocket clientSocket;
|
|
private volatile boolean running = true;
|
|
private Thread receiveThread;
|
|
private final Map<Long, Message.Sent> sentMessages = Collections.synchronizedMap(new HashMap<>());
|
|
private int lastMessageID = 0;
|
|
private final InetAddress serverAddress;
|
|
|
|
private long ourClientID = 0;
|
|
|
|
public Client(String address) throws IOException {
|
|
serverAddress = InetAddress.getByName(address);
|
|
clientSocket = new DatagramSocket();
|
|
receiveThread = new Thread(this);
|
|
receiveThread.start();
|
|
|
|
sendMessage(new Message.Sent(PacketTable.CONNECT, ourClientID, ++lastMessageID));
|
|
|
|
while (running) {
|
|
String prompt;
|
|
if ((prompt = view.nextInput()) != null) {
|
|
if (prompt.trim().isEmpty())
|
|
continue;
|
|
if (prompt.charAt(0) == '6') {
|
|
running = false;
|
|
break;
|
|
}
|
|
view.printGameMenu();
|
|
String[] args = prompt.split(" ");
|
|
char c = prompt.charAt(0);
|
|
if (c > '0' && c < '4') {
|
|
if (args.length < 2) {
|
|
System.err.println("Args must include type!");
|
|
continue;
|
|
}
|
|
}
|
|
byte messageType;
|
|
switch (c) {
|
|
case '1':
|
|
messageType = PacketTable.BUILD;
|
|
break;
|
|
case '2':
|
|
messageType = PacketTable.TRAIN;
|
|
break;
|
|
case '3':
|
|
messageType = PacketTable.UPGRADE;
|
|
break;
|
|
case '5':
|
|
messageType = PacketTable.PRINT_MAP_DATA;
|
|
break;
|
|
default:
|
|
System.err.println("> Invalid command input!");
|
|
return;
|
|
}
|
|
Message.Sent buildMessage = new Message.Sent(messageType,ourClientID,++lastMessageID);
|
|
buildMessage.getWriter().writeUTF(prompt.substring(1));
|
|
sendMessage(buildMessage);
|
|
}
|
|
ArrayList<Long> removes = new ArrayList<>();
|
|
for (HashMap.Entry<Long, Message.Sent> message : sentMessages.entrySet()){
|
|
Message.Sent sent = message.getValue();
|
|
if (!sent.isAcknowledged() && sent.getTimeSinceSent().get() > Server.MAX_PACKET_ACK_TIME_SECONDS) {
|
|
System.out.println("The server did not acknowledge our message, did they receive it?");
|
|
sendMessage(sent);
|
|
removes.add(message.getKey());
|
|
}
|
|
}
|
|
for (Long l : removes)
|
|
sentMessages.remove(l);
|
|
}
|
|
clientSocket.close();
|
|
}
|
|
|
|
public void run(){
|
|
while (running){
|
|
try {
|
|
byte[] receiveData = new byte[Server.PACKET_SIZE];
|
|
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
|
clientSocket.receive(receivePacket);
|
|
|
|
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(receivePacket.getData()));
|
|
|
|
byte packetID = stream.readByte();
|
|
long clientID = stream.readLong();
|
|
long messageID = stream.readLong();
|
|
|
|
System.out.println("Receiving message with ID " + messageID + " to client: " + clientID + " of type " + packetID);
|
|
|
|
switch (packetID) {
|
|
case PacketTable.ACK:
|
|
if (ourClientID == 0)
|
|
ourClientID = clientID;
|
|
Message.Sent message = sentMessages.get(messageID);
|
|
if (message == null)
|
|
throw new RuntimeException("Server acknowledged a message we never sent! (" + messageID + ")");
|
|
message.acknowledged();
|
|
sentMessages.remove(messageID);
|
|
System.out.println("Message acknowledged with ID: " + messageID);
|
|
System.out.println("Messages left: " + sentMessages.size());
|
|
for (HashMap.Entry<Long, Message.Sent> ms : sentMessages.entrySet())
|
|
System.out.println("MessageID: " + ms.getKey());
|
|
break;
|
|
case PacketTable.MESSAGE:
|
|
System.out.println(stream.readUTF());
|
|
break;
|
|
case PacketTable.DISCONNECT:
|
|
running = false;
|
|
break;
|
|
}
|
|
if (packetID != PacketTable.ACK && packetID != PacketTable.DISCONNECT){
|
|
sendMessage(new Message.Sent(PacketTable.ACK, ourClientID, messageID));
|
|
}
|
|
} catch (Exception e){
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void sendMessage(Message.Sent message){
|
|
if (message.getPacketID() != PacketTable.ACK)
|
|
sentMessages.put(message.getMessageID(), message);
|
|
byte[] data = message.getData().toByteArray();
|
|
DatagramPacket sendPacket = new DatagramPacket(data, data.length, serverAddress, Server.SERVER_PORT);
|
|
try {
|
|
System.out.println("Sending message with ID " + message.getMessageID() + " to server from: " + message.getClientID() + " of type " + message.getPacketID());
|
|
clientSocket.send(sendPacket);
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
}
|