Commits

Simon Claret  committed 38c3f07

Initial import

  • Participants

Comments (0)

Files changed (13)

File .DS_Store

Binary file added.

File ClientData.java

+package cs416project;
+
+/* This class encapsulates a client's id and location */
+
+public class ClientData {
+
+	private int id;
+	private Location loc;
+	
+	public ClientData(int id, Location loc)
+	{
+		this.id = id;
+		this.loc = loc;
+	}
+	
+	public int getID()
+	{
+		return id;
+	}
+
+	public void setID(int id)
+	{
+		this.id = id;
+	}
+
+	public Location getLocation()
+	{
+		return loc;
+	}
+	
+	public void setLocation(Location loc)
+	{
+		this.loc = loc;
+	}
+	
+	public String toString()
+	{
+		return "Client: " + id + " - " +loc.toString();
+	}
+}

File Communicator.java

+package cs416project;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.net.UnknownHostException;
+
+/*
+ * this class manages a communication channel with a server.
+ */
+public class Communicator {
+
+	protected Socket s;
+	PrintWriter out;
+	BufferedReader in;
+	int zone;
+	
+	public Communicator(int zone, String type) {
+		this.zone = zone;
+		connect(zone);
+		debug("Sent type declaration (" + type + "), waiting for ack");
+		waitForResponse("DPACS1.0/WELCOME");
+		sendPacket("DPACS1.0/"+type.toUpperCase());
+	}
+	
+	protected void sendPacket(String msg)
+	{
+		out.print(msg);
+		out.flush();
+	}
+	
+	protected void waitForResponse(String desiredResponse) {
+		try {
+			String response = in.readLine();
+			if (response.equals(desiredResponse) != true) 
+			{
+				debug("Invalid server response.");
+	            System.exit(1);
+			}
+		debug("Response received: " + response);
+		} catch (IOException e) {
+			debug("Socket IO error.");
+            System.exit(1);
+		}		
+	}
+
+	protected int getZone()
+	{
+		return zone;
+	}
+
+	protected void connect(int zone)
+	{
+		try {
+			
+			s = new Socket("register.ubcsailing.org", 4000 + zone);
+			out = new PrintWriter(s.getOutputStream());
+			in = new BufferedReader(new InputStreamReader(s.getInputStream()));
+			debug("Connected to " + s.getInetAddress().toString() + " port " + s.getPort());
+
+		} catch (UnknownHostException e) {
+			debug("Could not connect to register.ubcsailing.org");
+            System.exit(1);
+		} catch (IOException e) {
+			debug("Could not get I/O for register.ubcsailing.org");
+            System.exit(1);
+		}
+	}	
+	
+	protected void aufWiedersehen()
+	{
+		try {
+			in.close();
+			out.close();
+			s.close();
+			debug("Connection closed");
+		} catch (IOException e) {
+			debug("Could not close I/O or sockets");
+            System.exit(1);
+
+		}
+	}
+	
+	//prints debug messages
+	protected void debug(String s)
+	{
+		System.out.println(this.getClass().getSimpleName()+ ": "+s);
+	}
+}

File DoPeriodically.java

+package cs416project;
+
+/*
+ * This abstract class defines methods that are necessary in the thread that
+ * sends position updates to the server
+ */
+public abstract class DoPeriodically extends Thread {
+
+	protected int ms_between_updates;
+	
+	public void setUpdateDelay(int ms_between_updates) {
+		this.ms_between_updates = ms_between_updates;
+	}
+
+	protected void takeABreak() {
+		try {
+			Thread.sleep(ms_between_updates);
+		} catch (InterruptedException e) {
+		}
+	}
+
+}

File InformationPuller.java

+package cs416project;
+
+
+/*
+ * This class requests the location of other clients from the appropriate 
+ * server for the zone we are in at regular intervals
+ */
+public class InformationPuller extends Thread {
+	
+	PullingCommunicator com;
+	MapPanel map; 
+	
+	public InformationPuller(MapPanel map)
+	{
+		//This reference is used to find out which zone is currently being displayed. 
+		//TODO: think of a better way to get this information.
+		this.map = map;  
+		initializeCom();	
+	}
+	
+	public void run() {
+		while (true) {
+			if (map.getZone() != com.getZone())
+			{
+				com.aufWiedersehen();
+				initializeCom();
+			}
+			map.setClientList(com.getClients());
+			map.setMapStale();
+		}
+	}
+	
+	private void initializeCom()
+	{
+		com = new PullingCommunicator(map.getZone());
+	}
+
+}

File JavaClient.java

+package cs416project;
+
+import java.awt.*;
+import java.awt.event.*;
+import java.lang.reflect.InvocationTargetException;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+import javax.swing.*;
+
+//TODO: 
+//remove unnecessary global references to swing components such as buttons
+//factor the jframe code into a separate class from JavaClient
+//factor location processor into a separate class
+public class JavaClient extends JFrame implements ActionListener {
+
+	private static final long serialVersionUID = 1L;
+
+	public static final Integer loglock = new Integer(0);
+	public static final Integer redrawlock = new Integer(0);
+
+	MapPanel map;
+
+	private JSlider timeBetweenUpdates;
+	private JButton controlButton;
+	private JButton pauseButton;
+
+	private JTextField targetID;
+	private JTextField message;
+	private JTextArea messageLog;
+
+	private JTextField searchID;
+	private JButton searchButton;
+	private JLabel searchResult;
+
+	private JTextArea log;
+	private JButton clearLogButton;
+	private StringBuffer logEntries;
+
+	private JTextField clientID;
+	private JTextArea locations;
+
+	private LocationPusher lp;
+	private ClientData me;
+	private ArrayList<Location> ll; // locationsList
+	private int myID;
+	
+
+	public static void main(String[] args) {
+		new JavaClient("CPSC 416 Project - Client Emulator");
+	}
+
+	public JavaClient(String title) {
+		super(title);
+		resetLog();
+		Random gen = new Random();
+		myID = gen.nextInt(99) + 1;// default id
+		me = new ClientData(myID, new Location(-1, -1));
+		try {
+			SwingUtilities.invokeAndWait(new Runnable() {
+				public void run() {
+					setupGUI();
+				}
+			});
+		} catch (InterruptedException e) {
+		} catch (InvocationTargetException e) {
+		}
+		toLog("Client initialized");
+		Timer timer = new Timer(200, this);
+		timer.setActionCommand("redraw");
+		timer.start();
+		InformationPuller ip = new InformationPuller(map);
+		ip.start();
+	}
+
+	private void resetLog() {
+		logEntries = new StringBuffer(5000);
+	}
+
+	public void toLog(String entry) {
+		synchronized (JavaClient.loglock) {
+			Date now = new Date();
+			logEntries.insert(0, DateFormat.getTimeInstance().format(now)
+					+ ": " + entry + "\n");
+			log.setText(logEntries.toString());
+		}
+	}
+
+	public void actionPerformed(ActionEvent e) {
+		String action = e.getActionCommand();
+		if (action == "Start") {
+			if (processLocations() == true) {
+				disableInput();
+				me.setID(myID);
+				lp = new LocationPusher(me, ll);
+				lp.setJavaClient(this);
+				lp.setUpdateDelay(timeBetweenUpdates.getValue() * 1000);
+				lp.start();
+			}
+		} else if (action == "Stop") {
+			me.setLocation(new Location(-1, -1));
+			enableInput();
+			pauseButton.setText("Pause");
+			lp.aufWiedersehen();
+		} else if (action == "Pause") {
+			lp.pause();
+			pauseButton.setText("Unpause");
+			toLog("Paused");
+		} else if (action == "Unpause") {
+			lp.unPause();
+			pauseButton.setText("Pause");
+			toLog("Unpaused");
+		} else if (action == "Clear log") {
+			resetLog();
+			toLog("Log cleared");
+		} else if (action == "Search") {
+			searchResult.setText("Status: found user " + searchID.getText()
+					+ " on server 3 at position (77,84).");
+		} else if (action == "Zone 1" || action == "Zone 2" || action == "Zone 3"
+				|| action == "Zone 4") {
+			toLog("switching to " + action);
+			map.setZone(new Integer(action.substring(5,6)));
+		} else if (action == "redraw") {
+			synchronized (JavaClient.redrawlock) {
+				if (map.getMapStale() == true) {
+					map.repaint();
+				}
+			}
+		}
+
+	}
+
+	private void setupGUI() {
+		// set up gui
+		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+		setResizable(false);
+		Container content = getContentPane();
+		content.setLayout(new GridLayout(1, 2, 10, 0));
+
+		JPanel leftSide = new JPanel();
+		leftSide.setBackground(Color.RED);
+		JPanel rightSide = new JPanel();
+		leftSide.setLayout(new BoxLayout(leftSide, BoxLayout.Y_AXIS));
+		rightSide.setLayout(new BoxLayout(rightSide, BoxLayout.Y_AXIS));
+
+		// set up map panel
+		JPanel mapPanel = new JPanel();
+		mapPanel.setLayout(new BoxLayout(mapPanel, BoxLayout.Y_AXIS));
+		mapPanel.setBorder(BorderFactory.createTitledBorder("Map"));
+		map = new MapPanel(me);
+		mapPanel.add(map);
+		JPanel mapButtonsPanel = new JPanel();
+		mapButtonsPanel.setLayout(new GridLayout(2,2,5,5));
+		JRadioButton oneButton = new JRadioButton("Zone 1");
+		JRadioButton twoButton = new JRadioButton("Zone 2");
+		JRadioButton threeButton = new JRadioButton("Zone 3");
+		JRadioButton fourButton = new JRadioButton("Zone 4");
+		oneButton.addActionListener(this);
+		twoButton.addActionListener(this);
+		threeButton.addActionListener(this);
+		fourButton.addActionListener(this);
+		ButtonGroup mapButtonGroup = new ButtonGroup();
+		mapButtonGroup.add(oneButton);
+		mapButtonGroup.add(twoButton);
+		mapButtonGroup.add(threeButton);
+		mapButtonGroup.add(fourButton);
+		mapButtonsPanel.add(oneButton);
+		mapButtonsPanel.add(twoButton);
+		mapButtonsPanel.add(threeButton);
+		mapButtonsPanel.add(fourButton);
+		oneButton.setSelected(true);
+		mapPanel.add(mapButtonsPanel);
+		leftSide.add(mapPanel);
+
+		// set up command panel (for timing slider & command buttons)
+		JPanel commandPanel = new JPanel();
+		commandPanel.setBorder(BorderFactory.createTitledBorder("Commands"));
+		commandPanel.setLayout(new BoxLayout(commandPanel, BoxLayout.Y_AXIS));
+		// set up timing sliders
+		JLabel sliderLabel = new JLabel("Seconds between location changes",
+				JLabel.CENTER);
+		sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT);
+		commandPanel.add(sliderLabel);
+		timeBetweenUpdates = new JSlider(1, 10, 2);
+		timeBetweenUpdates.setMajorTickSpacing(5);
+		timeBetweenUpdates.setMinorTickSpacing(1);
+		timeBetweenUpdates.setPaintLabels(true);
+		timeBetweenUpdates.setPaintTicks(true);
+		timeBetweenUpdates.setMaximumSize(new Dimension(130, 50));
+		timeBetweenUpdates.setSnapToTicks(true);
+		commandPanel.add(timeBetweenUpdates);
+		// set up command buttons
+		JPanel buttonPanel = new JPanel();
+		controlButton = new JButton("Start");
+		controlButton.addActionListener(this);
+		buttonPanel.add(controlButton);
+		pauseButton = new JButton("Pause");
+		pauseButton.addActionListener(this);
+		pauseButton.setEnabled(false);
+		buttonPanel.add(pauseButton);
+		commandPanel.add(buttonPanel);
+		leftSide.add(commandPanel);
+
+		// set up messaging panel
+		JPanel msgPanel = new JPanel();
+		msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.Y_AXIS));
+		msgPanel.setBorder(BorderFactory.createTitledBorder("Messaging"));
+		JPanel topRow = new JPanel();
+		topRow.add(new JLabel("Target ID:"));
+		targetID = new JTextField(2);
+		targetID.setText("3");
+		topRow.add(targetID);
+		topRow.add(new JLabel("Message:"));
+		message = new JTextField("Hello world!", 7);
+		JButton sendButton = new JButton("Send");
+		sendButton.addActionListener(this);
+		topRow.add(message);
+		topRow.add(sendButton);
+		msgPanel.add(topRow);
+		msgPanel.add(new JSeparator(SwingConstants.HORIZONTAL));
+		messageLog = new JTextArea(3, 25);
+		messageLog.setEditable(false);
+		JScrollPane mScrollPane = new JScrollPane(messageLog,
+				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+		msgPanel.add(mScrollPane);
+		rightSide.add(msgPanel);
+
+		// set up search panel
+		JPanel searchPanel = new JPanel();
+		searchPanel.setLayout(new BoxLayout(searchPanel, BoxLayout.Y_AXIS));
+		searchPanel.setBorder(BorderFactory.createTitledBorder("Search"));
+		JPanel searchInputPanel = new JPanel();
+		JLabel searchLabel = new JLabel("User to search for (ID):");
+		searchInputPanel.add(searchLabel);
+		searchID = new JTextField(3);
+		searchID.setText("5");
+		searchInputPanel.add(searchID);
+		searchButton = new JButton("Search");
+		searchButton.addActionListener(this);
+		searchInputPanel.add(searchButton);
+		searchPanel.add(searchInputPanel);
+		searchPanel.add(new JSeparator(SwingConstants.HORIZONTAL));
+		JPanel searchResultsPanel = new JPanel();
+		searchResult = new JLabel("Status: no search initiated.");
+		searchResultsPanel.add(searchResult);
+		searchPanel.add(searchResultsPanel);
+		rightSide.add(searchPanel);
+
+		// set up log panel
+		JPanel logPanel = new JPanel();
+		logPanel.setLayout(new BoxLayout(logPanel, BoxLayout.Y_AXIS));
+		logPanel.setBorder(BorderFactory.createTitledBorder("Log"));
+		log = new JTextArea(5, 25);
+		log.setEditable(false);
+		JScrollPane logScrollPane = new JScrollPane(log,
+				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+		logPanel.add(logScrollPane);
+		JPanel logButtonsPanel = new JPanel();
+		clearLogButton = new JButton("Clear log");
+		clearLogButton.addActionListener(this);
+		logButtonsPanel.add(clearLogButton);
+		logPanel.add(logButtonsPanel);
+		rightSide.add(logPanel);
+
+		// set up data panel
+		JPanel dataPanel = new JPanel();
+		dataPanel.setLayout(new BoxLayout(dataPanel, BoxLayout.Y_AXIS));
+		dataPanel.setBorder(BorderFactory.createTitledBorder("Data"));
+		JPanel idPanel = new JPanel();
+		idPanel.add(new JLabel("Client ID:"));
+		clientID = new JTextField(2);
+		clientID.setText(Integer.toString(myID));
+		idPanel.add(clientID);
+		dataPanel.add(idPanel);
+		dataPanel.add(new JLabel("Locations:"));
+		locations = new JTextArea(5, 25);
+		locations
+				.setText("20,5\n21,5\n22,5\n23,5\n24,5\n25,5\n26,5\n27,5\n28,5\n29,5\n"
+						+ "30,5\n31,5\n32,5\n33,5\n34,5\n35,5\n36,5\n37,5\n38,5\n39,5\n"
+						+ "40,5\n40,6\n40,7\n40,8\n40,9\n40,10\n40,11\n40,12\n40,13\n"
+						+ "40,14\n40,15\n40,16\n40,17\n40,18\n40,19\n40,20\n40,21\n40,22\n"
+						+ "40,23\n40,24\n40,25\n40,26\n40,27\n40,28\n40,29\n40,30\n40,31\n"
+						+ "40,32\n40,33\n40,34\n40,35\n40,36\n40,37\n40,38\n40,39\n40,40\n"
+						+ "39,40\n38,40\n37,40\n36,40\n35,40\n34,40\n33,40\n32,40\n31,40\n"
+						+ "30,40\n29,40\n28,40\n27,40\n26,40\n25,40\n24,40\n23,40\n22,40\n"
+						+ "21,40\n20,39\n20,38\n20,37\n20,36\n20,35\n20,34\n20,33\n20,32\n"
+						+ "20,31\n20,30\n20,29\n20,28\n20,27\n20,26\n20,25\n20,24\n20,23\n"
+						+ "20,22\n20,21\n20,20\n20,19\n20,18\n20,17\n20,16\n20,15\n20,14\n"
+						+ "20,13\n20,12\n20,11\n20,10\n20,9\n20,8\n20,7\n20,6");
+		JScrollPane scrollPane = new JScrollPane(locations,
+				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
+				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
+		dataPanel.add(scrollPane);
+		rightSide.add(dataPanel);
+
+		content.add(leftSide);
+		content.add(rightSide);
+
+		pack();
+		setVisible(true);
+	}
+
+	/*
+	 * parses the locations entered and places them into a vector. if formatting
+	 * errors are encountered then an error message is shown. returns true if
+	 * processing succeeded, false otherwise.
+	 */
+	private boolean processLocations() {
+		StringTokenizer st = new StringTokenizer(this.locations.getText(),
+				"\n", false);
+		ll = new ArrayList<Location>(st.countTokens());
+		int x, y;
+		int i = 0;
+		while (st.hasMoreElements()) {
+			i++;
+			String location = st.nextToken();
+			String[] lc = location.split(","); // locationComponents
+			if (lc.length == 2) {
+				try {
+					x = new Integer(lc[0]);
+					y = new Integer(lc[1]);
+				} catch (NumberFormatException e) {
+					return failToProcess(i, location);
+				}
+				if (x >= 0 && x <= 60 && y >= 0 && y <= 60) {
+					ll.add(new Location(x, y));
+				} else {
+					return failToProcess(i, location);
+				}
+			} else {
+				return failToProcess(i, location);
+			}
+		}
+		try {
+			myID = new Integer(clientID.getText());
+		} catch (NumberFormatException e) {
+			return failToSetID();
+		}
+		if (myID < 1 || myID > 99)
+			return failToSetID();
+		return true;
+	}
+
+	private boolean failToProcess(int i, String location) {
+		JOptionPane
+				.showMessageDialog(
+						locations,
+						"Invalid location data entered on line "
+								+ i
+								+ " - \""
+								+ location
+								+ "\".  Format: a,b where a & b are between 0 and 60 inclusive.");
+		return false;
+	}
+
+	private boolean failToSetID() {
+		JOptionPane
+				.showMessageDialog(
+						clientID,
+						"Invalid client ID entered.  Client ID must be an integer between 1 and 99 inclusive.");
+		return false;
+	}
+
+	private void disableInput() {
+		controlButton.setText("Stop");
+		pauseButton.setEnabled(true);
+		timeBetweenUpdates.setEnabled(false);
+		clientID.setEnabled(false);
+		locations.setEnabled(false);
+	}
+
+	private void enableInput() {
+		controlButton.setText("Start");
+		pauseButton.setEnabled(false);
+		timeBetweenUpdates.setEnabled(true);
+		clientID.setEnabled(true);
+		locations.setEnabled(true);
+	}
+	
+	//pass-through function that calls the corresponding function on map
+	//careful - this smells like crappy OO design.  perhaps there is 
+	//a better way to do this?
+	public void setMapStale()
+	{
+		map.setMapStale();
+	}
+
+}

File Location.java

+package cs416project;
+
+public class Location {
+
+	private int x, y;
+
+	public Location(int x, int y)
+	{
+		this.x = x;
+		this.y = y;
+	}
+	public int getX()
+	{
+		return x;
+	}
+	public int getY()
+	{
+		return y;
+	}
+	public String toString()
+	{
+		return "(" + x + "," + y + ")";
+	}
+	
+	public int getZone()
+	{
+		if (x >=1 && x <= 30 && y >= 1 && y <=30) return 1;
+		if (x >=31 && x <= 60 && y >= 1 && y <=30) return 2;
+		if (x >=1 && x <= 30 && y >= 31 && y <=60) return 3;
+		if (x >=31 && x <= 60 && y >= 31 && y <=60) return 4;
+		return -1;//indicates that the client is currently not in any zone (i.e. offline - should not be drawn!)
+		
+	}
+}

File LocationPusher.java

+package cs416project;
+
+import java.util.ArrayList;
+import java.util.ListIterator;
+
+/* 
+ * This class sends position updates to the server
+ * 
+ * Note that we cycle through a list of positions at a set speed in
+ * order to simulate a stream of incoming position updates from GPS
+ */
+public class LocationPusher extends DoPeriodically {
+	
+	private JavaClient jClient;
+	private Boolean ohNoIHaveBeenPoisoned;
+	private Boolean paused;
+	
+	private ArrayList<Location> ll;
+	private ListIterator<Location> i;
+	private ClientData me;
+	
+	PushingCommunicator com;
+	
+	public LocationPusher(ClientData me, ArrayList<Location> ll)
+	{
+		this.me = me;
+		this.ll = ll;
+		ohNoIHaveBeenPoisoned = false;
+		resetIterator();
+		paused = false;
+	}
+	
+	public void setJavaClient(JavaClient client) {
+		jClient = client;		
+	}
+	
+	public void run() {
+		while (ohNoIHaveBeenPoisoned == false) {
+			while (ohNoIHaveBeenPoisoned == false && paused == true)
+			{
+				try {
+					Thread.sleep(200);
+				} catch (InterruptedException e) {
+				}
+			}
+			if (ohNoIHaveBeenPoisoned == true) break;
+			if (i.hasNext() == false)
+			{
+				resetIterator();
+			}
+			me.setLocation(i.next());
+			if (jClient != null)
+			{
+				jClient.setMapStale();
+			}
+			pushLocation();
+			takeABreak();
+		}
+		com.aufWiedersehen();
+	}
+
+	private void pushLocation() {
+		if (jClient != null)
+		{
+			jClient.toLog("pushing " + me.getLocation().toString());
+		}
+		if(com == null)
+		{
+			initializeCom();
+		}
+		if(com.getZone() != me.getLocation().getZone())
+		{
+			com.aufWiedersehen();
+			initializeCom();
+		}
+		com.push(me);
+	}
+	
+	private void initializeCom()
+	{
+		com = new PushingCommunicator(me.getLocation().getZone());
+	}
+
+	private void resetIterator() {
+		i = ll.listIterator();
+	}
+	
+	public void aufWiedersehen()
+	{
+		ohNoIHaveBeenPoisoned = true;
+		this.interrupt();
+	}
+	
+	public void pause()
+	{
+		this.paused = true;
+	}
+	
+	public void unPause()
+	{
+		this.paused = false;
+	}
+}

File MapPanel.java

+package cs416project;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+
+import javax.imageio.ImageIO;
+import javax.swing.JPanel;
+
+public class MapPanel extends JPanel {
+
+	private static final long serialVersionUID = 1L;
+	BufferedImage map;
+	private int sx1, sy1, sx2, sy2;
+	private int xoffset, yoffset;
+	private ClientData me;
+	private int zone;
+	private boolean mapStale;
+	
+	private List<ClientData> clientList;
+	
+	Font font;
+	FontMetrics fm;
+
+	public MapPanel(ClientData me) {
+		this.me = me;
+		setMapStale();
+		setPreferredSize(new Dimension(300, 300));
+		loadMap();
+		font = new Font("Dialog", Font.BOLD, 14);
+		fm = getFontMetrics(font);
+		setZone(1);
+		setClientList(new LinkedList<ClientData>());
+	}
+
+	public void setClientList(List<ClientData> clientList) {
+		this.clientList = clientList;
+	}
+
+	public Dimension getPreferredSize() {
+		return new Dimension(300, 300);
+	}
+
+	public int getZone()
+	{
+		return zone;
+	}
+	
+	public void setZone(int zone) {
+		this.zone = zone;
+		if (zone == 1) {
+			xoffset = 0;
+			yoffset = 0;
+			sx1 = 0;
+			sy1 = 0;
+			sx2 = 355;
+			sy2 = 355;
+		} else if (zone == 2) {
+			xoffset = 30;
+			yoffset = 0;
+			sx1 = 355;
+			sy1 = 0;
+			sx2 = 710;
+			sy2 = 355;
+		} else if (zone == 3) {
+			xoffset = 0;
+			yoffset = 30;
+			sx1 = 0;
+			sy1 = 355;
+			sx2 = 355;
+			sy2 = 710;
+		} else if (zone == 4) {
+			xoffset = 30;
+			yoffset = 30;
+			sx1 = 355;
+			sy1 = 355;
+			sx2 = 710;
+			sy2 = 710;
+		} else
+		{
+			System.err.println("Error: invalid zone.");
+			System.exit(1);
+		}
+		setMapStale();
+	}
+
+	private boolean isInCurrentZone(Location l) {
+		return (l.getZone() == zone);
+	}
+
+	//centers the label in the give bounding box
+	private void centerLabel(int id, Graphics2D g2, int x, int y, int w, int h)
+	{
+		String label = Integer.toString(id);
+		int fheight = fm.getHeight();
+		int fascent = fm.getAscent();
+		int fwidth = fm.stringWidth(label);
+		int x0 = x + (w-fwidth)/2;
+		int y0 = y + (h-fheight)/2 + fascent;
+		g2.drawString(label, x0, y0);
+	}
+	
+	// Idea: is it possible to do this coordinate juggling using matrices? That
+	// might create cleaner code.
+	private void paintClient(Graphics2D g2, Color color, ClientData c) {
+		Location l = c.getLocation();
+		if (isInCurrentZone(l) == true) {
+			g2.setColor(color);
+			//define bounding box
+			int bbx = (l.getX()-xoffset)*10-12;
+			int bby = (l.getY()-yoffset)*10-12;
+			int bbw = 24;
+			int bbh = 24;
+			
+			g2.fillOval(bbx, bby, bbw, bbh);
+			g2.setColor(Color.WHITE);
+			centerLabel(c.getID(), g2, bbx, bby, bbw, bbh);
+		}
+	}
+	
+	/* 
+	 * paints the list of other clients that are in the zone (but not me)
+	 */
+	private void paintClients(Graphics2D g2, List<ClientData> clients)
+	{
+		ListIterator<ClientData> i = clients.listIterator();
+		ClientData curClient;
+		while (i.hasNext())
+		{
+			curClient = i.next();
+			//avoid drawing a representation of myself that has been echoed back by the server
+			//doing so would result in 2 mes being drawn :-(
+			if (curClient.getID() != this.me.getID())
+			{
+				paintClient(g2, Color.BLUE, curClient);
+			}
+		}
+	}
+
+	public void paintComponent(Graphics g) {
+		super.paintComponent(g);
+				Graphics2D g2 = (Graphics2D) g;
+				g2.setFont(font);
+				g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+						RenderingHints.VALUE_ANTIALIAS_ON);
+				g2.drawImage(map, 0, 0, 300, 300, sx1, sy1, sx2, sy2, null);
+				g2.drawString("(" + xoffset + "," + yoffset + ")", 3, 15);
+				g2.drawString("(" + (xoffset + 30) + "," + yoffset + ")", 253,
+						15);
+				g2.drawString("(" + xoffset + "," + (yoffset + 30) + ")", 3,
+						295);
+				g2.drawString(
+						"(" + (xoffset + 30) + "," + (yoffset + 30) + ")", 253,
+						295);
+				g2.drawRect(0, 0, 300, 300);
+				paintClient(g2, Color.RED, me);
+				paintClients(g2, clientList);
+				mapStale = false;
+	}
+
+	public void loadMap() {
+		try {
+			map = ImageIO.read(new File("map2.gif"));
+		} catch (IOException e) {
+		}
+	}
+	
+	/*
+	 * This function is called by the locationpusher or informationpuller
+	 * threads when something has changed on the map. When this happens we set a
+	 * flag that tells the redraw event that the map must be redrawn. The redraw
+	 * event tells the MapPanel to repaint itself if the flag is set.
+	 */
+	public void setMapStale() {
+		synchronized (JavaClient.redrawlock) {
+			mapStale = true;
+		}
+	}
+	
+	public boolean getMapStale() {
+		return mapStale;		
+	}
+	
+}

File PullingCommunicator.java

+package cs416project;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+public class PullingCommunicator extends Communicator {
+
+	public PullingCommunicator(int zone)
+	{
+		super(zone, "pull");
+	}
+
+	public List<ClientData> getClients() {
+		String response, response2;
+		//clientList: a list of the clients in the current zone
+		List<ClientData> cl = new LinkedList<ClientData>();
+		String[] cc;
+		ClientData cd;
+		
+		try {
+			response = in.readLine();
+			if(response.length()>=4) //if the server returns an empty list then don't bother
+			{
+				response2 = response.substring(2, response.length()-2).replace('{', '}').replace("},}", ";");
+				StringTokenizer st = new StringTokenizer(response2, ";", false);
+				while (st.hasMoreElements()) {
+					String client = st.nextToken();
+					cc = client.split(","); // clientcomponents
+					cd = new ClientData(new Integer(cc[0]), new Location(new Integer(cc[1]),new Integer(cc[2])));
+					cl.add(cd);
+				}
+			}
+		} catch (IOException e) {
+		}
+		return cl;
+	}
+}

File PushingCommunicator.java

+package cs416project;
+
+/*
+ * This class manages the upstream (pushing of location updates) communication 
+ * relationship between a client and the server pool
+ */
+public class PushingCommunicator extends Communicator {
+	public PushingCommunicator(int zone) {
+		super(zone, "push");
+	}
+	
+	public void push(ClientData me)
+	{
+		sendPacket("{"+me.getID()+","+me.getLocation().getX()+","+me.getLocation().getY()+"}");
+	}
+	
+	
+}

File dpacs.jar

Binary file added.

File map2.gif

Added
New image