Compare commits

..

No commits in common. "d2200e63005f7a9c4159171c8a9d6e3b68ae7f08" and "0372f2e2c31d781c3970bf81c44539a9d2d25618" have entirely different histories.

4 changed files with 57 additions and 194 deletions

View File

@ -8,7 +8,7 @@ import ca.cosc3p91.a4.userinterface.GameDisplay;
import ca.cosc3p91.a4.util.ChallengeAdapter; import ca.cosc3p91.a4.util.ChallengeAdapter;
import java.beans.XMLEncoder; import java.beans.XMLEncoder;
import java.io.BufferedOutputStream; import java.io.*;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.Random; import java.util.Random;
@ -21,19 +21,25 @@ public class GameEngine implements Runnable {
private Player player; private Player player;
boolean running = true; boolean running = true;
public boolean execFlag = true;
private float pillageFactor = 0.5f; private float pillageFactor = 0.5f;
private int currentTime; private int currentTime;
private final InputStream input;
private final OutputStream output;
public ObjectOutputStream mapOutput;
private final Random random = new Random(System.nanoTime()); private final Random random = new Random(System.nanoTime());
public Map map; public Map map;
public GameDisplay view; public GameDisplay view;
public GameEngine() { public GameEngine(InputStream commandStream, OutputStream outputBuf) {
player = new Player(); player = new Player();
map = generateInitialMap(); map = generateInitialMap();
input = commandStream;
output = outputBuf;
} }
public void attackVillage(Map map) { public void attackVillage(Map map) {
@ -149,21 +155,23 @@ public class GameEngine implements Runnable {
public void run() { public void run() {
String in; String in;
view = new GameDisplay(); view = new GameDisplay(input);
view.printVillageState(this.map,"Current Village State"); view.printVillageState(this.map,"Current Village State");
view.printGameMenu(); view.printGameMenu();
Map exploringMap = null; Map exploringMap = null;
boolean deleteMyHeart = true; boolean deleteMyHeart = true;
while (running) { while (running) {
execFlag = true;
for (Building b : this.map.contains){ for (Building b : this.map.contains){
if ((b instanceof ResourceBuilding)) { if ((b instanceof ResourceBuilding)) {
((ResourceBuilding) b).update(this.map.getTownHall()); ((ResourceBuilding) b).update(this.map.getTownHall());
} }
} }
try { try {
if ((in = view.nextInput()) != null) { if ((in = view.nextInput()) != null && !in.isEmpty()) {
String[] args = in.split(" "); String[] args = in.split(" ");
if (in.charAt(0) == '0') continue;
view.printLastInput(); view.printLastInput();
// reset the map if they aren't exploring // reset the map if they aren't exploring
@ -172,52 +180,53 @@ public class GameEngine implements Runnable {
switch (in.charAt(0)) { switch (in.charAt(0)) {
case '1': case '1':
if (args.length < 2) { if (args.length < 2) {
System.err.println("Args must include type!"); output.write("Args must include type".getBytes());
} else { } else {
BuildingFactory bfactory = new BuildingFactory(); BuildingFactory bfactory = new BuildingFactory();
Building type = bfactory.getBuilding(args[1]); Building type = bfactory.getBuilding(args[1]);
if (type == null) if (type == null) {
System.err.println("Args are not a valid building!"); output.write("Args are not a valid building!".getBytes());
else if (this.map.build(new Tile(), type) ) { } else if (this.map.build(new Tile(), type) ) {
System.out.println(type.getClass().getSimpleName()+" successfully built\n"); output.write((type.getClass().getSimpleName()+" successfully built\n").getBytes());
} else } else {
System.out.println("Missing resources to build "+type.getClass().getSimpleName()); output.write(("Missing resources to build "+type.getClass().getSimpleName()).getBytes());
}
} }
break; break;
case '2': case '2':
if (args.length < 2) { if (args.length < 2) {
System.err.println("Args must include type!"); output.write("Args must include type".getBytes());
} else { } else {
InhabitantFactory ifactory = new InhabitantFactory(); InhabitantFactory ifactory = new InhabitantFactory();
Inhabitant type = ifactory.getInhabitant(args[1]); Inhabitant type = ifactory.getInhabitant(args[1]);
if (type == null) if (type == null) {
System.err.println("Args are not a valid inhabitant!"); output.write("Args are not a valid inhabitant!".getBytes());
else if (this.map.train(type) ) { } else if (this.map.train(type) ) {
System.out.println("successfully trained a(n) "+type.getClass().getSimpleName()); output.write(("successfully trained a(n) "+type.getClass().getSimpleName()).getBytes());
} else System.out.println("Missing gold to train "+type.getClass().getSimpleName()); } else output.write(("Missing resources to train "+type.getClass().getSimpleName()).getBytes());
} }
break; break;
case '3': case '3':
if (args.length < 2) { if (args.length < 2) {
System.err.println("Args must include type!"); output.write("Args must include type".getBytes());
} else { } else {
int unitIndex = Integer.parseInt(args[1].substring(1)); int unitIndex = Integer.parseInt(args[1].substring(1));
if (unitIndex < 0) { if (unitIndex < 0) {
System.err.println("Invalid Index"); output.write("Invalid Index".getBytes());
break; break;
} }
if (args[1].contains("i") && (unitIndex < map.inhabitants.size()) ) { if (args[1].contains("i") && (unitIndex < map.inhabitants.size()) ) {
if ( map.upgradeInhabitant(unitIndex) ) { if ( map.upgradeInhabitant(unitIndex) ) {
System.out.println("successfully upgraded a(n) "+map.inhabitants.get(unitIndex).getClass().getSimpleName()); output.write(("successfully upgraded a(n) "+map.inhabitants.get(unitIndex).getClass().getSimpleName()).getBytes());
} else System.out.println("Missing Resources to upgrade "+map.inhabitants.get(unitIndex).getClass().getSimpleName()); } else output.write(("Missing Resources to upgrade "+map.inhabitants.get(unitIndex).getClass().getSimpleName()).getBytes());
} else if (args[1].contains("b") && (unitIndex < map.contains.size()) ) { } else if (args[1].contains("b") && (unitIndex < map.contains.size()) ) {
if ( map.upgradeBuilding(unitIndex) ) { if ( map.upgradeBuilding(unitIndex) ) {
System.out.println("successfully upgraded a(n) "+map.contains.get(unitIndex).getClass().getSimpleName()); output.write(("successfully upgraded a(n) "+map.contains.get(unitIndex).getClass().getSimpleName()).getBytes());
} else System.out.println("Missing Resources to upgrade "+map.contains.get(unitIndex).getClass().getSimpleName()); } else output.write(("Missing Resources to upgrade "+map.contains.get(unitIndex).getClass().getSimpleName()).getBytes());
} else { } else {
System.err.println("Args are not a valid unit!"); output.write("Args is not a valid unit".getBytes());
} }
} }
break; break;
@ -248,6 +257,7 @@ public class GameEngine implements Runnable {
} }
if (deleteMyHeart) if (deleteMyHeart)
exploringMap = null; exploringMap = null;
execFlag = false;
} }
save("test.xml", this.map); save("test.xml", this.map);
} }

View File

@ -2,15 +2,6 @@ package ca.cosc3p91.a4.util;
public class PacketTable { public class PacketTable {
// packetID -> byte defined in this file
// clientID -> long
// messageID -> long
// packetID, clientID (0 if connecting to server)
public static final byte CONNECT = 0x1;
// packetID, clientID
public static final byte DISCONNECT = 0x2;
// packetID, clientID, messageID
public static final byte ACK = 0x3;
} }

View File

@ -4,170 +4,35 @@ import ca.cosc3p91.a4.game.GameEngine;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.rmi.ServerException;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
public class Server implements Runnable { public class Server {
public static final int SERVER_PORT = 42069; public static final int SERVER_PORT = 42069;
public static final int PACKET_SIZE = 4096;
public static final long MAX_PACKET_ACK_TIME_SECONDS = 30;
private final HashMap<Long, ConnectedClient> clients = new HashMap<>(); public static ByteArrayInputStream stream_in;
private long clientAssignmentID = 0; public static ByteArrayOutputStream stream_out;
private long lastMessageID = 0; public static GameEngine mainEngine;
private final DatagramSocket socket;
private final Thread ioThread;
private GameEngine mainEngine;
private volatile boolean running = true;
public Server() throws SocketException {
socket = new DatagramSocket(SERVER_PORT);
ioThread = new Thread(this);
ioThread.start();
}
public void run(){
while (running) {
byte[] receiveData = new byte[PACKET_SIZE];
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
try {
// BLOCKING!
socket.receive(receivePacket);
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(receivePacket.getData()));
byte packetID = stream.readByte();
long clientID = stream.readLong();
ConnectedClient client = clients.get(clientID);
if (packetID == PacketTable.CONNECT){
clients.put(++clientAssignmentID, new ConnectedClient(socket, clientID, 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 ConnectedClient.ServerRequest(packetID, stream));
}
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public void halt() throws InterruptedException {
running = false;
ioThread.join();
}
public static void main(String[] args) throws IOException, InterruptedException { public static void main(String[] args) throws IOException, InterruptedException {
new Server(); DatagramSocket serverSocket = new DatagramSocket(SERVER_PORT);
} byte[] receiveData = new byte[1284];
byte[] sendData = new byte[1284];
private static class ConnectedClient implements Runnable { stream_in = new ByteArrayInputStream(receiveData);
private final InetAddress address; stream_out = new ByteArrayOutputStream(1284);
private final int port; new Thread(mainEngine = new GameEngine(stream_in,stream_out)).start();
private final ArrayList<ServerRequest> requests = new ArrayList<>(); while(true) {
private final Queue<ServerRequest> pendingRequests = new PriorityQueue<>(); DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
// could use read/write lock for some of this, as certain operations, mostly timeout check, won't modify data. serverSocket.receive(receivePacket);
private final ReentrantLock requestLock = new ReentrantLock(); stream_in.reset();
private final DatagramSocket socket; Thread.sleep(500);
private final long clientID; InetAddress IPAddress = receivePacket.getAddress();
private volatile boolean running = true; int port = receivePacket.getPort();
private final Thread processingThread; sendData = stream_out.toByteArray();
stream_out.reset();
public ConnectedClient(DatagramSocket socket, long clientID, InetAddress address, int port){ DatagramPacket sendPacket =
this.socket = socket; new DatagramPacket(sendData, sendData.length, IPAddress, port);
this.address = address; serverSocket.send(sendPacket);
this.port = port;
this.clientID = clientID;
processingThread = new Thread(this);
processingThread.start();
} }
public void handleRequest(ServerRequest request){
requestLock.lock();
pendingRequests.add(request);
requestLock.unlock();
}
private void processRequest(ServerRequest request){
try {
switch (request.getID()){
case PacketTable.ACK:
long messageID = request.getDataStream().readLong();
break;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void run(){
while (running){
requestLock.lock();
while (pendingRequests.size() > 0) {
ServerRequest request = pendingRequests.remove();
processRequest(request);
requests.add(request);
}
requestLock.unlock();
requests.removeIf(ServerRequest::isAck);
for (ServerRequest request : requests){
// TODO:
if (request.getTimeSinceReceived().get() > MAX_PACKET_ACK_TIME_SECONDS)
System.out.println("A packet hasn't received a ack, it might have been lost!");
}
}
}
public void halt() throws InterruptedException {
running = false;
processingThread.join();
}
private static class ServerRequest {
private final byte id;
private final Time receiveTime;
private final DataInputStream receive;
private boolean ack = false;
public ServerRequest(byte id, DataInputStream receive){
this.id = id;
this.receive = receive;
receiveTime = Time.getTime();
}
public byte getID(){
return id;
}
public void acknowledged(){
this.ack = true;
}
public boolean isAck(){
return this.ack;
}
public DataInputStream getDataStream(){
return receive;
}
public Time getTimeSinceReceived(){
return Time.getTime().difference(receiveTime);
}
}
} }
} }

View File

@ -47,7 +47,4 @@ public class Time implements Serializable {
return new Time(System.currentTimeMillis() / 1000); return new Time(System.currentTimeMillis() / 1000);
} }
public Time difference(Time right) {
return new Time(this.timeSeconds - right.timeSeconds);
}
} }