import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.util.*; import java.net.*; import java.io.*; //En Paper är en utökad JPanel med funktionalitet speciel för spelet class Paper extends JPanel implements Runnable{ //Synkroniserade Listor med Point (koordinater) som representerar rutor som ormarna består av public java.util.List hostList = Collections.synchronizedList(new LinkedList()); public java.util.List remoteList = Collections.synchronizedList(new LinkedList()); public Point[] fruits = new Point[4]; public Point[] oldFruits = null; public int remPort = 0; private InetAddress remAddress = null; public DatagramSocket socket = null; public SnakeBuilder sb = null; public Point hostErasePoint = null; public Point remoteErasePoint = null; public boolean isServer = false; public boolean newFruits = false; public String whoWon = null; private Color[] col = {Color.pink, Color.orange, Color.green, Color.yellow}; public Paper(int myPort, String remHost, int remPort) { this.remPort = remPort; try{ remAddress = InetAddress.getByName(remHost); socket = new DatagramSocket(myPort); }catch(Exception e){ e.printStackTrace(); } addMouseListener(new Focus(this)); setFocusable(true); Thread thread = new Thread(this); thread.start(); } //Funktionen ritar ut all information som finns i de //datastrukturer som har med grafiken att göra public void paintComponent(Graphics g) { Point p = null; Point erp = null; g.setColor(Color.blue); Iterator i; //Vi låser aktuell lista så länge vi ittererar över den. synchronized (hostList){ i = hostList.iterator(); while(i.hasNext()){ p = (Point)i.next(); g.fillRect((int)(p.getX() * 10), (int)(p.getY() * 10), 10, 10); if (hostErasePoint != null){ g.clearRect((int)(hostErasePoint.getX() * 10), (int)(hostErasePoint.getY() * 10), 10, 10); } } } synchronized (remoteList){ g.setColor(Color.red); i = remoteList.iterator(); while(i.hasNext()){ p = (Point)i.next(); g.fillRect((int)(p.getX() * 10), (int)(p.getY() * 10), 10, 10); if (remoteErasePoint != null){ g.clearRect((int)(remoteErasePoint.getX() * 10), (int)(remoteErasePoint.getY() * 10), 10, 10); } } } if (newFruits){ for(int j = 0; j < fruits.length; j++){ g.setColor(col[j]); if (oldFruits != null){ g.clearRect((int)(oldFruits[j].getX() * 10), (int)(oldFruits[j].getY() * 10), 10, 10); } g.fillOval((int)(fruits[j].getX() * 10), (int)(fruits[j].getY() * 10), 9, 9); } if (oldFruits == null) oldFruits = new Point[4]; for (int j = 0; j < fruits.length; j++) oldFruits[j] = fruits[j]; newFruits = false; } if (whoWon != null){ g.setColor(Color.orange); g.fillRect(200,155,110,32); g.setColor(Color.black); g.drawString(whoWon, 225, 175); } } //Vi adderar en puknt i till vår orm och skickar sammtidig en likadan till motspelaren. public void addPoint(Point p) { hostList.add(p); sendPoint(p, false); repaint(); } //Här tas den sista rutan bort. Funktionen används när ormen inte bör växa. public void addPoint(Point p, Point erp) { hostErasePoint = erp; hostList.add(p); sendPoint(p, true); repaint(); } //Funktionen används ändast av server. public void sendFruits(String str){ try{ socket.send(new DatagramPacket(str.getBytes(), 0, str.length(), remAddress, remPort)); }catch(IOException ioe){ ioe.printStackTrace(); } } //Om man har förlorat måste motståndaren underrättas public void sendGameOver(){ String str = "gameover"; try{ socket.send(new DatagramPacket(str.getBytes(), 0, str.length(), remAddress, remPort)); }catch(IOException ioe){ ioe.printStackTrace(); } } //All data skickas i form av strängar. (kolla receivePoint) private void sendPoint(Point p, boolean doRemove){ String str = null; if (doRemove) str = new String((int)p.getX()+" "+(int)p.getY()+" remove"); else str = new String((int)p.getX()+" "+(int)p.getY()); try{ socket.send(new DatagramPacket(str.getBytes(), 0, str.length(), remAddress, remPort)); }catch(IOException ioe){ ioe.printStackTrace(); } } //Extremt simpelt protokoll som endast tar hänsyn till antalet tokens. Om vi bara har en //token vet vi att motståndaren har förlorat oberoende av vad strängen representerar. private void recivePoint(String data){ StringTokenizer str = new StringTokenizer(data, " "); Point p = null; if (str.countTokens() == 1){ sb.gameOver = true; whoWon = "Winner!"; } else if (str.countTokens() == 2) remoteList.add(new Point(Integer.parseInt(str.nextToken()), Integer.parseInt(str.nextToken()))); else if (str.countTokens() == 3){ remoteList.add(new Point(Integer.parseInt(str.nextToken()), Integer.parseInt(str.nextToken()))); remoteErasePoint = (Point)remoteList.remove(0); }else{ if (sb.fr != null) sb.fr.clear(); for (int i = 0; i < fruits.length; i++){ p = new Point(Integer.parseInt(str.nextToken()), Integer.parseInt(str.nextToken())); fruits[i] = p; sb.fr.add(p); } newFruits = true; } repaint(); } public void run(){ byte[] buffer = new byte[50]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); try{ //Den som skickar iväg ett paket först blir client och den andre server. //Det är viktigt att utse dessa eftersom frukter måste komma fråna ett //håll samt för att kunna ge olika startpositioner för spelarna String str = "I am server"; socket.send(new DatagramPacket(str.getBytes(), 0, str.length(), remAddress, remPort)); socket.receive(packet); str = new String(packet.getData(), packet.getOffset(), packet.getLength()); if (str.equals("I am server")){ str = "I am client"; socket.send(new DatagramPacket(str.getBytes(), 0, str.length(), remAddress, remPort)); }else if (str.equals("I am client")) isServer = true; }catch(Exception e){ e.printStackTrace(); } sb = new SnakeBuilder(this); addKeyListener(new SteerKey(sb)); //Här börjar själva spelandet. Denna tråd tar emot packet medan //skickandet sköts av klassen SnakeBuilder while (true){ try{ socket.receive(packet); recivePoint(new String(packet.getData(), packet.getOffset(), packet.getLength())); }catch(Exception ioe){ ioe.printStackTrace(); } } } }