diff --git a/Assignment 4/src/ca/cosc3p91/a4/Main.java b/Assignment 4/src/ca/cosc3p91/a4/Main.java index 47c2b32..88c86f7 100644 --- a/Assignment 4/src/ca/cosc3p91/a4/Main.java +++ b/Assignment 4/src/ca/cosc3p91/a4/Main.java @@ -6,7 +6,7 @@ import java.io.*; public class Main { public static void main(String[] args) throws IOException { - Client gameClient = new Client("localhost"); + new Client("localhost"); } } diff --git a/Assignment 4/src/ca/cosc3p91/a4/userinterface/GameDisplay.java b/Assignment 4/src/ca/cosc3p91/a4/userinterface/GameDisplay.java index 116d5f3..72f3c0b 100644 --- a/Assignment 4/src/ca/cosc3p91/a4/userinterface/GameDisplay.java +++ b/Assignment 4/src/ca/cosc3p91/a4/userinterface/GameDisplay.java @@ -53,7 +53,7 @@ public class GameDisplay { Integer.toString(map.getTownHall().getGoldCapacity()), Integer.toString(map.getTownHall().getCurrentGold()))); - Print.print(resourcesPrinter.createTable(true, false, true)); + ArrayList total = new ArrayList<>(resourcesPrinter.createTable(true, false, true)); Print buildingPrinter = new Print("Village Buildings", 2, resourcesPrinter.getWidth()); buildingPrinter.addColumn(new Print.Column("Name")); @@ -65,7 +65,7 @@ public class GameDisplay { Integer.toString(b.getLevel() + 1), Integer.toString(b.getHealth()))); - Print.print(buildingPrinter.createTable(true, false, true)); + total.addAll(buildingPrinter.createTable(true, false, true)); Print inhabs = new Print("Village Inhabitants", 2, buildingPrinter.getWidth()); inhabs.addColumn(new Print.Column("Name")); @@ -74,7 +74,11 @@ public class GameDisplay { for (Inhabitant i : map.inhabitants) inhabs.addRow(new Print.Row(i.getClass().getSimpleName(), Integer.toString(i.getLevel() + 1))); - return inhabs.createTable(true, true, true); + total.addAll(inhabs.createTable(true, true, true)); + System.out.println(buildingPrinter.getWidth()); + System.out.println(resourcesPrinter.getWidth()); + System.out.println(inhabs.getWidth()); + return total; } public void printVillageState(Map map, String displayName) { diff --git a/Assignment 4/src/ca/cosc3p91/a4/util/network/Client.java b/Assignment 4/src/ca/cosc3p91/a4/util/network/Client.java index 7292945..3d601d5 100644 --- a/Assignment 4/src/ca/cosc3p91/a4/util/network/Client.java +++ b/Assignment 4/src/ca/cosc3p91/a4/util/network/Client.java @@ -22,6 +22,9 @@ public class Client implements Runnable { private final Map sentMessages = Collections.synchronizedMap(new HashMap<>()); private int lastMessageID = 0; private final InetAddress serverAddress; + private String[] lineBuffer = new String[0]; + private int expectedLines = 0; + private int currentLines = 0; private long ourClientID = 0; @@ -120,7 +123,23 @@ public class Client implements Runnable { case PacketTable.MESSAGE: System.out.println(stream.readUTF()); break; + case PacketTable.BEGIN_MAP_DATA: + expectedLines = stream.readInt(); + currentLines = 0; + lineBuffer = new String[expectedLines]; + break; + case PacketTable.MAP_LINE_DATA: + int lineNumber = stream.readInt(); + lineBuffer[lineNumber] = stream.readUTF(); + currentLines++; + if (currentLines >= expectedLines) { + for (String line : lineBuffer){ + System.out.println(line); + } + } + break; case PacketTable.DISCONNECT: + System.out.println("Disconnecting!"); running = false; break; } diff --git a/Assignment 4/src/ca/cosc3p91/a4/util/network/PacketTable.java b/Assignment 4/src/ca/cosc3p91/a4/util/network/PacketTable.java index ca3d279..146ec39 100644 --- a/Assignment 4/src/ca/cosc3p91/a4/util/network/PacketTable.java +++ b/Assignment 4/src/ca/cosc3p91/a4/util/network/PacketTable.java @@ -22,7 +22,10 @@ public class PacketTable { public static final byte TRAIN = 0x6; // messageHeader, upgrade public static final byte UPGRADE = 0x7; - // messageHeader, serial packets with map info - public static final byte PRINT_MAP_DATA = 0x8; - + // messageHeader + public static final byte PRINT_MAP_DATA = 0x8; // client -> server only! + // messageHeader, line count + public static final byte BEGIN_MAP_DATA = 0x9; // server -> client + // messageHeader, line number (int), UTF8 String (the line) + public static final byte MAP_LINE_DATA = 0xA; // server -> client } diff --git a/Assignment 4/src/ca/cosc3p91/a4/util/network/Server.java b/Assignment 4/src/ca/cosc3p91/a4/util/network/Server.java index 9ee675f..0dd00d4 100644 --- a/Assignment 4/src/ca/cosc3p91/a4/util/network/Server.java +++ b/Assignment 4/src/ca/cosc3p91/a4/util/network/Server.java @@ -143,6 +143,9 @@ public class Server implements Runnable { throw new RuntimeException("A message was acknowledged but does not exist!"); message.acknowledged(); sentMessages.remove(request.getMessageID()); + synchronized (sentMessages) { + sentMessages.notifyAll(); + } break; case PacketTable.MESSAGE: System.out.println(request.getReader().readUTF()); @@ -165,7 +168,7 @@ public class Server implements Runnable { usingEngine.upgradeBuilding(clientMap, Integer.parseInt(request.getReader().readUTF())); break; case PacketTable.PRINT_MAP_DATA: - usingEngine.view.getVillageStateTable(clientMap, "Home Village").forEach(this::sendMessageLn); + sendMapData(usingEngine.view.getVillageStateTable(clientMap, "Home Village")); break; } if (request.getPacketID() != PacketTable.ACK) @@ -187,35 +190,70 @@ public class Server implements Runnable { } requestLock.unlock(); - ArrayList removes = new ArrayList<>(); - for (HashMap.Entry message : sentMessages.entrySet()){ - Message.Sent sent = message.getValue(); - if (!sent.isAcknowledged() && sent.getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) { - System.out.println("The client did not acknowledge our message, did they receive it?"); - sendMessage(sent); - removes.add(message.getKey()); + // sentEntries needn't be in the synchronized block + Set> sentEntries = sentMessages.entrySet(); + synchronized (sentMessages) { + ArrayList removes = new ArrayList<>(); + for (HashMap.Entry message : sentEntries) { + Message.Sent sent = message.getValue(); + if (!sent.isAcknowledged() && sent.getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) { + System.out.println("The client did not acknowledge our message, did they receive it?"); + sendMessage(sent); + removes.add(message.getKey()); + } } + for (Long l : removes) + sentMessages.remove(l); } - for (Long l : removes) - sentMessages.remove(l); } } - private void sendMessageLn(String str) { + private void sendMapData(ArrayList lines) { + final long messageID = ++lastSentMessageID; + Message.Sent beginMapInfoMessage = new Message.Sent(PacketTable.BEGIN_MAP_DATA, clientID, messageID); + try { + beginMapInfoMessage.getWriter().writeInt(lines.size()); + sendMessage(beginMapInfoMessage); + } catch (IOException e) { + sendAndLogLn("Unable to send map data: " + e.getMessage()); + return; + } + new Thread(() -> { + while (sentMessages.containsKey(messageID)){ + try { + synchronized (sentMessages) { + sentMessages.wait(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + // once we know that the client is waiting on our map data, we can send it in any order. + for (int i = 0; i < lines.size(); i++){ + Message.Sent line = new Message.Sent(PacketTable.MAP_LINE_DATA, clientID, ++lastSentMessageID); + try { + // but we need the line index! + line.getWriter().writeInt(i); + line.getWriter().writeUTF(lines.get(i)); + } catch (IOException e){ + e.printStackTrace(); + } + sendMessage(line); + } + }).start(); + } + + private void sendAndLogLn(String str){ Message.Sent mess = new Message.Sent(PacketTable.MESSAGE, clientID, ++lastSentMessageID); try { mess.getWriter().writeUTF(str + "\n"); sendMessage(mess); + System.out.println(str); } catch (IOException e){ e.printStackTrace(); } } - private void sendAndLogLn(String str){ - sendMessageLn(str); - System.out.println(str); - } - public void sendMessage(Message.Sent message){ if (message.getPacketID() != PacketTable.ACK)