Compare commits
6 Commits
0372f2e2c3
...
d2200e6300
Author | SHA1 | Date |
---|---|---|
Brett | d2200e6300 | |
Brett | 88a52a6f74 | |
Brett | cf0e6bd5c8 | |
Brett | d5bbf1a930 | |
Brett | 3ce19169cc | |
Brett | d98d5ac025 |
|
@ -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.*;
|
import java.io.BufferedOutputStream;
|
||||||
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,25 +21,19 @@ 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(InputStream commandStream, OutputStream outputBuf) {
|
public GameEngine() {
|
||||||
player = new Player();
|
player = new Player();
|
||||||
map = generateInitialMap();
|
map = generateInitialMap();
|
||||||
input = commandStream;
|
|
||||||
output = outputBuf;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attackVillage(Map map) {
|
public void attackVillage(Map map) {
|
||||||
|
@ -155,23 +149,21 @@ public class GameEngine implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
String in;
|
String in;
|
||||||
|
|
||||||
view = new GameDisplay(input);
|
view = new GameDisplay();
|
||||||
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 && !in.isEmpty()) {
|
if ((in = view.nextInput()) != null) {
|
||||||
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
|
||||||
|
@ -180,53 +172,52 @@ 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) {
|
||||||
output.write("Args must include type".getBytes());
|
System.err.println("Args must include type!");
|
||||||
} 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)
|
||||||
output.write("Args are not a valid building!".getBytes());
|
System.err.println("Args are not a valid building!");
|
||||||
} else if (this.map.build(new Tile(), type) ) {
|
else if (this.map.build(new Tile(), type) ) {
|
||||||
output.write((type.getClass().getSimpleName()+" successfully built\n").getBytes());
|
System.out.println(type.getClass().getSimpleName()+" successfully built\n");
|
||||||
} else {
|
} else
|
||||||
output.write(("Missing resources to build "+type.getClass().getSimpleName()).getBytes());
|
System.out.println("Missing resources to build "+type.getClass().getSimpleName());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '2':
|
case '2':
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
output.write("Args must include type".getBytes());
|
System.err.println("Args must include type!");
|
||||||
} 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)
|
||||||
output.write("Args are not a valid inhabitant!".getBytes());
|
System.err.println("Args are not a valid inhabitant!");
|
||||||
} else if (this.map.train(type) ) {
|
else if (this.map.train(type) ) {
|
||||||
output.write(("successfully trained a(n) "+type.getClass().getSimpleName()).getBytes());
|
System.out.println("successfully trained a(n) "+type.getClass().getSimpleName());
|
||||||
} else output.write(("Missing resources to train "+type.getClass().getSimpleName()).getBytes());
|
} else System.out.println("Missing gold to train "+type.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '3':
|
case '3':
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
output.write("Args must include type".getBytes());
|
System.err.println("Args must include type!");
|
||||||
} else {
|
} else {
|
||||||
int unitIndex = Integer.parseInt(args[1].substring(1));
|
int unitIndex = Integer.parseInt(args[1].substring(1));
|
||||||
|
|
||||||
if (unitIndex < 0) {
|
if (unitIndex < 0) {
|
||||||
output.write("Invalid Index".getBytes());
|
System.err.println("Invalid Index");
|
||||||
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) ) {
|
||||||
output.write(("successfully upgraded a(n) "+map.inhabitants.get(unitIndex).getClass().getSimpleName()).getBytes());
|
System.out.println("successfully upgraded a(n) "+map.inhabitants.get(unitIndex).getClass().getSimpleName());
|
||||||
} else output.write(("Missing Resources to upgrade "+map.inhabitants.get(unitIndex).getClass().getSimpleName()).getBytes());
|
} else System.out.println("Missing Resources to upgrade "+map.inhabitants.get(unitIndex).getClass().getSimpleName());
|
||||||
} 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) ) {
|
||||||
output.write(("successfully upgraded a(n) "+map.contains.get(unitIndex).getClass().getSimpleName()).getBytes());
|
System.out.println("successfully upgraded a(n) "+map.contains.get(unitIndex).getClass().getSimpleName());
|
||||||
} else output.write(("Missing Resources to upgrade "+map.contains.get(unitIndex).getClass().getSimpleName()).getBytes());
|
} else System.out.println("Missing Resources to upgrade "+map.contains.get(unitIndex).getClass().getSimpleName());
|
||||||
} else {
|
} else {
|
||||||
output.write("Args is not a valid unit".getBytes());
|
System.err.println("Args are not a valid unit!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -257,7 +248,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,15 @@ 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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,35 +4,170 @@ 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 {
|
public class Server implements Runnable {
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
public static ByteArrayInputStream stream_in;
|
private final HashMap<Long, ConnectedClient> clients = new HashMap<>();
|
||||||
public static ByteArrayOutputStream stream_out;
|
private long clientAssignmentID = 0;
|
||||||
public static GameEngine mainEngine;
|
private long lastMessageID = 0;
|
||||||
|
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 {
|
||||||
DatagramSocket serverSocket = new DatagramSocket(SERVER_PORT);
|
new Server();
|
||||||
byte[] receiveData = new byte[1284];
|
|
||||||
byte[] sendData = new byte[1284];
|
|
||||||
stream_in = new ByteArrayInputStream(receiveData);
|
|
||||||
stream_out = new ByteArrayOutputStream(1284);
|
|
||||||
new Thread(mainEngine = new GameEngine(stream_in,stream_out)).start();
|
|
||||||
while(true) {
|
|
||||||
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
|
||||||
serverSocket.receive(receivePacket);
|
|
||||||
stream_in.reset();
|
|
||||||
Thread.sleep(500);
|
|
||||||
InetAddress IPAddress = receivePacket.getAddress();
|
|
||||||
int port = receivePacket.getPort();
|
|
||||||
sendData = stream_out.toByteArray();
|
|
||||||
stream_out.reset();
|
|
||||||
DatagramPacket sendPacket =
|
|
||||||
new DatagramPacket(sendData, sendData.length, IPAddress, port);
|
|
||||||
serverSocket.send(sendPacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class ConnectedClient implements Runnable {
|
||||||
|
private final InetAddress address;
|
||||||
|
private final int port;
|
||||||
|
private final ArrayList<ServerRequest> requests = new ArrayList<>();
|
||||||
|
private final Queue<ServerRequest> pendingRequests = new PriorityQueue<>();
|
||||||
|
// could use read/write lock for some of this, as certain operations, mostly timeout check, won't modify data.
|
||||||
|
private final ReentrantLock requestLock = new ReentrantLock();
|
||||||
|
private final DatagramSocket socket;
|
||||||
|
private final long clientID;
|
||||||
|
private volatile boolean running = true;
|
||||||
|
private final Thread processingThread;
|
||||||
|
|
||||||
|
public ConnectedClient(DatagramSocket socket, long clientID, InetAddress address, int port){
|
||||||
|
this.socket = socket;
|
||||||
|
this.address = address;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,4 +47,7 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue