Index: Server.java
===================================================================
--- Server.java	(revision 2)
+++ Server.java	(nonexistent)
@@ -1,492 +0,0 @@
-import java.rmi.registry.Registry;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.RemoteException;
-import java.rmi.server.UnicastRemoteObject;
-
-import java.util.Map;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.Executors;
-import java.util.concurrent.CopyOnWriteArrayList;;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Collections;
-
-import java.io.*;
-
-import java.net.DatagramPacket;
-
-import java.nio.file.*;
-
-//Server class essentially a Peer
-public class Server implements PeerInterface {
-
-    // Unique peer id
-    public String id;
-
-    // Size controls
-    public static final int DEFAULT_MAX_SIZE = 1000000000;
-    public int limit;
-    public int used;
-
-    // Thread Pool
-    public static ScheduledThreadPoolExecutor pool;
-
-    // Sockets
-    public static Socket MC;
-    public static Socket MDB;
-    public static Socket MDR;
-
-    // State Backup
-    public boolean doingBackUp;
-    public String file;
-    public int replications;
-
-    // State Restore
-    public boolean restoring;
-    public AtomicBoolean waitRestoreReply = new AtomicBoolean(false);
-    public ConcurrentHashMap<String, Chunk> recoveredChunks = new ConcurrentHashMap<String, Chunk>(); // ChunkNo ->
-                                                                                                      // Chunk
-
-    // Service Management Information
-    public ConcurrentHashMap<String, Chunk> chunks = new ConcurrentHashMap<String, Chunk>(); // fileId-ChunkNo
-                                                                                             // -> Chunk
-    public ConcurrentHashMap<String, String> files = new ConcurrentHashMap<String, String>(); // fileId -> // file name
-
-    // Empty Constructor
-    public Server() {
-    }
-
-    // Simplified Constructor(Default values)
-    public Server(String id) throws IOException {
-        this.used = 0;
-        this.id = id;
-        this.limit = DEFAULT_MAX_SIZE;
-        this.setupSockets();
-        this.loadLocal();
-        this.setupDirectory();
-    }
-
-    // Full constructor
-    public Server(String id, int portMC, String addressMC, int portMDB, String addressMDB, int portMDR,
-            String addressMDR) throws IOException {
-        this.used = 0;
-        this.id = id;
-        this.limit = DEFAULT_MAX_SIZE;
-        this.setupSockets(portMC, addressMC, portMDB, addressMDB, portMDR, addressMDR);
-        this.loadLocal();
-        this.setupDirectory();
-    }
-
-    // Backup Protocol
-    public void backup(String path, int replications) throws RemoteException {
-
-        // Start backup
-        this.doingBackUp = true;
-
-        // Extract file name
-        int index = path.lastIndexOf("\\");
-        String fileName = path.substring(index + 1);
-
-        // Generate Hash from file name and date
-        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
-
-        // Is file backed up already?
-        if (this.files.get(fileId) != null) {
-            System.out.println("File already backed up");
-            return;
-        }
-
-        // Add to file list
-        this.files.put(fileId, fileName);
-
-        try {
-
-            // Get file bytes
-            byte[] bytes = FileManager.readFile(path);
-
-            // Get file in chunks
-            byte[][] chunks = FileManager.split(bytes);
-
-            // Send each chunk
-            for (int i = 0; i < chunks.length; i++) {
-
-                // Base receive time of 1 second
-                int receiveTime = 1000;
-
-                // PUTCHUNK tries
-                for (int j = 0; j < 5; j++) {
-
-                    // Prepare message
-                    Message msg = new Message(Message.MessageType.PUTCHUNK, id, fileId, i, replications, chunks[i]);
-
-                    // Pack in datagram
-                    DatagramPacket packet = msg.packit(MDB.address, MDB.port);
-
-                    // Send message
-                    MDB.socket.send(packet);
-
-                    // Wait for replies and check Replication level
-                    this.replications = 0;
-                    this.file = fileId;
-                    Thread.sleep(receiveTime);
-                    if (this.replications >= replications) {
-                        break;
-                    }
-
-                    // Double receive time
-                    receiveTime *= 2;
-
-                }
-
-            }
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        // Terminate backup
-        this.doingBackUp = false;
-
-    }
-
-    // Restore Protocol
-    public void restore(String path) throws RemoteException {
-
-        this.restoring = true;
-
-        // File name
-        int index = path.lastIndexOf("\\");
-        String fileName = path.substring(index + 1);
-
-        // Generate Hash from file name and date
-        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
-
-        // Find in list of backed up
-        String name = this.files.get(fileId);
-
-        // File not backed up yet, just return
-        if (name == null) {
-            System.out.println("File not backed up by this peer");
-            return;
-        }
-
-        try {
-
-            // Get byte count (probably better than actually fetching the bytes array)
-            File file = new File(path);
-            int bytes = (int) file.length();
-
-            // How many chunks to expect
-            int chunks = 1 + bytes / FileManager.chunkSize;
-
-            // Reset list of Chunks recovered
-            this.recoveredChunks.clear();
-
-            // Request chunks
-            for (int i = 0; i < chunks; i++) {
-
-                // Prepare message
-                Message msg = new Message(Message.MessageType.GETCHUNK, id, fileId, i, 0, new byte[0]);
-
-                // Pack in datagram
-                DatagramPacket packet = msg.packit(MC.address, MC.port);
-
-                // Send message
-                MC.socket.send(packet);
-
-                // Wait for response
-                this.waitRestoreReply.set(true);
-                long time = System.nanoTime();
-                while (this.waitRestoreReply.get()) {
-                    // Restore timeout
-                    if (TimeUnit.NANOSECONDS.toMillis(time) - TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) > 1000) {
-                        System.out.println("Restore timeout, missing chunks");
-                        return;
-                    }
-                }
-
-            }
-
-            // Get simple list from recovered chunks
-            List<Chunk> list = new ArrayList<Chunk>(this.recoveredChunks.values());
-
-            // Sort chunks
-            Collections.sort(list, new Comparator<Chunk>() {
-                @Override
-                public int compare(Chunk p1, Chunk p2) {
-                    return p1.chunkN - p2.chunkN; // Ascending
-                }
-            });
-
-            // Pack chunks
-            byte[][] allBytes = new byte[list.size()][];
-            for (int i = 0; i < list.size(); i++) {
-                allBytes[i] = list.get(i).bytes;
-            }
-
-            // Merge chunks
-            byte[] fileBytes = FileManager.merge(allBytes);
-
-            // Save file
-            Path current = Paths.get("");
-            String place = current.toAbsolutePath().toString() + "\\peer" + id + "\\restored\\" + fileName;
-            try (FileOutputStream stream = new FileOutputStream(place)) {
-                stream.write(fileBytes);
-                stream.close();
-            }
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        this.restoring = false;
-
-    }
-
-    // Delete chunks associated to this file
-    public void delete(String path) throws RemoteException {
-
-        // File name
-        int index = path.lastIndexOf("\\");
-        String fileName = path.substring(index + 1);
-
-        // Generate Hash from file name and date
-        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
-
-        // Remove from list of backed up
-        String name = this.files.get(fileId);
-        if (name != null) {
-            this.files.remove(fileId);
-        }
-
-        try {
-
-            // Prepare message
-            Message msg = new Message(Message.MessageType.DELETE, id, fileId, 0, 0, new byte[0]);
-
-            // Pack in datagram
-            DatagramPacket packet = msg.packit(MC.address, MC.port);
-
-            // Send message
-            MC.socket.send(packet);
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-    }
-
-    public void reclaim(int memory) throws RemoteException {
-
-        // Memory can't be lower than 0
-        if (memory < 0) {
-            return;
-        }
-
-        // Assign new limit
-        limit = memory;
-
-        try {
-
-            // Check need to delete chunks
-            // Going to delete by order in the map
-            while (used > limit) {
-
-                // Pick an entry
-                Map.Entry<String, Chunk> entry = chunks.entrySet().iterator().next();
-
-                // Remove from used space
-                used -= entry.getValue().bytes.length;
-
-                // Prepare message
-                Message msg = new Message(Message.MessageType.REMOVED, id, entry.getValue().fileId,
-                        entry.getValue().chunkN, 0, new byte[0]);
-
-                // Pack in datagram
-                DatagramPacket packet = msg.packit(MC.address, MC.port);
-
-                // Send message
-                MC.socket.send(packet);
-
-                // Remove from map
-                chunks.remove(entry.getKey());
-
-                // Remove from file system
-                Path current = Paths.get("");
-                String place = current.toAbsolutePath().toString() + "\\peer" + id + "\\backup\\"
-                        + entry.getValue().fileId + "\\chk" + entry.getValue().chunkN + ".chk";
-                File toDelete = new File(place);
-                toDelete.delete();
-
-            }
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-    }
-
-    public void state() throws RemoteException {
-
-    }
-
-    // Get information of previous peer life from file system
-    public void loadLocal() {
-
-        // Base path
-        Path currentRelativePath = Paths.get("");
-        String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + id;
-
-        // Backup path
-        File file = new File(peerDir + "\\backup");
-
-        // FileIds backed up
-        File[] files = file.listFiles(File::isDirectory);
-
-        // Peer didn't exist, just return
-        if (files == null)
-            return;
-
-        // Each file directory
-        for (int i = 0; i < files.length; i++) {
-
-            // FileId
-            String fileId = files[i].getName();
-
-            // Get Chunks inside
-            file = new File(peerDir + "\\backup" + "\\" + fileId);
-            File[] chunks = file.listFiles();
-
-            // No chunks, just return
-            if (chunks == null)
-                return;
-
-            // Each chunk
-            for (int j = 0; j < chunks.length; j++) {
-
-                // Chunk name
-                String chunkNo = chunks[j].getName();
-
-                // Remove chk
-                chunkNo = chunkNo.replaceAll("\\.", "");
-                chunkNo = chunkNo.replaceAll("chk", "");
-
-                try {
-
-                    // Read bytes
-                    byte[] bytes = Files.readAllBytes(chunks[j].toPath());
-
-                    // Updated used bytes
-                    used += bytes.length;
-
-                    // Add to HashMap
-                    this.chunks.put(fileId + "-" + chunkNo, new Chunk(fileId, Integer.parseInt(chunkNo), bytes, 0));
-
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-
-            }
-
-        }
-
-    }
-
-    // Setup directory
-    public void setupDirectory() {
-
-        // Base path
-        Path currentRelativePath = Paths.get("");
-        String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + id;
-
-        // Backup path
-        File file = new File(peerDir + "\\backup");
-        file.mkdirs();
-
-        // Backup path
-        file = new File(peerDir + "\\restored");
-        file.mkdirs();
-
-    }
-
-    // Default Socket Setup
-    public void setupSockets() throws IOException {
-
-        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10000);
-
-        MC = new Socket(this, Socket.Type.MC);
-        pool.execute(MC);
-
-        MDB = new Socket(this, Socket.Type.MDB);
-        pool.execute(MDB);
-
-        MDR = new Socket(this, Socket.Type.MDR);
-        pool.execute(MDR);
-
-    }
-
-    // Specifc Socket Setup
-    public void setupSockets(int portMC, String addressMC, int portMDB, String addressMDB, int portMDR,
-            String addressMDR) throws IOException {
-
-        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10000);
-
-        MC = new Socket(this, Socket.Type.MC, portMC, addressMC);
-        pool.execute(MC);
-
-        MDB = new Socket(this, Socket.Type.MDB, portMDB, addressMDB);
-        pool.execute(MDB);
-
-        MDR = new Socket(this, Socket.Type.MDR, portMDR, addressMDR);
-        pool.execute(MDR);
-
-    }
-
-    public static void main(String args[]) {
-
-        try {
-
-            // Server object
-            Server obj;
-
-            // Basic (version id)
-            if (args.length == 2) {
-                obj = new Server(args[1]);
-            }
-            // Full (version id access port mc port mdb port mdr)
-            else if (args.length == 9) {
-                obj = new Server(args[1], Integer.parseInt(args[3]), args[4], Integer.parseInt(args[5]), args[6],
-                        Integer.parseInt(args[7]), args[8]);
-            }
-            // Invalid arguments
-            else {
-                System.out.println("java Server <version> <server id>");
-                System.out.println(
-                        "java Server <version> <server id> <access_point> <MC_port> <MC_IP_address> <MDB_port> <MDB_IP_address> <MDR_port> <MDR_IP_address>");
-                return;
-            }
-
-            // Bind the server as a registry
-            PeerInterface stub = (PeerInterface) UnicastRemoteObject.exportObject(obj, 0);
-
-            // Bind the remote object's stub in the registry
-            Registry registry = LocateRegistry.getRegistry();
-            registry.bind(obj.id, stub);
-
-            System.err.println("Server ready");
-
-        } catch (Exception e) {
-
-            System.err.println("Server exception: " + e.toString());
-            e.printStackTrace();
-
-        }
-    }
-
-}
\ No newline at end of file
Index: Interpreter.java
===================================================================
--- Interpreter.java	(revision 2)
+++ Interpreter.java	(nonexistent)
@@ -1,337 +0,0 @@
-
-import java.util.Random;
-import java.rmi.registry.Registry;
-import java.rmi.registry.LocateRegistry;
-import java.rmi.RemoteException;
-import java.rmi.server.UnicastRemoteObject;
-
-import java.util.Map;
-
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.Executors;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Collections;
-
-import java.io.*;
-
-import java.net.DatagramPacket;
-
-import java.nio.file.*;
-
-public class Interpreter implements Runnable {
-
-    public Random rand = new Random();
-
-    private static final byte CR = 0xD;
-    private static final byte LF = 0xA;
-
-    public Server peer;
-
-    public String raw;
-
-    public String header;
-    public byte[] body;
-
-    public String version;
-    public String senderId;
-    public String fileId;
-    public String chunkNo;
-    public String replication;
-
-    public AtomicBoolean elseSentChunk = new AtomicBoolean(false);
-    public AtomicBoolean elseSentChunkReclaim = new AtomicBoolean(true);
-
-    public Message.MessageType type;
-
-    public Interpreter() {
-    }
-
-    public Interpreter(Server peer, DatagramPacket packet) {
-
-        // Set peer
-        this.peer = peer;
-
-        // Seperate header and body
-        messageSplitHeaderBody(packet.getData(), packet.getLength());
-
-        System.out.println(header);
-
-        // Get parts
-        String[] parts = header.split(" ");
-
-        // Identify type and specific arguments
-        switch (parts[0]) {
-        case "PUTCHUNK":
-            type = Message.MessageType.PUTCHUNK;
-            chunkNo = parts[4];
-            replication = parts[5];
-            break;
-        case "STORED":
-            type = Message.MessageType.STORED;
-            chunkNo = parts[4];
-            break;
-        case "GETCHUNK":
-            type = Message.MessageType.GETCHUNK;
-            chunkNo = parts[4];
-            break;
-        case "CHUNK":
-            type = Message.MessageType.CHUNK;
-            chunkNo = parts[4];
-            break;
-        case "DELETE":
-            type = Message.MessageType.DELETE;
-            break;
-        case "REMOVED":
-            type = Message.MessageType.REMOVED;
-            chunkNo = parts[4];
-            break;
-        default:
-            break;
-        }
-
-        // Version, SenderId and FileId are shared
-        version = parts[1];
-        senderId = parts[2];
-        fileId = parts[3];
-
-    }
-
-    // Split message header and body
-    public void messageSplitHeaderBody(byte[] message, int size) {
-
-        // Find index of CRLF
-        int i = 0;
-        for (i = 0; i < size; i++) {
-            if (i <= size - 5) {
-                if (message[i] == CR && message[i + 1] == LF && message[i + 2] == CR && message[i + 3] == LF) {
-                    break;
-                }
-            }
-        }
-
-        // Get header
-        byte[] headerByte = new byte[i];
-
-        System.arraycopy(message, 0, headerByte, 0, i);
-
-        header = new String(headerByte);
-        header = header.trim();
-
-        // Get body
-        if (size > i + 3) {
-            body = new byte[size - i - 4];
-            System.arraycopy(message, i + 4, body, 0, size - i - 4);
-        } else {
-            body = null;
-        }
-
-    }
-
-    @Override
-    public void run() {
-
-        // Do whatever the message asks
-        try {
-
-            // Identify Type
-            switch (type) {
-            case PUTCHUNK:
-
-                // Someone(NOT US!) sent a CHUNK, cancel it
-                if (this.elseSentChunkReclaim.get() && senderId != this.peer.id)
-                    this.elseSentChunkReclaim.set(true);
-
-                // Dont backup self
-                if (peer.doingBackUp)
-                    return;
-
-                // Check peer has enough size available
-                if (peer.used + body.length > peer.limit)
-                    return;
-
-                // Get path to peer
-                Path currentRelativePath = Paths.get("");
-                String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + peer.id;
-                peerDir += "\\backup";
-                peerDir += "\\" + fileId;
-                File file = new File(peerDir);
-
-                // Create path
-                file.mkdirs();
-
-                // Store chunk in file system
-                peerDir += "\\chk" + chunkNo + ".chk";
-                File chunk = new File(peerDir);
-                FileOutputStream out = new FileOutputStream(chunk);
-                out.write(body);
-                out.close();
-
-                // Store chunk in Peer cache(hashmap)
-                peer.chunks.put(fileId + "-" + chunkNo,
-                        new Chunk(fileId, Integer.parseInt(chunkNo), body, Integer.parseInt(replication)));
-
-                // Add to used memory
-                peer.used += body.length;
-
-                // Random delay
-                Thread.sleep(rand.nextInt(400));
-
-                // Send STORED to MC
-                Message msg = new Message(Message.MessageType.STORED, peer.id, fileId, Integer.parseInt(chunkNo), 0,
-                        new byte[0]);
-
-                DatagramPacket packet = msg.packit(Server.MC.address, Server.MC.port);
-
-                Server.MC.socket.send(packet);
-
-                break;
-            case STORED:
-
-                // If doing backup then add to replications
-                if (peer.doingBackUp && peer.file.equals(fileId)) {
-                    peer.replications++;
-                }
-
-                // Chunk that was stored
-                String keyStored = fileId + "-" + chunkNo;
-
-                // If we have the chunk
-                if (this.peer.chunks.get(keyStored) != null) {
-                    this.peer.chunks.get(keyStored).cReps++;
-                }
-
-                break;
-            case GETCHUNK:
-
-                // Find chunk in store
-                Chunk requestedChunk = peer.chunks.get(fileId + "-" + chunkNo);
-
-                // Chunk not found, just return
-                if (requestedChunk == null)
-                    return;
-
-                // Set flag to lookout for CHUNK
-                this.elseSentChunk.set(false);
-
-                // Wait random delay
-                Thread.sleep(rand.nextInt(400));
-
-                // Someone else sent the CHUNK, just return
-                if (this.elseSentChunk.get())
-                    return;
-
-                // Send CHUNK to MDR
-                Message msgGetChunk = new Message(Message.MessageType.CHUNK, peer.id, fileId, Integer.parseInt(chunkNo),
-                        0, requestedChunk.bytes);
-
-                DatagramPacket packetGetChunk = msgGetChunk.packit(Server.MDR.address, Server.MDR.port);
-
-                Server.MDR.socket.send(packetGetChunk);
-
-                break;
-            case CHUNK:
-
-                // Find chunk in store
-                Chunk receivedChunk = peer.chunks.get(fileId + "-" + chunkNo);
-
-                // Someone(NOT US!) sent a CHUNK we were going to send, cancel it
-                if (receivedChunk != null && senderId != this.peer.id) {
-                    this.elseSentChunk.set(true);
-                }
-
-                // Add to recovered if not there yet
-                if (this.peer.restoring) {
-                    if (!this.peer.recoveredChunks.containsKey(chunkNo)) {
-                        this.peer.recoveredChunks.put(chunkNo, new Chunk(fileId, Integer.parseInt(chunkNo), body, 0));
-                        // Chunk received, continue asking for rest
-                        if (peer.waitRestoreReply.get())
-                            peer.waitRestoreReply.set(false);
-                    }
-
-                }
-
-                break;
-            case DELETE:
-
-                boolean found1 = false;
-
-                // Delete cache chunks
-                for (String keyDelete : this.peer.chunks.keySet()) {
-                    if (keyDelete.contains(this.fileId)) {
-                        found1 = true;
-                        this.peer.chunks.remove(keyDelete);
-                    }
-                }
-
-                // Delete filesystem chunks
-                // If at least one in cache was found then surely it exists in file system and
-                // we should delete
-                if (found1) {
-                    Path current = Paths.get("");
-                    String fileDir = current.toAbsolutePath().toString() + "\\peer" + peer.id;
-                    fileDir += "\\backup";
-                    fileDir += "\\" + fileId;
-                    File fileDirFile = new File(fileDir);
-                    FileManager.deleteDir(fileDirFile);
-                }
-
-                break;
-            case REMOVED:
-
-                // Chunk requested
-                String key = fileId + "-" + chunkNo;
-
-                // If we have the chunk
-                if (this.peer.chunks.get(key) != null) {
-
-                    // Remove replication
-                    this.peer.chunks.get(key).cReps--;
-
-                    System.out.println(this.peer.chunks.get(key).cReps);
-                    System.out.println(this.peer.chunks.get(key).reps);
-
-                    // If bellow replication degree
-                    if (this.peer.chunks.get(key).cReps < this.peer.chunks.get(key).reps) {
-                        // Wait random delay
-                        this.elseSentChunkReclaim.set(false);
-                        Thread.sleep(rand.nextInt(400));
-
-                        // Someone already started backup of this chunk
-                        if (this.elseSentChunkReclaim.get())
-                            return;
-
-                        // Prepare message
-                        Message msgRemovedChunk = new Message(Message.MessageType.PUTCHUNK, this.peer.id, fileId,
-                                this.peer.chunks.get(key).chunkN, this.peer.chunks.get(key).reps,
-                                this.peer.chunks.get(key).bytes);
-
-                        // Pack in datagram
-                        DatagramPacket packetRemovedChunk = msgRemovedChunk.packit(this.peer.MDB.address,
-                                this.peer.MDB.port);
-
-                        // Send message
-                        this.peer.MDB.socket.send(packetRemovedChunk);
-                    }
-
-                }
-
-                break;
-
-            default:
-                break;
-            }
-
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-    }
-
-}
Index: FileManager.java
===================================================================
--- FileManager.java	(revision 2)
+++ FileManager.java	(nonexistent)
@@ -1,176 +0,0 @@
-import java.io.*;
-import java.nio.file.*;
-import java.security.*;
-import java.nio.charset.*;
-
-public class FileManager {
-
-    public static int chunkSize = 64000;
-
-    // Save byte array chunk to path
-    public static void saveChunk(byte[] bytes, String path, int chunkN) throws FileNotFoundException, IOException {
-
-        String fileName = path + Integer.toString(chunkN);
-
-        try (FileOutputStream stream = new FileOutputStream(fileName)) {
-            stream.write(bytes);
-        }
-
-    }
-
-    // Hash a string with SHA256
-    public static String hash(String input) {
-
-        try {
-
-            byte[] bytes = digestHashing(input);
-            return bytesToHex(bytes);
-
-        } catch (Exception e) {
-
-            e.printStackTrace();
-
-        }
-
-        return null;
-
-    }
-
-    // Use MessageDigest to hash in bytes
-    private static byte[] digestHashing(String input) throws NoSuchAlgorithmException {
-
-        MessageDigest digest = MessageDigest.getInstance("SHA-256");
-        return digest.digest(input.getBytes(StandardCharsets.UTF_8));
-
-    }
-
-    // Use bytes from MessageDigest to a String
-    private static String bytesToHex(byte[] hash) {
-
-        StringBuffer hexString = new StringBuffer();
-
-        for (int i = 0; i < hash.length; i++) {
-
-            String hex = Integer.toHexString(0xff & hash[i]);
-
-            if (hex.length() == 1)
-                hexString.append('0');
-
-            hexString.append(hex);
-
-        }
-
-        return hexString.toString();
-
-    }
-
-    // Delete directory and files
-    public static void deleteDir(File file) {
-        File[] contents = file.listFiles();
-        if (contents != null) {
-            for (File f : contents) {
-                if (!Files.isSymbolicLink(f.toPath())) {
-                    deleteDir(f);
-                }
-            }
-        }
-        file.delete();
-    }
-
-    // Read all bytes of file(generic use)
-    public static byte[] readFile(String path) {
-
-        Path filePath = Paths.get(path);
-
-        byte[] bytes = null;
-
-        try {
-            bytes = Files.readAllBytes(filePath);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        return bytes;
-    }
-
-    // Split file in chunks
-    public static byte[][] split(byte[] fileBytes) {
-
-        // Number of chunks needed is always integer division plus 1 which may be
-        // partially used or empty
-        int nChunks = 1 + fileBytes.length / chunkSize;
-
-        // Total bytes
-        int bytes = fileBytes.length;
-        int rBytes = 0;
-
-        // Chunks seperated
-        byte[][] chunks = new byte[nChunks][];
-        for (int i = 0; i < nChunks; i++) {
-
-            // Calculate proper size
-            int sizeToCopy = chunkSize;
-            if (bytes < sizeToCopy)
-                sizeToCopy = bytes;
-
-            // Chunk with proper size
-            chunks[i] = new byte[sizeToCopy];
-
-            // Copy to chunk
-            System.arraycopy(fileBytes, rBytes, chunks[i], 0, sizeToCopy);
-
-            // Remove form total bytes
-            bytes -= sizeToCopy;
-            rBytes += sizeToCopy;
-
-        }
-        return chunks;
-    }
-
-    // Merge chunks into single byte array
-    public static byte[] merge(byte[][] chunks) throws IOException {
-
-        // Total bytes
-        int tBytes = 0;
-        for (int i = 0; i < chunks.length; i++) {
-            tBytes += chunks[i].length;
-        }
-
-        byte[] mergedFile = new byte[tBytes];
-
-        // Written bytes count
-        int nBytes = 0;
-
-        // Write bytes
-        for (int i = 0; i < chunks.length; i++) {
-            System.arraycopy(chunks[i], 0, mergedFile, nBytes, chunks[i].length);
-            nBytes += chunks[i].length;
-        }
-
-        return mergedFile;
-    }
-
-    // Test main for split and merge file
-    public static void main(String[] args) {
-
-        String fileName = args[0];
-        Path filePath = Paths.get(fileName);
-        byte[] fileBytes = null;
-
-        try {
-            fileBytes = Files.readAllBytes(filePath);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-        byte[][] chunks = split(fileBytes);
-
-        try {
-            fileBytes = merge(chunks);
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-
-    }
-
-}
\ No newline at end of file
Index: Client.java
===================================================================
--- Client.java	(revision 2)
+++ Client.java	(nonexistent)
@@ -1,98 +0,0 @@
-
-import java.rmi.registry.LocateRegistry;
-import java.rmi.registry.Registry;
-
-public class Client {
-
-    public static void main(String[] args) {
-
-        // Command parameters
-        String[] opnd = { "operand", "opnd1", "opnd2" };
-
-        // Process Arguments
-
-        // 2 arguments can only be STATE check
-        if (args.length == 2) {
-            opnd[0] = "STATE";
-        }
-        // 3 arguments can only be RECLAIM/DELETE/RESTORE
-        else if (args.length == 3) {
-            switch (args[1]) {
-            case "RESTORE":
-                opnd[0] = "RESTORE";
-                // File
-                opnd[1] = args[2];
-                break;
-            case "DELETE":
-                opnd[0] = "DELETE";
-                // File
-                opnd[1] = args[2];
-                break;
-            case "RECLAIM":
-                opnd[0] = "RECLAIM";
-                // Reclaim size
-                opnd[1] = args[2];
-                break;
-            default:
-                System.out.println("Error: Invalid operation name");
-                return;
-            }
-        }
-        // 4 arguments can only be BACKUP
-        else if (args.length == 4) {
-            opnd[0] = "BACKUP";
-            // File
-            opnd[1] = args[2];
-            // Replication level
-            opnd[2] = args[3];
-        }
-        // Any more arguments are invalid too
-        else {
-            System.out.println("java Client <peer_ap> <protocol> <opnd_1> <opnd_2");
-            return;
-        }
-
-        // Host id
-        String[] host = new String(args[0]).split("/");
-
-        try {
-
-            PeerInterface stub;
-
-            // Lookup for RMI
-            if(host.length > 2){
-                Registry registry = LocateRegistry.getRegistry(host[2]);
-                stub = (PeerInterface) registry.lookup(host[3]);
-            }else{
-                Registry registry = LocateRegistry.getRegistry();
-                stub = (PeerInterface) registry.lookup(args[0]);
-            }                     
-
-            // Identify action
-            switch (opnd[0]) {
-            case "BACKUP":
-                stub.backup(opnd[1], Integer.parseInt(opnd[2]));
-                break;
-            case "RESTORE":
-                stub.restore(opnd[1]);
-                break;
-            case "DELETE":
-                stub.delete(opnd[1]);
-                break;
-            case "RECLAIM":
-                stub.reclaim(Integer.parseInt(opnd[1]));
-                break;
-            default:
-                break;
-            }
-
-        } catch (Exception e) {
-
-            System.err.println("Client exception: " + e.toString());
-            e.printStackTrace();
-
-        }
-
-    }
-
-}
\ No newline at end of file
Index: Chunk.java
===================================================================
--- Chunk.java	(revision 2)
+++ Chunk.java	(nonexistent)
@@ -1,17 +0,0 @@
-
-public class Chunk {
-
-    public String fileId;
-    public int chunkN;
-    public byte[] bytes;
-    public int reps;
-    public int cReps = 0;
-
-    public Chunk(String fileId, int chunkN, byte[] bytes, int reps) {
-        this.fileId = fileId;
-        this.chunkN = chunkN;
-        this.bytes = bytes;
-        this.reps = reps;
-    }
-
-}
\ No newline at end of file
Index: Socket.java
===================================================================
--- Socket.java	(revision 2)
+++ Socket.java	(nonexistent)
@@ -1,89 +0,0 @@
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.InetAddress;
-import java.net.MulticastSocket;
-import java.util.AbstractMap.SimpleEntry;
-
-public class Socket implements Runnable {
-
-    public MulticastSocket socket;
-    public int port;
-    public InetAddress address;
-
-    public enum Type {
-        MC, MDB, MDR
-    }
-
-    public Type type;
-
-    public Server peer;
-
-    // Message size(safe size)
-    public static final int MESSAGE_SIZE = 65500;
-
-    // Default constructor
-    public Socket(Server peer, Type type) throws IOException {
-
-        this.peer = peer;
-
-        switch (type) {
-        case MC:
-            this.port = 8000;
-            this.address = InetAddress.getByName("224.0.0.1");
-            break;
-        case MDB:
-            this.port = 8001;
-            this.address = InetAddress.getByName("224.0.0.2");
-            break;
-        case MDR:
-            this.port = 8002;
-            this.address = InetAddress.getByName("224.0.0.3");
-            break;
-        default:
-            return;
-        }
-
-        this.type = type;
-        this.socket = new MulticastSocket(this.port);
-        this.socket.joinGroup(address);
-
-    }
-
-    // Specific constructor
-    public Socket(Server peer, Type type, int port, String address) throws IOException {
-
-        this.peer = peer;
-        this.type = type;
-        this.port = port;
-        this.address = InetAddress.getByName(address);
-        this.socket = new MulticastSocket(this.port);
-        this.socket.joinGroup(this.address);
-
-    }
-
-    @Override
-    public void run() {
-
-        while (true) {
-
-            byte[] buf = new byte[MESSAGE_SIZE];
-            DatagramPacket packet = new DatagramPacket(buf, buf.length);
-
-            try {
-
-                this.socket.receive(packet);
-
-                System.out.println("Packet received, launching interpreter");
-
-                Server.pool.execute(new Interpreter(peer, packet));
-
-            } catch (IOException e) {
-                e.printStackTrace();
-            }
-
-        }
-
-    }
-
-}
Index: PeerInterface.java
===================================================================
--- PeerInterface.java	(revision 2)
+++ PeerInterface.java	(nonexistent)
@@ -1,17 +0,0 @@
-
-import java.rmi.Remote;
-import java.rmi.RemoteException;
-
-public interface PeerInterface extends Remote {
-
-    void backup(String path, int replications) throws RemoteException;
-
-    void restore(String path) throws RemoteException;
-
-    void state() throws RemoteException;
-
-    void reclaim(int memory) throws RemoteException;
-
-    void delete(String path) throws RemoteException;
-
-}
\ No newline at end of file
Index: Message.java
===================================================================
--- Message.java	(revision 2)
+++ Message.java	(nonexistent)
@@ -1,110 +0,0 @@
-import java.net.DatagramPacket;
-import java.net.InetAddress;
-
-public class Message {
-
-    private static final String CRLF = "\r\n";
-
-    public String header;
-    public byte[] body;
-
-    public enum MessageType {
-        PUTCHUNK, STORED, GETCHUNK, CHUNK, DELETE, REMOVED
-    };
-
-    public MessageType type;
-
-    public Message() {
-    }
-
-    // Build a message
-    public Message(MessageType type, String senderId, String fileId, int chunkN, int replicationDeg, byte[] bytes) {
-
-        // Reset body
-        body = new byte[0];
-
-        // Add chunk
-        body = bytes;
-
-        // Assign type
-        this.type = type;
-
-        // Prepare type
-        switch (type) {
-        case PUTCHUNK:
-            header = "PUTCHUNK";
-            break;
-        case STORED:
-            header = "STORED";
-            break;
-        case GETCHUNK:
-            header = "GETCHUNK";
-            break;
-        case CHUNK:
-            header = "CHUNK";
-            break;
-        case DELETE:
-            header = "DELETE";
-            break;
-        case REMOVED:
-            header = "REMOVED";
-            break;
-        default:
-            break;
-        }
-
-        // Version, SenderId and FileId are shared
-        // TODO hardcoded version 1.0
-        header += " 1.0";
-        header += " " + senderId;
-        header += " " + fileId;
-
-        // Remaining message header
-        // Technically it would be more efficient to add chunkN to all types and remove
-        // in DELETE but eh
-        switch (type) {
-        case PUTCHUNK:
-            header += " " + Integer.toString(chunkN);
-            header += " " + Integer.toString(replicationDeg);
-            break;
-        case STORED:
-            header += " " + Integer.toString(chunkN);
-            break;
-        case GETCHUNK:
-            header += " " + Integer.toString(chunkN);
-            break;
-        case CHUNK:
-            header += " " + Integer.toString(chunkN);
-            break;
-        case DELETE:
-            // Nothing to add
-            break;
-        case REMOVED:
-            header += " " + Integer.toString(chunkN);
-            break;
-        default:
-            break;
-        }
-
-        // Finalize with double CRLF
-        header += CRLF + CRLF;
-
-    }
-
-    // Pack a datagram from message
-    public DatagramPacket packit(InetAddress address, int port) {
-
-        byte[] headerBytes = header.getBytes();
-
-        byte[] full = new byte[headerBytes.length + body.length];
-
-        System.arraycopy(headerBytes, 0, full, 0, headerBytes.length);
-        System.arraycopy(body, 0, full, headerBytes.length, body.length);
-
-        DatagramPacket packet = new DatagramPacket(full, full.length, address, port);
-
-        return packet;
-
-    }
-
-}
\ No newline at end of file
Index: src/Chunk.java
===================================================================
--- src/Chunk.java	(nonexistent)
+++ src/Chunk.java	(revision 3)
@@ -0,0 +1,17 @@
+
+public class Chunk {
+
+    public String fileId;
+    public int chunkN;
+    public byte[] bytes;
+    public int reps;
+    public int cReps = 0;
+
+    public Chunk(String fileId, int chunkN, byte[] bytes, int reps) {
+        this.fileId = fileId;
+        this.chunkN = chunkN;
+        this.bytes = bytes;
+        this.reps = reps;
+    }
+
+}
\ No newline at end of file
Index: src/Client.java
===================================================================
--- src/Client.java	(nonexistent)
+++ src/Client.java	(revision 3)
@@ -0,0 +1,98 @@
+
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+
+public class Client {
+
+    public static void main(String[] args) {
+
+        // Command parameters
+        String[] opnd = { "operand", "opnd1", "opnd2" };
+
+        // Process Arguments
+
+        // 2 arguments can only be STATE check
+        if (args.length == 2) {
+            opnd[0] = "STATE";
+        }
+        // 3 arguments can only be RECLAIM/DELETE/RESTORE
+        else if (args.length == 3) {
+            switch (args[1]) {
+            case "RESTORE":
+                opnd[0] = "RESTORE";
+                // File
+                opnd[1] = args[2];
+                break;
+            case "DELETE":
+                opnd[0] = "DELETE";
+                // File
+                opnd[1] = args[2];
+                break;
+            case "RECLAIM":
+                opnd[0] = "RECLAIM";
+                // Reclaim size
+                opnd[1] = args[2];
+                break;
+            default:
+                System.out.println("Error: Invalid operation name");
+                return;
+            }
+        }
+        // 4 arguments can only be BACKUP
+        else if (args.length == 4) {
+            opnd[0] = "BACKUP";
+            // File
+            opnd[1] = args[2];
+            // Replication level
+            opnd[2] = args[3];
+        }
+        // Any more arguments are invalid too
+        else {
+            System.out.println("java Client <peer_ap> <protocol> <opnd_1> <opnd_2");
+            return;
+        }
+
+        // Host id
+        String[] host = new String(args[0]).split("/");
+
+        try {
+
+            PeerInterface stub;
+
+            // Lookup for RMI
+            if(host.length > 2){
+                Registry registry = LocateRegistry.getRegistry(host[2]);
+                stub = (PeerInterface) registry.lookup(host[3]);
+            }else{
+                Registry registry = LocateRegistry.getRegistry();
+                stub = (PeerInterface) registry.lookup(args[0]);
+            }                     
+
+            // Identify action
+            switch (opnd[0]) {
+            case "BACKUP":
+                stub.backup(opnd[1], Integer.parseInt(opnd[2]));
+                break;
+            case "RESTORE":
+                stub.restore(opnd[1]);
+                break;
+            case "DELETE":
+                stub.delete(opnd[1]);
+                break;
+            case "RECLAIM":
+                stub.reclaim(Integer.parseInt(opnd[1]));
+                break;
+            default:
+                break;
+            }
+
+        } catch (Exception e) {
+
+            System.err.println("Client exception: " + e.toString());
+            e.printStackTrace();
+
+        }
+
+    }
+
+}
\ No newline at end of file
Index: src/FileManager.java
===================================================================
--- src/FileManager.java	(nonexistent)
+++ src/FileManager.java	(revision 3)
@@ -0,0 +1,176 @@
+import java.io.*;
+import java.nio.file.*;
+import java.security.*;
+import java.nio.charset.*;
+
+public class FileManager {
+
+    public static int chunkSize = 64000;
+
+    // Save byte array chunk to path
+    public static void saveChunk(byte[] bytes, String path, int chunkN) throws FileNotFoundException, IOException {
+
+        String fileName = path + Integer.toString(chunkN);
+
+        try (FileOutputStream stream = new FileOutputStream(fileName)) {
+            stream.write(bytes);
+        }
+
+    }
+
+    // Hash a string with SHA256
+    public static String hash(String input) {
+
+        try {
+
+            byte[] bytes = digestHashing(input);
+            return bytesToHex(bytes);
+
+        } catch (Exception e) {
+
+            e.printStackTrace();
+
+        }
+
+        return null;
+
+    }
+
+    // Use MessageDigest to hash in bytes
+    private static byte[] digestHashing(String input) throws NoSuchAlgorithmException {
+
+        MessageDigest digest = MessageDigest.getInstance("SHA-256");
+        return digest.digest(input.getBytes(StandardCharsets.UTF_8));
+
+    }
+
+    // Use bytes from MessageDigest to a String
+    private static String bytesToHex(byte[] hash) {
+
+        StringBuffer hexString = new StringBuffer();
+
+        for (int i = 0; i < hash.length; i++) {
+
+            String hex = Integer.toHexString(0xff & hash[i]);
+
+            if (hex.length() == 1)
+                hexString.append('0');
+
+            hexString.append(hex);
+
+        }
+
+        return hexString.toString();
+
+    }
+
+    // Delete directory and files
+    public static void deleteDir(File file) {
+        File[] contents = file.listFiles();
+        if (contents != null) {
+            for (File f : contents) {
+                if (!Files.isSymbolicLink(f.toPath())) {
+                    deleteDir(f);
+                }
+            }
+        }
+        file.delete();
+    }
+
+    // Read all bytes of file(generic use)
+    public static byte[] readFile(String path) {
+
+        Path filePath = Paths.get(path);
+
+        byte[] bytes = null;
+
+        try {
+            bytes = Files.readAllBytes(filePath);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return bytes;
+    }
+
+    // Split file in chunks
+    public static byte[][] split(byte[] fileBytes) {
+
+        // Number of chunks needed is always integer division plus 1 which may be
+        // partially used or empty
+        int nChunks = 1 + fileBytes.length / chunkSize;
+
+        // Total bytes
+        int bytes = fileBytes.length;
+        int rBytes = 0;
+
+        // Chunks seperated
+        byte[][] chunks = new byte[nChunks][];
+        for (int i = 0; i < nChunks; i++) {
+
+            // Calculate proper size
+            int sizeToCopy = chunkSize;
+            if (bytes < sizeToCopy)
+                sizeToCopy = bytes;
+
+            // Chunk with proper size
+            chunks[i] = new byte[sizeToCopy];
+
+            // Copy to chunk
+            System.arraycopy(fileBytes, rBytes, chunks[i], 0, sizeToCopy);
+
+            // Remove form total bytes
+            bytes -= sizeToCopy;
+            rBytes += sizeToCopy;
+
+        }
+        return chunks;
+    }
+
+    // Merge chunks into single byte array
+    public static byte[] merge(byte[][] chunks) throws IOException {
+
+        // Total bytes
+        int tBytes = 0;
+        for (int i = 0; i < chunks.length; i++) {
+            tBytes += chunks[i].length;
+        }
+
+        byte[] mergedFile = new byte[tBytes];
+
+        // Written bytes count
+        int nBytes = 0;
+
+        // Write bytes
+        for (int i = 0; i < chunks.length; i++) {
+            System.arraycopy(chunks[i], 0, mergedFile, nBytes, chunks[i].length);
+            nBytes += chunks[i].length;
+        }
+
+        return mergedFile;
+    }
+
+    // Test main for split and merge file
+    public static void main(String[] args) {
+
+        String fileName = args[0];
+        Path filePath = Paths.get(fileName);
+        byte[] fileBytes = null;
+
+        try {
+            fileBytes = Files.readAllBytes(filePath);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        byte[][] chunks = split(fileBytes);
+
+        try {
+            fileBytes = merge(chunks);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+}
\ No newline at end of file
Index: src/Interpreter.java
===================================================================
--- src/Interpreter.java	(nonexistent)
+++ src/Interpreter.java	(revision 3)
@@ -0,0 +1,337 @@
+
+import java.util.Random;
+import java.rmi.registry.Registry;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+import java.util.Map;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executors;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Collections;
+
+import java.io.*;
+
+import java.net.DatagramPacket;
+
+import java.nio.file.*;
+
+public class Interpreter implements Runnable {
+
+    public Random rand = new Random();
+
+    private static final byte CR = 0xD;
+    private static final byte LF = 0xA;
+
+    public Server peer;
+
+    public String raw;
+
+    public String header;
+    public byte[] body;
+
+    public String version;
+    public String senderId;
+    public String fileId;
+    public String chunkNo;
+    public String replication;
+
+    public AtomicBoolean elseSentChunk = new AtomicBoolean(false);
+    public AtomicBoolean elseSentChunkReclaim = new AtomicBoolean(true);
+
+    public Message.MessageType type;
+
+    public Interpreter() {
+    }
+
+    public Interpreter(Server peer, DatagramPacket packet) {
+
+        // Set peer
+        this.peer = peer;
+
+        // Seperate header and body
+        messageSplitHeaderBody(packet.getData(), packet.getLength());
+
+        System.out.println(header);
+
+        // Get parts
+        String[] parts = header.split(" ");
+
+        // Identify type and specific arguments
+        switch (parts[0]) {
+        case "PUTCHUNK":
+            type = Message.MessageType.PUTCHUNK;
+            chunkNo = parts[4];
+            replication = parts[5];
+            break;
+        case "STORED":
+            type = Message.MessageType.STORED;
+            chunkNo = parts[4];
+            break;
+        case "GETCHUNK":
+            type = Message.MessageType.GETCHUNK;
+            chunkNo = parts[4];
+            break;
+        case "CHUNK":
+            type = Message.MessageType.CHUNK;
+            chunkNo = parts[4];
+            break;
+        case "DELETE":
+            type = Message.MessageType.DELETE;
+            break;
+        case "REMOVED":
+            type = Message.MessageType.REMOVED;
+            chunkNo = parts[4];
+            break;
+        default:
+            break;
+        }
+
+        // Version, SenderId and FileId are shared
+        version = parts[1];
+        senderId = parts[2];
+        fileId = parts[3];
+
+    }
+
+    // Split message header and body
+    public void messageSplitHeaderBody(byte[] message, int size) {
+
+        // Find index of CRLF
+        int i = 0;
+        for (i = 0; i < size; i++) {
+            if (i <= size - 5) {
+                if (message[i] == CR && message[i + 1] == LF && message[i + 2] == CR && message[i + 3] == LF) {
+                    break;
+                }
+            }
+        }
+
+        // Get header
+        byte[] headerByte = new byte[i];
+
+        System.arraycopy(message, 0, headerByte, 0, i);
+
+        header = new String(headerByte);
+        header = header.trim();
+
+        // Get body
+        if (size > i + 3) {
+            body = new byte[size - i - 4];
+            System.arraycopy(message, i + 4, body, 0, size - i - 4);
+        } else {
+            body = null;
+        }
+
+    }
+
+    @Override
+    public void run() {
+
+        // Do whatever the message asks
+        try {
+
+            // Identify Type
+            switch (type) {
+            case PUTCHUNK:
+
+                // Someone(NOT US!) sent a CHUNK, cancel it
+                if (this.elseSentChunkReclaim.get() && senderId != this.peer.id)
+                    this.elseSentChunkReclaim.set(true);
+
+                // Dont backup self
+                if (peer.doingBackUp)
+                    return;
+
+                // Check peer has enough size available
+                if (peer.used + body.length > peer.limit)
+                    return;
+
+                // Get path to peer
+                Path currentRelativePath = Paths.get("");
+                String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + peer.id;
+                peerDir += "\\backup";
+                peerDir += "\\" + fileId;
+                File file = new File(peerDir);
+
+                // Create path
+                file.mkdirs();
+
+                // Store chunk in file system
+                peerDir += "\\chk" + chunkNo + ".chk";
+                File chunk = new File(peerDir);
+                FileOutputStream out = new FileOutputStream(chunk);
+                out.write(body);
+                out.close();
+
+                // Store chunk in Peer cache(hashmap)
+                peer.chunks.put(fileId + "-" + chunkNo,
+                        new Chunk(fileId, Integer.parseInt(chunkNo), body, Integer.parseInt(replication)));
+
+                // Add to used memory
+                peer.used += body.length;
+
+                // Random delay
+                Thread.sleep(rand.nextInt(400));
+
+                // Send STORED to MC
+                Message msg = new Message(Message.MessageType.STORED, peer.id, fileId, Integer.parseInt(chunkNo), 0,
+                        new byte[0]);
+
+                DatagramPacket packet = msg.packit(Server.MC.address, Server.MC.port);
+
+                Server.MC.socket.send(packet);
+
+                break;
+            case STORED:
+
+                // If doing backup then add to replications
+                if (peer.doingBackUp && peer.file.equals(fileId)) {
+                    peer.replications++;
+                }
+
+                // Chunk that was stored
+                String keyStored = fileId + "-" + chunkNo;
+
+                // If we have the chunk
+                if (this.peer.chunks.get(keyStored) != null) {
+                    this.peer.chunks.get(keyStored).cReps++;
+                }
+
+                break;
+            case GETCHUNK:
+
+                // Find chunk in store
+                Chunk requestedChunk = peer.chunks.get(fileId + "-" + chunkNo);
+
+                // Chunk not found, just return
+                if (requestedChunk == null)
+                    return;
+
+                // Set flag to lookout for CHUNK
+                this.elseSentChunk.set(false);
+
+                // Wait random delay
+                Thread.sleep(rand.nextInt(400));
+
+                // Someone else sent the CHUNK, just return
+                if (this.elseSentChunk.get())
+                    return;
+
+                // Send CHUNK to MDR
+                Message msgGetChunk = new Message(Message.MessageType.CHUNK, peer.id, fileId, Integer.parseInt(chunkNo),
+                        0, requestedChunk.bytes);
+
+                DatagramPacket packetGetChunk = msgGetChunk.packit(Server.MDR.address, Server.MDR.port);
+
+                Server.MDR.socket.send(packetGetChunk);
+
+                break;
+            case CHUNK:
+
+                // Find chunk in store
+                Chunk receivedChunk = peer.chunks.get(fileId + "-" + chunkNo);
+
+                // Someone(NOT US!) sent a CHUNK we were going to send, cancel it
+                if (receivedChunk != null && senderId != this.peer.id) {
+                    this.elseSentChunk.set(true);
+                }
+
+                // Add to recovered if not there yet
+                if (this.peer.restoring) {
+                    if (!this.peer.recoveredChunks.containsKey(chunkNo)) {
+                        this.peer.recoveredChunks.put(chunkNo, new Chunk(fileId, Integer.parseInt(chunkNo), body, 0));
+                        // Chunk received, continue asking for rest
+                        if (peer.waitRestoreReply.get())
+                            peer.waitRestoreReply.set(false);
+                    }
+
+                }
+
+                break;
+            case DELETE:
+
+                boolean found1 = false;
+
+                // Delete cache chunks
+                for (String keyDelete : this.peer.chunks.keySet()) {
+                    if (keyDelete.contains(this.fileId)) {
+                        found1 = true;
+                        this.peer.chunks.remove(keyDelete);
+                    }
+                }
+
+                // Delete filesystem chunks
+                // If at least one in cache was found then surely it exists in file system and
+                // we should delete
+                if (found1) {
+                    Path current = Paths.get("");
+                    String fileDir = current.toAbsolutePath().toString() + "\\peer" + peer.id;
+                    fileDir += "\\backup";
+                    fileDir += "\\" + fileId;
+                    File fileDirFile = new File(fileDir);
+                    FileManager.deleteDir(fileDirFile);
+                }
+
+                break;
+            case REMOVED:
+
+                // Chunk requested
+                String key = fileId + "-" + chunkNo;
+
+                // If we have the chunk
+                if (this.peer.chunks.get(key) != null) {
+
+                    // Remove replication
+                    this.peer.chunks.get(key).cReps--;
+
+                    System.out.println(this.peer.chunks.get(key).cReps);
+                    System.out.println(this.peer.chunks.get(key).reps);
+
+                    // If bellow replication degree
+                    if (this.peer.chunks.get(key).cReps < this.peer.chunks.get(key).reps) {
+                        // Wait random delay
+                        this.elseSentChunkReclaim.set(false);
+                        Thread.sleep(rand.nextInt(400));
+
+                        // Someone already started backup of this chunk
+                        if (this.elseSentChunkReclaim.get())
+                            return;
+
+                        // Prepare message
+                        Message msgRemovedChunk = new Message(Message.MessageType.PUTCHUNK, this.peer.id, fileId,
+                                this.peer.chunks.get(key).chunkN, this.peer.chunks.get(key).reps,
+                                this.peer.chunks.get(key).bytes);
+
+                        // Pack in datagram
+                        DatagramPacket packetRemovedChunk = msgRemovedChunk.packit(this.peer.MDB.address,
+                                this.peer.MDB.port);
+
+                        // Send message
+                        this.peer.MDB.socket.send(packetRemovedChunk);
+                    }
+
+                }
+
+                break;
+
+            default:
+                break;
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+}
Index: src/Message.java
===================================================================
--- src/Message.java	(nonexistent)
+++ src/Message.java	(revision 3)
@@ -0,0 +1,110 @@
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+
+public class Message {
+
+    private static final String CRLF = "\r\n";
+
+    public String header;
+    public byte[] body;
+
+    public enum MessageType {
+        PUTCHUNK, STORED, GETCHUNK, CHUNK, DELETE, REMOVED
+    };
+
+    public MessageType type;
+
+    public Message() {
+    }
+
+    // Build a message
+    public Message(MessageType type, String senderId, String fileId, int chunkN, int replicationDeg, byte[] bytes) {
+
+        // Reset body
+        body = new byte[0];
+
+        // Add chunk
+        body = bytes;
+
+        // Assign type
+        this.type = type;
+
+        // Prepare type
+        switch (type) {
+        case PUTCHUNK:
+            header = "PUTCHUNK";
+            break;
+        case STORED:
+            header = "STORED";
+            break;
+        case GETCHUNK:
+            header = "GETCHUNK";
+            break;
+        case CHUNK:
+            header = "CHUNK";
+            break;
+        case DELETE:
+            header = "DELETE";
+            break;
+        case REMOVED:
+            header = "REMOVED";
+            break;
+        default:
+            break;
+        }
+
+        // Version, SenderId and FileId are shared
+        // TODO hardcoded version 1.0
+        header += " 1.0";
+        header += " " + senderId;
+        header += " " + fileId;
+
+        // Remaining message header
+        // Technically it would be more efficient to add chunkN to all types and remove
+        // in DELETE but eh
+        switch (type) {
+        case PUTCHUNK:
+            header += " " + Integer.toString(chunkN);
+            header += " " + Integer.toString(replicationDeg);
+            break;
+        case STORED:
+            header += " " + Integer.toString(chunkN);
+            break;
+        case GETCHUNK:
+            header += " " + Integer.toString(chunkN);
+            break;
+        case CHUNK:
+            header += " " + Integer.toString(chunkN);
+            break;
+        case DELETE:
+            // Nothing to add
+            break;
+        case REMOVED:
+            header += " " + Integer.toString(chunkN);
+            break;
+        default:
+            break;
+        }
+
+        // Finalize with double CRLF
+        header += CRLF + CRLF;
+
+    }
+
+    // Pack a datagram from message
+    public DatagramPacket packit(InetAddress address, int port) {
+
+        byte[] headerBytes = header.getBytes();
+
+        byte[] full = new byte[headerBytes.length + body.length];
+
+        System.arraycopy(headerBytes, 0, full, 0, headerBytes.length);
+        System.arraycopy(body, 0, full, headerBytes.length, body.length);
+
+        DatagramPacket packet = new DatagramPacket(full, full.length, address, port);
+
+        return packet;
+
+    }
+
+}
\ No newline at end of file
Index: src/PeerInterface.java
===================================================================
--- src/PeerInterface.java	(nonexistent)
+++ src/PeerInterface.java	(revision 3)
@@ -0,0 +1,17 @@
+
+import java.rmi.Remote;
+import java.rmi.RemoteException;
+
+public interface PeerInterface extends Remote {
+
+    void backup(String path, int replications) throws RemoteException;
+
+    void restore(String path) throws RemoteException;
+
+    void state() throws RemoteException;
+
+    void reclaim(int memory) throws RemoteException;
+
+    void delete(String path) throws RemoteException;
+
+}
\ No newline at end of file
Index: src/Server.java
===================================================================
--- src/Server.java	(nonexistent)
+++ src/Server.java	(revision 3)
@@ -0,0 +1,489 @@
+import java.rmi.registry.Registry;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.RemoteException;
+import java.rmi.server.UnicastRemoteObject;
+
+import java.util.Map;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Executors;
+import java.util.concurrent.CopyOnWriteArrayList;;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Collections;
+
+import java.io.*;
+
+import java.net.DatagramPacket;
+
+import java.nio.file.*;
+
+//Server class essentially a Peer
+public class Server implements PeerInterface {
+
+    // Unique peer id
+    public String id;
+
+    // Size controls
+    public static final int DEFAULT_MAX_SIZE = 1000000000;
+    public int limit;
+    public int used;
+
+    // Thread Pool
+    public static ScheduledThreadPoolExecutor pool;
+
+    // Sockets
+    public static Socket MC;
+    public static Socket MDB;
+    public static Socket MDR;
+
+    // State Backup
+    public boolean doingBackUp;
+    public String file;
+    public int replications;
+
+    // State Restore
+    public boolean restoring;
+    public AtomicBoolean waitRestoreReply = new AtomicBoolean(false);
+    public ConcurrentHashMap<String, Chunk> recoveredChunks = new ConcurrentHashMap<String, Chunk>(); // ChunkNo ->
+                                                                                                      // Chunk
+
+    // Service Management Information
+    public ConcurrentHashMap<String, Chunk> chunks = new ConcurrentHashMap<String, Chunk>(); // fileId-ChunkNo
+                                                                                             // -> Chunk
+    public ConcurrentHashMap<String, String> files = new ConcurrentHashMap<String, String>(); // fileId -> // file name
+
+    // Empty Constructor
+    public Server() {
+    }
+
+    // Simplified Constructor(Default values)
+    public Server(String id) throws IOException {
+        this.used = 0;
+        this.id = id;
+        this.limit = DEFAULT_MAX_SIZE;
+        this.setupSockets();
+        this.loadLocal();
+        this.setupDirectory();
+    }
+
+    // Full constructor
+    public Server(String id, int portMC, String addressMC, int portMDB, String addressMDB, int portMDR,
+            String addressMDR) throws IOException {
+        this.used = 0;
+        this.id = id;
+        this.limit = DEFAULT_MAX_SIZE;
+        this.setupSockets(portMC, addressMC, portMDB, addressMDB, portMDR, addressMDR);
+        this.loadLocal();
+        this.setupDirectory();
+    }
+
+    // Backup Protocol
+    public void backup(String path, int replications) throws RemoteException {
+
+        // Start backup
+        this.doingBackUp = true;
+
+        // Extract file name
+        String fileName = new File(path).getName();
+
+        // Generate Hash from file name and date
+        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
+
+        // Is file backed up already?
+        if (this.files.get(fileId) != null) {
+            System.out.println("File already backed up");
+            return;
+        }
+
+        // Add to file list
+        this.files.put(fileId, fileName);
+
+        try {
+
+            // Get file bytes
+            byte[] bytes = FileManager.readFile(path);
+
+            // Get file in chunks
+            byte[][] chunks = FileManager.split(bytes);
+
+            // Send each chunk
+            for (int i = 0; i < chunks.length; i++) {
+
+                // Base receive time of 1 second
+                int receiveTime = 1000;
+
+                // PUTCHUNK tries
+                for (int j = 0; j < 5; j++) {
+
+                    // Prepare message
+                    Message msg = new Message(Message.MessageType.PUTCHUNK, id, fileId, i, replications, chunks[i]);
+
+                    // Pack in datagram
+                    DatagramPacket packet = msg.packit(MDB.address, MDB.port);
+
+                    // Send message
+                    MDB.socket.send(packet);
+
+                    // Wait for replies and check Replication level
+                    this.replications = 0;
+                    this.file = fileId;
+                    Thread.sleep(receiveTime);
+                    if (this.replications >= replications) {
+                        break;
+                    }
+
+                    // Double receive time
+                    receiveTime *= 2;
+
+                }
+
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // Terminate backup
+        this.doingBackUp = false;
+
+    }
+
+    // Restore Protocol
+    public void restore(String path) throws RemoteException {
+
+        this.restoring = true;
+
+        // File name
+        String fileName = new File(path).getName();
+
+        // Generate Hash from file name and date
+        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
+
+        // Find in list of backed up
+        String name = this.files.get(fileId);
+
+        // File not backed up yet, just return
+        if (name == null) {
+            System.out.println("File not backed up by this peer");
+            return;
+        }
+
+        try {
+
+            // Get byte count (probably better than actually fetching the bytes array)
+            File file = new File(path);
+            int bytes = (int) file.length();
+
+            // How many chunks to expect
+            int chunks = 1 + bytes / FileManager.chunkSize;
+
+            // Reset list of Chunks recovered
+            this.recoveredChunks.clear();
+
+            // Request chunks
+            for (int i = 0; i < chunks; i++) {
+
+                // Prepare message
+                Message msg = new Message(Message.MessageType.GETCHUNK, id, fileId, i, 0, new byte[0]);
+
+                // Pack in datagram
+                DatagramPacket packet = msg.packit(MC.address, MC.port);
+
+                // Send message
+                MC.socket.send(packet);
+
+                // Wait for response
+                this.waitRestoreReply.set(true);
+                long time = System.nanoTime();
+                while (this.waitRestoreReply.get()) {
+                    // Restore timeout
+                    if (TimeUnit.NANOSECONDS.toMillis(time) - TimeUnit.NANOSECONDS.toMillis(System.nanoTime()) > 1000) {
+                        System.out.println("Restore timeout, missing chunks");
+                        return;
+                    }
+                }
+
+            }
+
+            // Get simple list from recovered chunks
+            List<Chunk> list = new ArrayList<Chunk>(this.recoveredChunks.values());
+
+            // Sort chunks
+            Collections.sort(list, new Comparator<Chunk>() {
+                @Override
+                public int compare(Chunk p1, Chunk p2) {
+                    return p1.chunkN - p2.chunkN; // Ascending
+                }
+            });
+
+            // Pack chunks
+            byte[][] allBytes = new byte[list.size()][];
+            for (int i = 0; i < list.size(); i++) {
+                allBytes[i] = list.get(i).bytes;
+            }
+
+            // Merge chunks
+            byte[] fileBytes = FileManager.merge(allBytes);
+
+            // Save file
+            Path current = Paths.get("");
+            String place = current.toAbsolutePath().toString() + "\\peer" + id + "\\restored\\" + fileName;
+            try (FileOutputStream stream = new FileOutputStream(place)) {
+                stream.write(fileBytes);
+                stream.close();
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        this.restoring = false;
+
+    }
+
+    // Delete chunks associated to this file
+    public void delete(String path) throws RemoteException {
+
+        // File name
+        String fileName = new File(path).getName();
+
+        // Generate Hash from file name and date
+        String fileId = FileManager.hash(fileName + Long.toString(new File(path).lastModified()));
+
+        // Remove from list of backed up
+        String name = this.files.get(fileId);
+        if (name != null) {
+            this.files.remove(fileId);
+        }
+
+        try {
+
+            // Prepare message
+            Message msg = new Message(Message.MessageType.DELETE, id, fileId, 0, 0, new byte[0]);
+
+            // Pack in datagram
+            DatagramPacket packet = msg.packit(MC.address, MC.port);
+
+            // Send message
+            MC.socket.send(packet);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    public void reclaim(int memory) throws RemoteException {
+
+        // Memory can't be lower than 0
+        if (memory < 0) {
+            return;
+        }
+
+        // Assign new limit
+        limit = memory;
+
+        try {
+
+            // Check need to delete chunks
+            // Going to delete by order in the map
+            while (used > limit) {
+
+                // Pick an entry
+                Map.Entry<String, Chunk> entry = chunks.entrySet().iterator().next();
+
+                // Remove from used space
+                used -= entry.getValue().bytes.length;
+
+                // Prepare message
+                Message msg = new Message(Message.MessageType.REMOVED, id, entry.getValue().fileId,
+                        entry.getValue().chunkN, 0, new byte[0]);
+
+                // Pack in datagram
+                DatagramPacket packet = msg.packit(MC.address, MC.port);
+
+                // Send message
+                MC.socket.send(packet);
+
+                // Remove from map
+                chunks.remove(entry.getKey());
+
+                // Remove from file system
+                Path current = Paths.get("");
+                String place = current.toAbsolutePath().toString() + "\\peer" + id + "\\backup\\"
+                        + entry.getValue().fileId + "\\chk" + entry.getValue().chunkN + ".chk";
+                File toDelete = new File(place);
+                toDelete.delete();
+
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    public void state() throws RemoteException {
+
+    }
+
+    // Get information of previous peer life from file system
+    public void loadLocal() {
+
+        // Base path
+        Path currentRelativePath = Paths.get("");
+        String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + id;
+
+        // Backup path
+        File file = new File(peerDir + "\\backup");
+
+        // FileIds backed up
+        File[] files = file.listFiles(File::isDirectory);
+
+        // Peer didn't exist, just return
+        if (files == null)
+            return;
+
+        // Each file directory
+        for (int i = 0; i < files.length; i++) {
+
+            // FileId
+            String fileId = files[i].getName();
+
+            // Get Chunks inside
+            file = new File(peerDir + "\\backup" + "\\" + fileId);
+            File[] chunks = file.listFiles();
+
+            // No chunks, just return
+            if (chunks == null)
+                return;
+
+            // Each chunk
+            for (int j = 0; j < chunks.length; j++) {
+
+                // Chunk name
+                String chunkNo = chunks[j].getName();
+
+                // Remove chk
+                chunkNo = chunkNo.replaceAll("\\.", "");
+                chunkNo = chunkNo.replaceAll("chk", "");
+
+                try {
+
+                    // Read bytes
+                    byte[] bytes = Files.readAllBytes(chunks[j].toPath());
+
+                    // Updated used bytes
+                    used += bytes.length;
+
+                    // Add to HashMap
+                    this.chunks.put(fileId + "-" + chunkNo, new Chunk(fileId, Integer.parseInt(chunkNo), bytes, 0));
+
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+            }
+
+        }
+
+    }
+
+    // Setup directory
+    public void setupDirectory() {
+
+        // Base path
+        Path currentRelativePath = Paths.get("");
+        String peerDir = currentRelativePath.toAbsolutePath().toString() + "\\peer" + id;
+
+        // Backup path
+        File file = new File(peerDir + "\\backup");
+        file.mkdirs();
+
+        // Backup path
+        file = new File(peerDir + "\\restored");
+        file.mkdirs();
+
+    }
+
+    // Default Socket Setup
+    public void setupSockets() throws IOException {
+
+        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10000);
+
+        MC = new Socket(this, Socket.Type.MC);
+        pool.execute(MC);
+
+        MDB = new Socket(this, Socket.Type.MDB);
+        pool.execute(MDB);
+
+        MDR = new Socket(this, Socket.Type.MDR);
+        pool.execute(MDR);
+
+    }
+
+    // Specifc Socket Setup
+    public void setupSockets(int portMC, String addressMC, int portMDB, String addressMDB, int portMDR,
+            String addressMDR) throws IOException {
+
+        pool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(10000);
+
+        MC = new Socket(this, Socket.Type.MC, portMC, addressMC);
+        pool.execute(MC);
+
+        MDB = new Socket(this, Socket.Type.MDB, portMDB, addressMDB);
+        pool.execute(MDB);
+
+        MDR = new Socket(this, Socket.Type.MDR, portMDR, addressMDR);
+        pool.execute(MDR);
+
+    }
+
+    public static void main(String args[]) {
+
+        try {
+
+            // Server object
+            Server obj;
+
+            // Basic (version id)
+            if (args.length == 2) {
+                obj = new Server(args[1]);
+            }
+            // Full (version id access port mc port mdb port mdr)
+            else if (args.length == 9) {
+                obj = new Server(args[1], Integer.parseInt(args[3]), args[4], Integer.parseInt(args[5]), args[6],
+                        Integer.parseInt(args[7]), args[8]);
+            }
+            // Invalid arguments
+            else {
+                System.out.println("java Server <version> <server id>");
+                System.out.println(
+                        "java Server <version> <server id> <access_point> <MC_port> <MC_IP_address> <MDB_port> <MDB_IP_address> <MDR_port> <MDR_IP_address>");
+                return;
+            }
+
+            // Bind the server as a registry
+            PeerInterface stub = (PeerInterface) UnicastRemoteObject.exportObject(obj, 0);
+
+            // Bind the remote object's stub in the registry
+            Registry registry = LocateRegistry.getRegistry();
+            registry.bind(obj.id, stub);
+
+            System.err.println("Server ready");
+
+        } catch (Exception e) {
+
+            System.err.println("Server exception: " + e.toString());
+            e.printStackTrace();
+
+        }
+    }
+
+}
\ No newline at end of file
Index: src/Socket.java
===================================================================
--- src/Socket.java	(nonexistent)
+++ src/Socket.java	(revision 3)
@@ -0,0 +1,89 @@
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.InetAddress;
+import java.net.MulticastSocket;
+import java.util.AbstractMap.SimpleEntry;
+
+public class Socket implements Runnable {
+
+    public MulticastSocket socket;
+    public int port;
+    public InetAddress address;
+
+    public enum Type {
+        MC, MDB, MDR
+    }
+
+    public Type type;
+
+    public Server peer;
+
+    // Message size(safe size)
+    public static final int MESSAGE_SIZE = 65500;
+
+    // Default constructor
+    public Socket(Server peer, Type type) throws IOException {
+
+        this.peer = peer;
+
+        switch (type) {
+        case MC:
+            this.port = 8000;
+            this.address = InetAddress.getByName("224.0.0.1");
+            break;
+        case MDB:
+            this.port = 8001;
+            this.address = InetAddress.getByName("224.0.0.2");
+            break;
+        case MDR:
+            this.port = 8002;
+            this.address = InetAddress.getByName("224.0.0.3");
+            break;
+        default:
+            return;
+        }
+
+        this.type = type;
+        this.socket = new MulticastSocket(this.port);
+        this.socket.joinGroup(address);
+
+    }
+
+    // Specific constructor
+    public Socket(Server peer, Type type, int port, String address) throws IOException {
+
+        this.peer = peer;
+        this.type = type;
+        this.port = port;
+        this.address = InetAddress.getByName(address);
+        this.socket = new MulticastSocket(this.port);
+        this.socket.joinGroup(this.address);
+
+    }
+
+    @Override
+    public void run() {
+
+        while (true) {
+
+            byte[] buf = new byte[MESSAGE_SIZE];
+            DatagramPacket packet = new DatagramPacket(buf, buf.length);
+
+            try {
+
+                this.socket.receive(packet);
+
+                System.out.println("Packet received, launching interpreter");
+
+                Server.pool.execute(new Interpreter(peer, packet));
+
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+        }
+
+    }
+
+}
Index: clientBackup.bat
===================================================================
--- clientBackup.bat	(revision 2)
+++ clientBackup.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
-java Client //localhost/1 BACKUP test.png 2
\ No newline at end of file
+cd bin
+java Client //localhost/1 BACKUP ../test.png 2
\ No newline at end of file
Index: clientDelete.bat
===================================================================
--- clientDelete.bat	(revision 2)
+++ clientDelete.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
-java Client //localhost/1 DELETE test.png
\ No newline at end of file
+cd bin
+java Client //localhost/1 DELETE ../test.png
\ No newline at end of file
Index: clientReclaim.bat
===================================================================
--- clientReclaim.bat	(revision 2)
+++ clientReclaim.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 java Client //localhost/2 RECLAIM 0
\ No newline at end of file
Index: clientRestore.bat
===================================================================
--- clientRestore.bat	(revision 2)
+++ clientRestore.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
-java Client //localhost/1 RESTORE test.png
\ No newline at end of file
+cd bin
+java Client //localhost/1 RESTORE ../test.png
\ No newline at end of file
Index: compile.bat
===================================================================
--- compile.bat	(revision 2)
+++ compile.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
-javac *.java
\ No newline at end of file
+mkdir bin
+javac -d bin src/*.java
\ No newline at end of file
Index: peer1.bat
===================================================================
--- peer1.bat	(revision 2)
+++ peer1.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 java Server 1 1 1 8000 224.0.0.1 8001 224.0.0.2 8002 224.0.0.3
\ No newline at end of file
Index: peer2.bat
===================================================================
--- peer2.bat	(revision 2)
+++ peer2.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 java Server 1 2 2 8000 224.0.0.1 8001 224.0.0.2 8002 224.0.0.3
\ No newline at end of file
Index: peer3.bat
===================================================================
--- peer3.bat	(revision 2)
+++ peer3.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 java Server 1 3 3 8000 224.0.0.1 8001 224.0.0.2 8002 224.0.0.3
\ No newline at end of file
Index: peer4.bat
===================================================================
--- peer4.bat	(revision 2)
+++ peer4.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 java Server 1 4 4 8000 224.0.0.1 8001 224.0.0.2 8002 224.0.0.3
\ No newline at end of file
Index: rmi.bat
===================================================================
--- rmi.bat	(revision 2)
+++ rmi.bat	(revision 3)
@@ -1,2 +1,3 @@
 @echo off 
+cd bin
 start rmiregistry
\ No newline at end of file
