Compare commits
No commits in common. "fc0981852a6e232a9aa0e3f31b60620b0291c240" and "0228d6947af5078a134a1e0417f9565aef9efab3" have entirely different histories.
fc0981852a
...
0228d6947a
|
@ -1,10 +0,0 @@
|
||||||
<?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;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void updateMap(Map map) {
|
public void updateMap(Map map) {
|
||||||
for (int i = 0; i < map.contains.size(); i++) {
|
for (Building b : map.contains){
|
||||||
if ((map.contains.get(i) instanceof ResourceBuilding)) {
|
if ((b instanceof ResourceBuilding)) {
|
||||||
((ResourceBuilding) map.contains.get(i)).update(map.getTownHall());
|
((ResourceBuilding) b).update(map.getTownHall());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean build (Map map, String buildingArg) {
|
public boolean build (Map map, String buildingArg) {
|
||||||
BuildingFactory bfactory = new BuildingFactory();
|
BuildingFactory bfactory = new BuildingFactory();
|
||||||
Building type = bfactory.getBuilding(buildingArg);
|
Building type = bfactory.getBuilding(buildingArg);
|
||||||
return map.build(new Tile(), type);
|
return map.build(new Tile(), type);
|
||||||
|
@ -159,7 +159,7 @@ public class GameEngine {
|
||||||
return map.train(type);
|
return map.train(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean upgradeBuilding (Map map, int buildingIndex) {
|
public boolean upgradeBuilding (Map map, int buildingIndex) {
|
||||||
return map.upgradeBuilding(buildingIndex);
|
return map.upgradeBuilding(buildingIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,45 +29,17 @@ public class Client implements Runnable {
|
||||||
|
|
||||||
sendMessage(new Message.Sent(PacketTable.CONNECT, 0, ++lastMessageID));
|
sendMessage(new Message.Sent(PacketTable.CONNECT, 0, ++lastMessageID));
|
||||||
|
|
||||||
|
String prompt;
|
||||||
while (running) {
|
while (running) {
|
||||||
String prompt;
|
|
||||||
if ((prompt = view.nextInput()) != null) {
|
if ((prompt = view.nextInput()) != null) {
|
||||||
if (prompt.trim().isEmpty())
|
if (prompt.trim().isEmpty())
|
||||||
continue;
|
continue;
|
||||||
if (prompt.charAt(0) == '6')
|
if (prompt.charAt(0) == '6')
|
||||||
break;
|
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();
|
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();
|
clientSocket.close();
|
||||||
}
|
}
|
||||||
|
@ -85,19 +57,14 @@ public class Client implements Runnable {
|
||||||
long clientID = stream.readLong();
|
long clientID = stream.readLong();
|
||||||
long messageID = stream.readLong();
|
long messageID = stream.readLong();
|
||||||
|
|
||||||
System.out.println("Receiving message with ID " + messageID + " to client: " + clientID + " of type " + packetID);
|
|
||||||
|
|
||||||
switch (packetID) {
|
switch (packetID) {
|
||||||
case PacketTable.ACK:
|
case PacketTable.ACK:
|
||||||
Message.Sent message = sentMessages.get(messageID);
|
Message.Sent message = sentMessages.get(messageID);
|
||||||
if (message == null)
|
if (message == null)
|
||||||
throw new RuntimeException("Server acknowledged a message we never sent! (" + messageID + ")");
|
throw new RuntimeException("Server message sync error!");
|
||||||
message.acknowledged();
|
message.acknowledged();
|
||||||
sentMessages.remove(messageID);
|
sentMessages.remove(messageID);
|
||||||
System.out.println("Message acknowledged with ID: " + messageID);
|
System.out.println("Message acknowledged " + messageID);
|
||||||
System.out.println("Messages left: " + sentMessages.size());
|
|
||||||
for (HashMap.Entry<Long, Message.Sent> ms : sentMessages.entrySet())
|
|
||||||
System.out.println("MessageID: " + ms.getKey());
|
|
||||||
break;
|
break;
|
||||||
case PacketTable.DISCONNECT:
|
case PacketTable.DISCONNECT:
|
||||||
running = false;
|
running = false;
|
||||||
|
@ -111,12 +78,10 @@ public class Client implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendMessage(Message.Sent message){
|
private void sendMessage(Message.Sent message){
|
||||||
if (message.getPacketID() != PacketTable.ACK)
|
sentMessages.put(message.getMessageID(), message);
|
||||||
sentMessages.put(message.getMessageID(), message);
|
|
||||||
byte[] data = message.getData().toByteArray();
|
byte[] data = message.getData().toByteArray();
|
||||||
DatagramPacket sendPacket = new DatagramPacket(data, data.length, serverAddress, Server.SERVER_PORT);
|
DatagramPacket sendPacket = new DatagramPacket(data, data.length, serverAddress, Server.SERVER_PORT);
|
||||||
try {
|
try {
|
||||||
System.out.println("Sending message with ID " + message.getMessageID() + " to server from: " + message.getClientID() + " of type " + message.getPacketID());
|
|
||||||
clientSocket.send(sendPacket);
|
clientSocket.send(sendPacket);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
|
@ -16,13 +16,7 @@ public class PacketTable {
|
||||||
public static final byte ACK = 0x3;
|
public static final byte ACK = 0x3;
|
||||||
// messageHeader, UTF8 String with length information (use DOS.writeUTF/DIS.readUTF)
|
// messageHeader, UTF8 String with length information (use DOS.writeUTF/DIS.readUTF)
|
||||||
public static final byte MESSAGE = 0x4;
|
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
|
// messageHeader, serial packets with map info
|
||||||
public static final byte USER_MAP_DATA = 0x8;
|
public static final byte MAP_DATA = 0x5;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,15 +5,11 @@ import ca.cosc3p91.a4.game.Map;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.rmi.ServerException;
|
import java.rmi.ServerException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.PriorityQueue;
|
import java.util.PriorityQueue;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
public class Server implements Runnable {
|
public class Server implements Runnable {
|
||||||
|
@ -36,8 +32,6 @@ public class Server implements Runnable {
|
||||||
socket = new DatagramSocket(SERVER_PORT);
|
socket = new DatagramSocket(SERVER_PORT);
|
||||||
ioThread = new Thread(this);
|
ioThread = new Thread(this);
|
||||||
ioThread.start();
|
ioThread.start();
|
||||||
|
|
||||||
mainEngine = new GameEngine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
|
@ -55,25 +49,21 @@ public class Server implements Runnable {
|
||||||
long clientID = stream.readLong();
|
long clientID = stream.readLong();
|
||||||
long messageID = 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);
|
ConnectedClient client = clients.get(clientID);
|
||||||
|
|
||||||
// the server must handle connection requests while the client's processing thread will handle all other messages
|
// the server must handle connection requests while the client's processing thread will handle all other messages
|
||||||
if (packetID == PacketTable.CONNECT){
|
if (packetID == PacketTable.CONNECT){
|
||||||
long cid = ++clientAssignmentID;
|
clients.put(++clientAssignmentID, new ConnectedClient(socket, mainEngine, clientID, messageID, receivePacket.getAddress(), receivePacket.getPort()));
|
||||||
System.out.println("A client has connected, his clientID is " + cid);
|
} else if (packetID == PacketTable.DISCONNECT) {
|
||||||
clients.put(cid, new ConnectedClient(socket, mainEngine, cid, messageID, receivePacket.getAddress(), receivePacket.getPort()));
|
if (client == null)
|
||||||
continue;
|
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
||||||
}
|
|
||||||
if (client == null)
|
|
||||||
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
|
||||||
if (packetID == PacketTable.DISCONNECT) {
|
|
||||||
client.halt();
|
client.halt();
|
||||||
clients.remove(clientID);
|
clients.put(clientID, null);
|
||||||
continue;
|
} 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()));
|
||||||
}
|
}
|
||||||
client.handleRequest(new Message.Received(packetID, clientID, messageID, stream, receivePacket.getData()));
|
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
@ -94,14 +84,11 @@ public class Server implements Runnable {
|
||||||
private final int port;
|
private final int port;
|
||||||
private final Queue<Message.Received> pendingRequests = new PriorityQueue<>();
|
private final Queue<Message.Received> pendingRequests = new PriorityQueue<>();
|
||||||
private final ReentrantLock requestLock = new ReentrantLock();
|
private final ReentrantLock requestLock = new ReentrantLock();
|
||||||
private final AtomicBoolean allowUpdate;
|
|
||||||
private final HashMap<Long, Message.Sent> sentMessages = new HashMap<>();
|
private final HashMap<Long, Message.Sent> sentMessages = new HashMap<>();
|
||||||
private final DatagramSocket serverSocket;
|
private final DatagramSocket serverSocket;
|
||||||
private final long clientID;
|
private final long clientID;
|
||||||
private volatile boolean running = true;
|
private volatile boolean running = true;
|
||||||
private final Thread processingThread;
|
private final Thread processingThread;
|
||||||
private final Thread gameEngineThread;
|
|
||||||
private final GameEngine usingEngine;
|
|
||||||
private final Map clientMap;
|
private final Map clientMap;
|
||||||
|
|
||||||
public ConnectedClient(DatagramSocket serverSocket, GameEngine engine, long clientID, long messageID, InetAddress address, int port){
|
public ConnectedClient(DatagramSocket serverSocket, GameEngine engine, long clientID, long messageID, InetAddress address, int port){
|
||||||
|
@ -110,18 +97,8 @@ public class Server implements Runnable {
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.clientID = clientID;
|
this.clientID = clientID;
|
||||||
this.clientMap = engine.generateInitialMap();
|
this.clientMap = engine.generateInitialMap();
|
||||||
this.usingEngine = engine;
|
|
||||||
this.allowUpdate = new AtomicBoolean(true);
|
|
||||||
processingThread = new Thread(this);
|
processingThread = new Thread(this);
|
||||||
processingThread.start();
|
processingThread.start();
|
||||||
gameEngineThread = new Thread(() -> {
|
|
||||||
while (running) {
|
|
||||||
if (this.allowUpdate.get()) {
|
|
||||||
engine.updateMap(clientMap);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
gameEngineThread.start();
|
|
||||||
|
|
||||||
sendMessage(new Message.Sent(PacketTable.ACK, clientID, messageID));
|
sendMessage(new Message.Sent(PacketTable.ACK, clientID, messageID));
|
||||||
}
|
}
|
||||||
|
@ -136,27 +113,16 @@ public class Server implements Runnable {
|
||||||
|
|
||||||
private void processRequest(Message.Received request){
|
private void processRequest(Message.Received request){
|
||||||
try {
|
try {
|
||||||
switch (request.getPacketID()) {
|
switch (request.getPacketID()){
|
||||||
case PacketTable.ACK:
|
case PacketTable.ACK:
|
||||||
Message.Sent message = sentMessages.get(request.getMessageID());
|
Message.Sent message = sentMessages.get(request.getMessageID());
|
||||||
if (message == null)
|
if (message == null)
|
||||||
throw new RuntimeException("A message was acknowledged but does not exist!");
|
throw new RuntimeException("A message was acknowledged but does not exist!");
|
||||||
message.acknowledged();
|
message.acknowledged();
|
||||||
sentMessages.remove(request.getMessageID());
|
|
||||||
break;
|
break;
|
||||||
case PacketTable.MESSAGE:
|
case PacketTable.MESSAGE:
|
||||||
System.out.println(request.getReader().readUTF());
|
System.out.println(request.getReader().readUTF());
|
||||||
break;
|
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) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -169,35 +135,26 @@ public class Server implements Runnable {
|
||||||
requestLock.lock();
|
requestLock.lock();
|
||||||
if (!pendingRequests.isEmpty()) {
|
if (!pendingRequests.isEmpty()) {
|
||||||
Message.Received request = pendingRequests.remove();
|
Message.Received request = pendingRequests.remove();
|
||||||
allowUpdate.set(false);
|
|
||||||
processRequest(request);
|
processRequest(request);
|
||||||
allowUpdate.set(true);
|
|
||||||
}
|
}
|
||||||
requestLock.unlock();
|
requestLock.unlock();
|
||||||
|
|
||||||
ArrayList<Long> removes = new ArrayList<>();
|
|
||||||
for (HashMap.Entry<Long, Message.Sent> message : sentMessages.entrySet()){
|
for (HashMap.Entry<Long, Message.Sent> message : sentMessages.entrySet()){
|
||||||
Message.Sent sent = message.getValue();
|
if (message.getValue().getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) {
|
||||||
if (!sent.isAcknowledged() && sent.getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) {
|
System.out.println("The server did not process our message, did they receive it?");
|
||||||
System.out.println("The client did not acknowledge our message, did they receive it?");
|
// todo: resend message
|
||||||
sendMessage(sent);
|
|
||||||
removes.add(message.getKey());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Long l : removes)
|
|
||||||
sentMessages.remove(l);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendMessage(Message.Sent message){
|
public void sendMessage(Message.Sent message){
|
||||||
if (message.getPacketID() != PacketTable.ACK)
|
this.sentMessages.put(message.getMessageID(), message);
|
||||||
this.sentMessages.put(message.getMessageID(), message);
|
|
||||||
byte[] data = message.getData().toByteArray();
|
byte[] data = message.getData().toByteArray();
|
||||||
if (data.length > PACKET_SIZE)
|
if (data.length > PACKET_SIZE)
|
||||||
throw new RuntimeException("Unable to send packet as it exceeds maximum packet size!");
|
throw new RuntimeException("Unable to send packet as it exceeds maximum packet size!");
|
||||||
DatagramPacket request = new DatagramPacket(data, data.length, address, port);
|
DatagramPacket request = new DatagramPacket(data, data.length, address, port);
|
||||||
try {
|
try {
|
||||||
System.out.println("Sending message with ID " + message.getMessageID() + " to client: " + message.getClientID() + " of type " + message.getPacketID());
|
|
||||||
serverSocket.send(request);
|
serverSocket.send(request);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
Loading…
Reference in New Issue