// "God Client". Displays a literal representation of the active objects // talking on the server now. The user can go in, select one of those // objects and manipulate it (by abusing the server protocol). import java.awt.*; import java.util.Vector; import java.util.Enumeration; import PS10Applet; import GodClientInfo; public class GodClient extends PS10Applet implements Runnable { Thread animThread; Vector clientList; GodClientInfo grid[][]; int frameDelay = 100; int frameNumber = 0; FixedNumberFont bigFont; FixedNumberFont smallFont; GodClientInfo selectedClient; int fieldWidth = 565; int fieldHeight = 400; public void init() { super.init(); this.setBackground(Color.white); this.setForeground(Color.black); update(getGraphics()); // force window create String frameDelayString = getParameter("frameDelay"); try { if (frameDelayString != null) frameDelay = Integer.parseInt(frameDelayString); } catch (Exception e) {} String otherServerHost = getParameter("serverHost"); if (otherServerHost != null) serverHost = otherServerHost; bigFont = new FixedNumberFont(this, "bigfont", 3, 4); smallFont = new FixedNumberFont(this, "smallfont", 5, 2); clientList = new Vector(20); // make a grid to store info about clients grid = new GodClientInfo[4][2]; for (int x = 0; x < 4; x++) for (int y = 0; y < 2; y++) grid[x][y] = null; } // find the best spot on the grid to put the new client // if there's an empty spot, go there. // Otherwise, replace the oldest one with the new client public void storeClientInGrid(GodClientInfo client) { int xorder[] = {1, 2, 0, 3}; // order of xs to search Point oldest = new Point(-1, -1); for (int i = 0; i < 4; i++) { int x = xorder[i]; // search from middle out for (int y = 0; y < 2; y++) if (grid[x][y] == null) { grid[x][y] = client; client.setScreenPosition(x * GodClientInfo.infoWidth, y * GodClientInfo.infoHeight); return; } else { if (oldest.x == -1 || grid[oldest.x][oldest.y].lastTime > grid[x][y].lastTime) { oldest.x = x; oldest.y = y; } } } // didn't find an empty slot. Time to reap the oldest spot. Graphics g = imageBuffer.getGraphics(); g.setColor(Color.white); grid[oldest.x][oldest.y] = client; client.setScreenPosition(oldest.x * GodClientInfo.infoWidth, oldest.y * GodClientInfo.infoHeight); // flash the old spot white g.fillRect(oldest.x * GodClientInfo.infoWidth, oldest.y * GodClientInfo.infoHeight, GodClientInfo.infoWidth, GodClientInfo.infoHeight); repaint(); } // Is the client somewhere on the grid already? public boolean clientInGrid(GodClientInfo client) { for (int x = 0; x < 4; x++) for (int y = 0; y < 2; y++) if (grid[x][y] == client) return true; return false; } // remove the client from the grid entirely public void removeClientFromGrid(GodClientInfo client) { for (int x = 0; x < 4; x++) for (int y = 0; y < 2; y++) if (grid[x][y] == client) grid[x][y] = null; } // mouse clicks - either select a client, or else manipulate the // already selected client public boolean mouseDown(Event e, int mouseX, int mouseY) { if (selectedClient != null && clientInGrid(selectedClient)) { // scale mouse coordinates to the full playing field int newX = mouseX * fieldWidth / imageBufferSize.width; int newY = mouseY * fieldHeight / imageBufferSize.height; // forge a message as them! out.println(selectedClient.id + " " + newX + " " + newY); } else { int x = mouseX / GodClientInfo.infoWidth; int y = mouseY / GodClientInfo.infoHeight; if (grid[x][y] == null) { // no one there! // An act of creation GodClientInfo c = new GodClientInfo((int)(Math.random() * 1000), (int)(Math.random() * fieldWidth), (int)(Math.random() * fieldHeight), bigFont, smallFont); c.setTime(System.currentTimeMillis()); c.setScreenPosition(x * GodClientInfo.infoWidth, y * GodClientInfo.infoHeight); clientList.addElement(c); grid[x][y] = c; selectedClient = c; out.println(c.id + " " + c.x + " " + c.y); } else selectedClient = grid[x][y]; } return true; } // key down lets go of the selected client public boolean keyDown(Event e, int key) { if (key == 'x') { if (selectedClient != null) out.println(selectedClient.id + " -99 -99"); } else { selectedClient = null; } return true; } // Receiving a message, three possible actions: // remove the client if it has just died. // create a new client if we've never seen it before // update the client's x, y, and time. public void receiveMessage(int otherID, int x, int y) { // System.out.println("Applet received " + otherID + " " + x + " " + y); // is it a negative position? If so, reap the client if (x < 0 && y < 0) { Enumeration e = clientList.elements(); while (e.hasMoreElements()) { GodClientInfo c = (GodClientInfo)e.nextElement(); if (c.id == otherID) removeClientFromGrid(c); } return; } // look to see if we already have a record of this client boolean oldClient = false; Enumeration e = clientList.elements(); while (e.hasMoreElements() && !oldClient) { GodClientInfo c = (GodClientInfo)e.nextElement(); if (c.id == otherID) { // already seen it - update info, update it in grid if necessary oldClient = true; c.x = x; c.y = y; c.setTime(System.currentTimeMillis()); if (!clientInGrid(c)) storeClientInGrid(c); } } // haven't seen client before - create a new one. if (oldClient == false) { GodClientInfo c = new GodClientInfo(otherID, x, y, bigFont, smallFont); c.setTime(System.currentTimeMillis()); storeClientInGrid(c); clientList.addElement(c); } } public void redrawImage() { clearImageBuffer(); Graphics g = imageBuffer.getGraphics(); g.setColor(Color.black); // draw all the guys on the grid for (int x = 0; x < 4; x++) for (int y = 0; y < 2; y++) if (grid[x][y] != null) { if (selectedClient == grid[x][y]) grid[x][y].render(g, Color.red); else grid[x][y].render(g, Color.black); } repaint(); } // this thread really isn't necessary, we could go towards an event // driven display. Oh well. public void run() { while (Thread.currentThread() == animThread) { frameNumber++; redrawImage(); try { Thread.sleep(frameDelay); } catch (InterruptedException ex) {}; } } public void start() { super.start(); animThread = new Thread(this); animThread.start(); } public void stop() { animThread = null; super.stop(); } }