Compare commits
No commits in common. "0228d6947af5078a134a1e0417f9565aef9efab3" and "d2200e63005f7a9c4159171c8a9d6e3b68ae7f08" have entirely different histories.
0228d6947a
...
d2200e6300
|
@ -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>
|
|
@ -1,12 +1,12 @@
|
||||||
package ca.cosc3p91.a4;
|
package ca.cosc3p91.a4;
|
||||||
|
|
||||||
import ca.cosc3p91.a4.util.network.Client;
|
import ca.cosc3p91.a4.util.Client;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
Client gameClient = new Client("localhost");
|
Client gameClient = new Client(42069);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,24 +13,30 @@ import java.nio.file.Files;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class GameEngine {
|
public class GameEngine implements Runnable {
|
||||||
|
|
||||||
public static final double GOLD_FACTOR = 5;
|
public static final double GOLD_FACTOR = 5;
|
||||||
public static final double IRON_FACTOR = 1;
|
public static final double IRON_FACTOR = 1;
|
||||||
public static final double WOOD_FACTOR = 0.1;
|
public static final double WOOD_FACTOR = 0.1;
|
||||||
|
|
||||||
|
private Player player;
|
||||||
|
boolean running = true;
|
||||||
|
|
||||||
private float pillageFactor = 0.5f;
|
private float pillageFactor = 0.5f;
|
||||||
|
|
||||||
private int currentTime;
|
private int currentTime;
|
||||||
|
|
||||||
private final Random random = new Random(System.nanoTime());
|
private final Random random = new Random(System.nanoTime());
|
||||||
|
|
||||||
|
public Map map;
|
||||||
public GameDisplay view;
|
public GameDisplay view;
|
||||||
|
|
||||||
public GameEngine() {
|
public GameEngine() {
|
||||||
|
player = new Player();
|
||||||
|
map = generateInitialMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void attackVillage(Map attacking, Map defending) {
|
public void attackVillage(Map map) {
|
||||||
// int defenseiveCounter = 1;
|
// int defenseiveCounter = 1;
|
||||||
// int inhabCounter = 0;
|
// int inhabCounter = 0;
|
||||||
// for (Building b : map.contains)
|
// for (Building b : map.contains)
|
||||||
|
@ -47,31 +53,31 @@ public class GameEngine {
|
||||||
// this.map.getTownHall().addWood((int) (map.getTownHall().getCurrentWood() * pillageFactor));
|
// this.map.getTownHall().addWood((int) (map.getTownHall().getCurrentWood() * pillageFactor));
|
||||||
// this.map.getTownHall().addIron((int) (map.getTownHall().getCurrentIron() * pillageFactor));
|
// this.map.getTownHall().addIron((int) (map.getTownHall().getCurrentIron() * pillageFactor));
|
||||||
// this.map.getTownHall().addGold((int) (map.getTownHall().getCurrentGold() * pillageFactor));
|
// this.map.getTownHall().addGold((int) (map.getTownHall().getCurrentGold() * pillageFactor));
|
||||||
ChallengeAdapter adapter = new ChallengeAdapter(attacking);
|
ChallengeAdapter adapter = new ChallengeAdapter(this.map);
|
||||||
adapter.attack(defending);
|
adapter.attack(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map generateInitialMap(){
|
private Map generateInitialMap(){
|
||||||
return new Map(new CasaDeNarino(1, VillageHallStages.villageStages[0]), 30);
|
return new Map(new CasaDeNarino(1, VillageHallStages.villageStages[0]), 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map generateMap(Map map) {
|
public Map generateMap() {
|
||||||
Map initialMap = generateInitialMap();
|
Map initialMap = generateInitialMap();
|
||||||
|
|
||||||
CasaDeNarino hall = initialMap.getTownHall();
|
CasaDeNarino hall = initialMap.getTownHall();
|
||||||
|
|
||||||
// generate a similar town hall
|
// generate a similar town hall
|
||||||
int levelChange = random.nextInt(2) - 1;
|
int levelChange = random.nextInt(2) - 1;
|
||||||
int nextLevel = map.getTownHall().getLevel() + levelChange;
|
int nextLevel = this.map.getTownHall().getLevel() + levelChange;
|
||||||
// only need to change if the new village level is higher than initial
|
// only need to change if the new village level is higher than initial
|
||||||
if (nextLevel > 0)
|
if (nextLevel > 0)
|
||||||
hall.upgrade(VillageHallStages.villageStages[nextLevel]);
|
hall.upgrade(VillageHallStages.villageStages[nextLevel]);
|
||||||
|
|
||||||
hall.addWood(map.getTownHall().getCurrentWood() + random.nextInt(500) - 150);
|
hall.addWood(this.map.getTownHall().getCurrentWood() + random.nextInt(500) - 150);
|
||||||
hall.addIron(map.getTownHall().getCurrentIron() + random.nextInt(500) - 150);
|
hall.addIron(this.map.getTownHall().getCurrentIron() + random.nextInt(500) - 150);
|
||||||
hall.addGold(map.getTownHall().getCurrentGold() + random.nextInt(500) - 150);
|
hall.addGold(this.map.getTownHall().getCurrentGold() + random.nextInt(500) - 150);
|
||||||
|
|
||||||
int buildingCount = map.contains.size();
|
int buildingCount = this.map.contains.size();
|
||||||
|
|
||||||
int saulGoodMines = 0;
|
int saulGoodMines = 0;
|
||||||
int ironMines = 0;
|
int ironMines = 0;
|
||||||
|
@ -80,7 +86,7 @@ public class GameEngine {
|
||||||
int cannons = 0;
|
int cannons = 0;
|
||||||
|
|
||||||
// count buildings in our map
|
// count buildings in our map
|
||||||
for (Building b : map.contains){
|
for (Building b : this.map.contains){
|
||||||
if (b instanceof SaulGoodMine)
|
if (b instanceof SaulGoodMine)
|
||||||
saulGoodMines++;
|
saulGoodMines++;
|
||||||
else if (b instanceof IronMine)
|
else if (b instanceof IronMine)
|
||||||
|
@ -139,35 +145,6 @@ public class GameEngine {
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateMap(Map map) {
|
|
||||||
for (Building b : map.contains){
|
|
||||||
if ((b instanceof ResourceBuilding)) {
|
|
||||||
((ResourceBuilding) b).update(map.getTownHall());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean build (Map map, String buildingArg) {
|
|
||||||
BuildingFactory bfactory = new BuildingFactory();
|
|
||||||
Building type = bfactory.getBuilding(buildingArg);
|
|
||||||
return map.build(new Tile(), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean train (Map map, String inhabitantArgs) {
|
|
||||||
InhabitantFactory ifactory = new InhabitantFactory();
|
|
||||||
Inhabitant type = ifactory.getInhabitant(inhabitantArgs);
|
|
||||||
return map.train(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean upgradeBuilding (Map map, int buildingIndex) {
|
|
||||||
return map.upgradeBuilding(buildingIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean upgradeInhabitant (Map map, int inhabitantIndex) {
|
|
||||||
return map.upgradeInhabitant(inhabitantIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
String in;
|
String in;
|
||||||
|
@ -179,6 +156,11 @@ public class GameEngine {
|
||||||
Map exploringMap = null;
|
Map exploringMap = null;
|
||||||
boolean deleteMyHeart = true;
|
boolean deleteMyHeart = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
|
for (Building b : this.map.contains){
|
||||||
|
if ((b instanceof ResourceBuilding)) {
|
||||||
|
((ResourceBuilding) b).update(this.map.getTownHall());
|
||||||
|
}
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if ((in = view.nextInput()) != null) {
|
if ((in = view.nextInput()) != null) {
|
||||||
String[] args = in.split(" ");
|
String[] args = in.split(" ");
|
||||||
|
@ -267,7 +249,8 @@ public class GameEngine {
|
||||||
if (deleteMyHeart)
|
if (deleteMyHeart)
|
||||||
exploringMap = null;
|
exploringMap = null;
|
||||||
}
|
}
|
||||||
} */
|
save("test.xml", this.map);
|
||||||
|
}
|
||||||
|
|
||||||
public void save(String file, Map map){
|
public void save(String file, Map map){
|
||||||
try (XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) {
|
try (XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(Files.newOutputStream(Paths.get(file))))) {
|
||||||
|
|
|
@ -56,7 +56,7 @@ public class Map implements Serializable {
|
||||||
|
|
||||||
public boolean upgradeBuilding(int buildingIndex) {
|
public boolean upgradeBuilding(int buildingIndex) {
|
||||||
|
|
||||||
if (buildingIndex >= contains.size() || buildingIndex < 0) return false;
|
if (buildingIndex >= contains.size()) return false;
|
||||||
|
|
||||||
Building b = contains.get(buildingIndex);
|
Building b = contains.get(buildingIndex);
|
||||||
|
|
||||||
|
@ -93,7 +93,7 @@ public class Map implements Serializable {
|
||||||
|
|
||||||
public boolean upgradeInhabitant(int inhabitantIndex) {
|
public boolean upgradeInhabitant(int inhabitantIndex) {
|
||||||
|
|
||||||
if (inhabitantIndex >= inhabitants.size() || inhabitantIndex < 0) return false;
|
if (inhabitantIndex >= inhabitants.size()) return false;
|
||||||
|
|
||||||
Inhabitant i = inhabitants.get(inhabitantIndex);
|
Inhabitant i = inhabitants.get(inhabitantIndex);
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@ public class GameDisplay {
|
||||||
private BufferedReader reader;
|
private BufferedReader reader;
|
||||||
private String input;
|
private String input;
|
||||||
|
|
||||||
public GameDisplay() {
|
public GameDisplay(InputStream readFrom) {
|
||||||
reader = new BufferedReader(new InputStreamReader(System.in));
|
reader = new BufferedReader(new InputStreamReader(readFrom));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String nextInput() throws IOException {
|
public String nextInput() throws IOException {
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ca.cosc3p91.a4.util;
|
||||||
|
|
||||||
|
import ca.cosc3p91.a4.userinterface.GameDisplay;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
|
||||||
|
public class Client {
|
||||||
|
GameDisplay view = new GameDisplay(System.in);
|
||||||
|
|
||||||
|
public Client(int port) throws IOException {
|
||||||
|
DatagramSocket clientSocket = new DatagramSocket();
|
||||||
|
InetAddress IPAddress = InetAddress.getByName("localhost");
|
||||||
|
String prompt;
|
||||||
|
byte[] sendData = new byte[1024];
|
||||||
|
byte[] receiveData = new byte[1024];
|
||||||
|
while (true) {
|
||||||
|
if ((prompt = view.nextInput()) != null) {
|
||||||
|
if (!prompt.isEmpty() && prompt.charAt(0) == '6') break;
|
||||||
|
sendData = prompt.getBytes();
|
||||||
|
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, port);
|
||||||
|
clientSocket.send(sendPacket);
|
||||||
|
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
||||||
|
clientSocket.receive(receivePacket);
|
||||||
|
String serverOutput = new String(receivePacket.getData()).trim();
|
||||||
|
System.out.println(">" + serverOutput);
|
||||||
|
view.printGameMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clientSocket.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package ca.cosc3p91.a4.util;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
}
|
|
@ -1,15 +1,11 @@
|
||||||
package ca.cosc3p91.a4.util.network;
|
package ca.cosc3p91.a4.util;
|
||||||
|
|
||||||
import ca.cosc3p91.a4.game.GameEngine;
|
import ca.cosc3p91.a4.game.GameEngine;
|
||||||
import ca.cosc3p91.a4.game.Map;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.rmi.ServerException;
|
import java.rmi.ServerException;
|
||||||
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.PriorityQueue;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
public class Server implements Runnable {
|
public class Server implements Runnable {
|
||||||
|
@ -20,9 +16,9 @@ public class Server implements Runnable {
|
||||||
|
|
||||||
private final HashMap<Long, ConnectedClient> clients = new HashMap<>();
|
private final HashMap<Long, ConnectedClient> clients = new HashMap<>();
|
||||||
private long clientAssignmentID = 0;
|
private long clientAssignmentID = 0;
|
||||||
|
private long lastMessageID = 0;
|
||||||
private final DatagramSocket socket;
|
private final DatagramSocket socket;
|
||||||
private final Thread ioThread;
|
private final Thread ioThread;
|
||||||
private long lastSentMessageID = 0;
|
|
||||||
|
|
||||||
private GameEngine mainEngine;
|
private GameEngine mainEngine;
|
||||||
|
|
||||||
|
@ -36,24 +32,21 @@ public class Server implements Runnable {
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
|
||||||
byte[] receiveData = new byte[PACKET_SIZE];
|
byte[] receiveData = new byte[PACKET_SIZE];
|
||||||
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
||||||
|
try {
|
||||||
// BLOCKING!
|
// BLOCKING!
|
||||||
socket.receive(receivePacket);
|
socket.receive(receivePacket);
|
||||||
|
|
||||||
// read in the message header that is associated with every message.
|
|
||||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(receivePacket.getData()));
|
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(receivePacket.getData()));
|
||||||
|
|
||||||
byte packetID = stream.readByte();
|
byte packetID = stream.readByte();
|
||||||
long clientID = stream.readLong();
|
long clientID = stream.readLong();
|
||||||
long messageID = stream.readLong();
|
|
||||||
|
|
||||||
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
|
|
||||||
if (packetID == PacketTable.CONNECT){
|
if (packetID == PacketTable.CONNECT){
|
||||||
clients.put(++clientAssignmentID, new ConnectedClient(socket, mainEngine, clientID, messageID, receivePacket.getAddress(), receivePacket.getPort()));
|
clients.put(++clientAssignmentID, new ConnectedClient(socket, clientID, receivePacket.getAddress(), receivePacket.getPort()));
|
||||||
} else if (packetID == PacketTable.DISCONNECT){
|
} else if (packetID == PacketTable.DISCONNECT){
|
||||||
if (client == null)
|
if (client == null)
|
||||||
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
throw new ServerException("Client disconnected with invalid client id! (" + clientID + ")");
|
||||||
|
@ -62,7 +55,7 @@ public class Server implements Runnable {
|
||||||
} else {
|
} else {
|
||||||
if (client == null)
|
if (client == null)
|
||||||
throw new ServerException("Client message with invalid client id! (" + clientID + ")");
|
throw new ServerException("Client message with invalid client id! (" + clientID + ")");
|
||||||
client.handleRequest(new Message.Received(packetID, clientID, messageID, stream, receivePacket.getData()));
|
client.handleRequest(new ConnectedClient.ServerRequest(packetID, stream));
|
||||||
}
|
}
|
||||||
} catch (IOException | InterruptedException e) {
|
} catch (IOException | InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -82,90 +75,99 @@ public class Server implements Runnable {
|
||||||
private static class ConnectedClient implements Runnable {
|
private static class ConnectedClient implements Runnable {
|
||||||
private final InetAddress address;
|
private final InetAddress address;
|
||||||
private final int port;
|
private final int port;
|
||||||
private final Queue<Message.Received> pendingRequests = new PriorityQueue<>();
|
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 ReentrantLock requestLock = new ReentrantLock();
|
||||||
private final HashMap<Long, Message.Sent> sentMessages = new HashMap<>();
|
private final DatagramSocket socket;
|
||||||
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 Map clientMap;
|
|
||||||
|
|
||||||
public ConnectedClient(DatagramSocket serverSocket, GameEngine engine, long clientID, long messageID, InetAddress address, int port){
|
public ConnectedClient(DatagramSocket socket, long clientID, InetAddress address, int port){
|
||||||
this.serverSocket = serverSocket;
|
this.socket = socket;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.clientID = clientID;
|
this.clientID = clientID;
|
||||||
this.clientMap = engine.generateInitialMap();
|
|
||||||
processingThread = new Thread(this);
|
processingThread = new Thread(this);
|
||||||
processingThread.start();
|
processingThread.start();
|
||||||
|
|
||||||
sendMessage(new Message.Sent(PacketTable.ACK, clientID, messageID));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleRequest(Message.Received request){
|
public void handleRequest(ServerRequest request){
|
||||||
if (request.getClientID() != this.clientID)
|
|
||||||
throw new RuntimeException("Server sent us a message, yet we are not the intended recipient!");
|
|
||||||
requestLock.lock();
|
requestLock.lock();
|
||||||
pendingRequests.add(request);
|
pendingRequests.add(request);
|
||||||
requestLock.unlock();
|
requestLock.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processRequest(Message.Received request){
|
private void processRequest(ServerRequest request){
|
||||||
try {
|
try {
|
||||||
switch (request.getPacketID()){
|
switch (request.getID()){
|
||||||
case PacketTable.ACK:
|
case PacketTable.ACK:
|
||||||
Message.Sent message = sentMessages.get(request.getMessageID());
|
long messageID = request.getDataStream().readLong();
|
||||||
if (message == null)
|
|
||||||
throw new RuntimeException("A message was acknowledged but does not exist!");
|
|
||||||
message.acknowledged();
|
|
||||||
break;
|
|
||||||
case PacketTable.MESSAGE:
|
|
||||||
System.out.println(request.getReader().readUTF());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run(){
|
public void run(){
|
||||||
while (running){
|
while (running){
|
||||||
// handle request processing without blocking the I/O thread
|
|
||||||
requestLock.lock();
|
requestLock.lock();
|
||||||
if (!pendingRequests.isEmpty()) {
|
while (pendingRequests.size() > 0) {
|
||||||
Message.Received request = pendingRequests.remove();
|
ServerRequest request = pendingRequests.remove();
|
||||||
processRequest(request);
|
processRequest(request);
|
||||||
|
requests.add(request);
|
||||||
}
|
}
|
||||||
requestLock.unlock();
|
requestLock.unlock();
|
||||||
|
|
||||||
for (HashMap.Entry<Long, Message.Sent> message : sentMessages.entrySet()){
|
requests.removeIf(ServerRequest::isAck);
|
||||||
if (message.getValue().getTimeSinceSent().get() > MAX_PACKET_ACK_TIME_SECONDS) {
|
for (ServerRequest request : requests){
|
||||||
System.out.println("The server did not process our message, did they receive it?");
|
// TODO:
|
||||||
// todo: resend message
|
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 sendMessage(Message.Sent message){
|
|
||||||
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 {
|
|
||||||
serverSocket.send(request);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void halt() throws InterruptedException {
|
public void halt() throws InterruptedException {
|
||||||
running = false;
|
running = false;
|
||||||
processingThread.join();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
package ca.cosc3p91.a4.util.network;
|
|
||||||
|
|
||||||
import ca.cosc3p91.a4.userinterface.GameDisplay;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.DatagramPacket;
|
|
||||||
import java.net.DatagramSocket;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class Client implements Runnable {
|
|
||||||
private GameDisplay view = new GameDisplay();
|
|
||||||
|
|
||||||
private DatagramSocket clientSocket;
|
|
||||||
private boolean running = true;
|
|
||||||
private Thread receiveThread;
|
|
||||||
private final HashMap<Long, Message.Sent> sentMessages = new HashMap<>();
|
|
||||||
private int lastMessageID = 0;
|
|
||||||
private final InetAddress serverAddress;
|
|
||||||
|
|
||||||
public Client(String address) throws IOException {
|
|
||||||
serverAddress = InetAddress.getByName(address);
|
|
||||||
clientSocket = new DatagramSocket();
|
|
||||||
receiveThread = new Thread(this);
|
|
||||||
receiveThread.start();
|
|
||||||
|
|
||||||
sendMessage(new Message.Sent(PacketTable.CONNECT, 0, ++lastMessageID));
|
|
||||||
|
|
||||||
String prompt;
|
|
||||||
while (running) {
|
|
||||||
if ((prompt = view.nextInput()) != null) {
|
|
||||||
if (prompt.trim().isEmpty())
|
|
||||||
continue;
|
|
||||||
if (prompt.charAt(0) == '6')
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
view.printGameMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clientSocket.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run(){
|
|
||||||
while (running){
|
|
||||||
try {
|
|
||||||
byte[] receiveData = new byte[Server.PACKET_SIZE];
|
|
||||||
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
|
|
||||||
clientSocket.receive(receivePacket);
|
|
||||||
|
|
||||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(receivePacket.getData()));
|
|
||||||
|
|
||||||
byte packetID = stream.readByte();
|
|
||||||
long clientID = stream.readLong();
|
|
||||||
long messageID = stream.readLong();
|
|
||||||
|
|
||||||
switch (packetID) {
|
|
||||||
case PacketTable.ACK:
|
|
||||||
Message.Sent message = sentMessages.get(messageID);
|
|
||||||
if (message == null)
|
|
||||||
throw new RuntimeException("Server message sync error!");
|
|
||||||
message.acknowledged();
|
|
||||||
sentMessages.remove(messageID);
|
|
||||||
System.out.println("Message acknowledged " + messageID);
|
|
||||||
break;
|
|
||||||
case PacketTable.DISCONNECT:
|
|
||||||
running = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMessage(Message.Sent message){
|
|
||||||
sentMessages.put(message.getMessageID(), message);
|
|
||||||
byte[] data = message.getData().toByteArray();
|
|
||||||
DatagramPacket sendPacket = new DatagramPacket(data, data.length, serverAddress, Server.SERVER_PORT);
|
|
||||||
try {
|
|
||||||
clientSocket.send(sendPacket);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,105 +0,0 @@
|
||||||
package ca.cosc3p91.a4.util.network;
|
|
||||||
|
|
||||||
import ca.cosc3p91.a4.util.Time;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
|
|
||||||
public class Message {
|
|
||||||
|
|
||||||
private final byte packetID;
|
|
||||||
private final long clientID, messageID;
|
|
||||||
|
|
||||||
public Message(byte packetID, long clientID, long messageID){
|
|
||||||
this.packetID = packetID;
|
|
||||||
this.clientID = clientID;
|
|
||||||
this.messageID = messageID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte getPacketID() {
|
|
||||||
return packetID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getClientID() {
|
|
||||||
return clientID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMessageID() {
|
|
||||||
return messageID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Received extends Message {
|
|
||||||
|
|
||||||
private final DataInputStream reader;
|
|
||||||
private final byte[] data;
|
|
||||||
|
|
||||||
public Received(byte packetID, long clientID, long messageID, DataInputStream reader, byte[] data) {
|
|
||||||
super(packetID, clientID, messageID);
|
|
||||||
this.reader = reader;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataInputStream getReader(){
|
|
||||||
return reader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getData(){
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Sent extends Message {
|
|
||||||
|
|
||||||
private final DataOutputStream writer;
|
|
||||||
private final ByteArrayOutputStream data;
|
|
||||||
private boolean ack = false;
|
|
||||||
private final Time timeSent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A message packet which will be sent to a client or the server, contains the standard message header and
|
|
||||||
* writes the header to the stream, make sure you don't write into the stream before constructing this!
|
|
||||||
*
|
|
||||||
* @param packetID type of this message
|
|
||||||
* @param clientID the client id, if this is going to the client it is unlikely to be used but should always be correct!
|
|
||||||
* @param messageID client specific message id, used to reference/acknowledge messages
|
|
||||||
* @param writer stream to write to
|
|
||||||
* @param data byte array stream which contains the byte[] used in packet construction
|
|
||||||
*/
|
|
||||||
public Sent(byte packetID, long clientID, long messageID) {
|
|
||||||
super(packetID, clientID, messageID);
|
|
||||||
this.data = new ByteArrayOutputStream();
|
|
||||||
this.writer = new DataOutputStream(this.data);
|
|
||||||
timeSent = Time.getTime();
|
|
||||||
// write the header to the stream, make sure you don't write into the stream before constructing this!
|
|
||||||
try {
|
|
||||||
writer.writeByte(packetID);
|
|
||||||
writer.writeLong(clientID);
|
|
||||||
writer.writeLong(messageID);
|
|
||||||
} catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void acknowledged(){
|
|
||||||
this.ack = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAcknowledged(){
|
|
||||||
return ack;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DataOutputStream getWriter(){
|
|
||||||
return writer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Time getTimeSinceSent(){
|
|
||||||
return Time.getTime().difference(timeSent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ByteArrayOutputStream getData(){
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
package ca.cosc3p91.a4.util.network;
|
|
||||||
|
|
||||||
public class PacketTable {
|
|
||||||
|
|
||||||
// packetID -> byte defined in this file
|
|
||||||
// clientID -> long
|
|
||||||
// messageID -> long
|
|
||||||
|
|
||||||
// messageHeader (packetID, clientID, messageID)
|
|
||||||
|
|
||||||
// messageHeader, (clientID = 0 if connecting to server)
|
|
||||||
public static final byte CONNECT = 0x1;
|
|
||||||
// messageHeader
|
|
||||||
public static final byte DISCONNECT = 0x2;
|
|
||||||
// messageHeader
|
|
||||||
public static final byte ACK = 0x3;
|
|
||||||
// messageHeader, UTF8 String with length information (use DOS.writeUTF/DIS.readUTF)
|
|
||||||
public static final byte MESSAGE = 0x4;
|
|
||||||
// messageHeader, serial packets with map info
|
|
||||||
public static final byte MAP_DATA = 0x5;
|
|
||||||
|
|
||||||
}
|
|
Loading…
Reference in New Issue