Compare commits
10 Commits
0228d6947a
...
fc0981852a
Author | SHA1 | Date |
---|---|---|
mike | fc0981852a | |
Brett | 1d7b378b52 | |
Brett | 49a78142d5 | |
Brett | 0a03b1f913 | |
Brett | b336a143a6 | |
Brett | a2cbc5d3af | |
mike | ae31c9f70b | |
Brett | 5ce406f376 | |
Brett | ceeef4b90e | |
mike | 9c878306b4 |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="RunConfigurationProducerService">
|
||||
<option name="ignoredProducers">
|
||||
<set>
|
||||
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -139,15 +139,15 @@ public class GameEngine {
|
|||
return score;
|
||||
}
|
||||
|
||||
public void updateMap(Map map) {
|
||||
for (Building b : map.contains){
|
||||
if ((b instanceof ResourceBuilding)) {
|
||||
((ResourceBuilding) b).update(map.getTownHall());
|
||||
public synchronized void updateMap(Map map) {
|
||||
for (int i = 0; i < map.contains.size(); i++) {
|
||||
if ((map.contains.get(i) instanceof ResourceBuilding)) {
|
||||
((ResourceBuilding) map.contains.get(i)).update(map.getTownHall());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean build (Map map, String buildingArg) {
|
||||
public synchronized boolean build (Map map, String buildingArg) {
|
||||
BuildingFactory bfactory = new BuildingFactory();
|
||||
Building type = bfactory.getBuilding(buildingArg);
|
||||
return map.build(new Tile(), type);
|
||||
|
@ -159,7 +159,7 @@ public class GameEngine {
|
|||
return map.train(type);
|
||||
}
|
||||
|
||||
public boolean upgradeBuilding (Map map, int buildingIndex) {
|
||||
public synchronized boolean upgradeBuilding (Map map, int buildingIndex) {
|
||||
return map.upgradeBuilding(buildingIndex);
|
||||
}
|
||||
|
||||
|
|
|
@ -29,17 +29,45 @@ public class Client implements Runnable {
|
|||
|
||||
sendMessage(new Message.Sent(PacketTable.CONNECT, 0, ++lastMessageID));
|
||||
|
||||
String prompt;
|
||||
while (running) {
|
||||
String prompt;
|
||||
if ((prompt = view.nextInput()) != null) {
|
||||
if (prompt.trim().isEmpty())
|
||||
continue;
|
||||
if (prompt.charAt(0) == '6')
|
||||
break;
|
||||
|
||||
byte messageType;
|
||||
switch (prompt.charAt(0)) {
|
||||
case '1':
|
||||
messageType = PacketTable.BUILD;
|
||||
break;
|
||||
case '2':
|
||||
messageType = PacketTable.TRAIN;
|
||||
break;
|
||||
case '3':
|
||||
messageType = PacketTable.UPGRADE;
|
||||
break;
|
||||
default:
|
||||
System.err.println("> Invalid command input!");
|
||||
return;
|
||||
}
|
||||
Message.Sent buildMessage = new Message.Sent(messageType,0,++lastMessageID);
|
||||
buildMessage.getData().write(prompt.substring(1).getBytes());
|
||||
sendMessage(buildMessage);
|
||||
|
||||
view.printGameMenu();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
@ -57,14 +85,19 @@ public class Client implements Runnable {
|
|||
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:
|
||||
Message.Sent message = sentMessages.get(messageID);
|
||||
if (message == null)
|
||||
throw new RuntimeException("Server message sync error!");
|
||||
throw new RuntimeException("Server acknowledged a message we never sent! (" + messageID + ")");
|
||||
message.acknowledged();
|
||||
sentMessages.remove(messageID);
|
||||
System.out.println("Message acknowledged " + 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.DISCONNECT:
|
||||
running = false;
|
||||
|
@ -78,10 +111,12 @@ public class Client implements Runnable {
|
|||
}
|
||||
|
||||
private void sendMessage(Message.Sent message){
|
||||
sentMessages.put(message.getMessageID(), 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();
|
||||
|
|
|
@ -16,7 +16,13 @@ public class PacketTable {
|
|||
public static final byte ACK = 0x3;
|
||||
// messageHeader, UTF8 String with length information (use DOS.writeUTF/DIS.readUTF)
|
||||
public static final byte MESSAGE = 0x4;
|
||||
// messageHeader, build
|
||||
public static final byte BUILD = 0x5;
|
||||
// messageHeader, train
|
||||
public static final byte TRAIN = 0x6;
|
||||
// messageHeader, upgrade
|
||||
public static final byte UPGRADE = 0x7;
|
||||
// messageHeader, serial packets with map info
|
||||
public static final byte MAP_DATA = 0x5;
|
||||
public static final byte USER_MAP_DATA = 0x8;
|
||||
|
||||
}
|
||||
|
|
|
@ -5,11 +5,15 @@ import ca.cosc3p91.a4.game.Map;
|
|||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.rmi.ServerException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class Server implements Runnable {
|
||||
|
@ -32,6 +36,8 @@ public class Server implements Runnable {
|
|||
socket = new DatagramSocket(SERVER_PORT);
|
||||
ioThread = new Thread(this);
|
||||
ioThread.start();
|
||||
|
||||
mainEngine = new GameEngine();
|
||||
}
|
||||
|
||||
public void run(){
|
||||
|
@ -49,21 +55,25 @@ public class Server implements Runnable {
|
|||
long clientID = stream.readLong();
|
||||
long messageID = stream.readLong();
|
||||
|
||||
System.out.println("Receiving message with ID " + messageID + " to client: " + clientID + " of type " + packetID);
|
||||
|
||||
ConnectedClient client = clients.get(clientID);
|
||||
|
||||
// the server must handle connection requests while the client's processing thread will handle all other messages
|
||||
if (packetID == PacketTable.CONNECT){
|
||||
clients.put(++clientAssignmentID, new ConnectedClient(socket, mainEngine, clientID, messageID, receivePacket.getAddress(), receivePacket.getPort()));
|
||||
} else if (packetID == PacketTable.DISCONNECT) {
|
||||
if (client == null)
|
||||
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
||||
client.halt();
|
||||
clients.put(clientID, null);
|
||||
} else {
|
||||
if (client == null)
|
||||
throw new ServerException("Client message with invalid client id! (" + clientID + ")");
|
||||
client.handleRequest(new Message.Received(packetID, clientID, messageID, stream, receivePacket.getData()));
|
||||
long cid = ++clientAssignmentID;
|
||||
System.out.println("A client has connected, his clientID is " + cid);
|
||||
clients.put(cid, new ConnectedClient(socket, mainEngine, cid, messageID, receivePacket.getAddress(), receivePacket.getPort()));
|
||||
continue;
|
||||
}
|
||||
if (client == null)
|
||||
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
||||
if (packetID == PacketTable.DISCONNECT) {
|
||||
client.halt();
|
||||
clients.remove(clientID);
|
||||
continue;
|
||||
}
|
||||
client.handleRequest(new Message.Received(packetID, clientID, messageID, stream, receivePacket.getData()));
|
||||
} catch (IOException | InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -84,11 +94,14 @@ public class Server implements Runnable {
|
|||
private final int port;
|
||||
private final Queue<Message.Received> pendingRequests = new PriorityQueue<>();
|
||||
private final ReentrantLock requestLock = new ReentrantLock();
|
||||
private final AtomicBoolean allowUpdate;
|
||||
private final HashMap<Long, Message.Sent> sentMessages = new HashMap<>();
|
||||
private final DatagramSocket serverSocket;
|
||||
private final long clientID;
|
||||
private volatile boolean running = true;
|
||||
private final Thread processingThread;
|
||||
private final Thread gameEngineThread;
|
||||
private final GameEngine usingEngine;
|
||||
private final Map clientMap;
|
||||
|
||||
public ConnectedClient(DatagramSocket serverSocket, GameEngine engine, long clientID, long messageID, InetAddress address, int port){
|
||||
|
@ -97,8 +110,18 @@ public class Server implements Runnable {
|
|||
this.port = port;
|
||||
this.clientID = clientID;
|
||||
this.clientMap = engine.generateInitialMap();
|
||||
this.usingEngine = engine;
|
||||
this.allowUpdate = new AtomicBoolean(true);
|
||||
processingThread = new Thread(this);
|
||||
processingThread.start();
|
||||
gameEngineThread = new Thread(() -> {
|
||||
while (running) {
|
||||
if (this.allowUpdate.get()) {
|
||||
engine.updateMap(clientMap);
|
||||
}
|
||||
}
|
||||
});
|
||||
gameEngineThread.start();
|
||||
|
||||
sendMessage(new Message.Sent(PacketTable.ACK, clientID, messageID));
|
||||
}
|
||||
|
@ -113,16 +136,27 @@ public class Server implements Runnable {
|
|||
|
||||
private void processRequest(Message.Received request){
|
||||
try {
|
||||
switch (request.getPacketID()){
|
||||
switch (request.getPacketID()) {
|
||||
case PacketTable.ACK:
|
||||
Message.Sent message = sentMessages.get(request.getMessageID());
|
||||
if (message == null)
|
||||
throw new RuntimeException("A message was acknowledged but does not exist!");
|
||||
message.acknowledged();
|
||||
sentMessages.remove(request.getMessageID());
|
||||
break;
|
||||
case PacketTable.MESSAGE:
|
||||
System.out.println(request.getReader().readUTF());
|
||||
break;
|
||||
case PacketTable.BUILD:
|
||||
usingEngine.build(clientMap,new String(request.getData(), StandardCharsets.UTF_8));
|
||||
break;
|
||||
case PacketTable.TRAIN:
|
||||
usingEngine.train(clientMap,new String(request.getData(), StandardCharsets.UTF_8));
|
||||
break;
|
||||
case PacketTable.UPGRADE:
|
||||
usingEngine.upgradeBuilding(clientMap, ByteBuffer.wrap(request.getData()).getInt());
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
@ -135,26 +169,35 @@ public class Server implements Runnable {
|
|||
requestLock.lock();
|
||||
if (!pendingRequests.isEmpty()) {
|
||||
Message.Received request = pendingRequests.remove();
|
||||
allowUpdate.set(false);
|
||||
processRequest(request);
|
||||
allowUpdate.set(true);
|
||||
}
|
||||
requestLock.unlock();
|
||||
|
||||
ArrayList<Long> removes = new ArrayList<>();
|
||||
for (HashMap.Entry<Long, Message.Sent> message : sentMessages.entrySet()){
|
||||
if (message.getValue().getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) {
|
||||
System.out.println("The server did not process our message, did they receive it?");
|
||||
// todo: resend message
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessage(Message.Sent message){
|
||||
this.sentMessages.put(message.getMessageID(), message);
|
||||
if (message.getPacketID() != PacketTable.ACK)
|
||||
this.sentMessages.put(message.getMessageID(), message);
|
||||
byte[] data = message.getData().toByteArray();
|
||||
if (data.length > PACKET_SIZE)
|
||||
throw new RuntimeException("Unable to send packet as it exceeds maximum packet size!");
|
||||
DatagramPacket request = new DatagramPacket(data, data.length, address, port);
|
||||
try {
|
||||
System.out.println("Sending message with ID " + message.getMessageID() + " to client: " + message.getClientID() + " of type " + message.getPacketID());
|
||||
serverSocket.send(request);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
|
Loading…
Reference in New Issue