Package plar.ClientServer

Source Code of plar.ClientServer.Connect

/*
  PlAr is Platform Arena: a 2D multiplayer shooting game
  Copyright (c) 2010, Antonio Ragagnin <spocchio@gmail.com>
  All rights reserved.

  This file is licensed under the New BSD License.
*/
package plar.ClientServer;

import java.awt.Point;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.*;

import java.net.SocketAddress;
import java.net.SocketException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import plar.ClientServer.Signal.Info;
import plar.ClientServer.Signal.LevelInfo;
import plar.ClientServer.Signal.Login;

import plar.core.Common;
import plar.core.Game;
import plar.core.KeyFlag;
import plar.core.Level;
import plar.core.ElementPlayer;
import plar.core.SpriteSet;

/**
*
* 1. Server class receive UDP packets and read the ID and Key flag
* 2. if them are both null, create a new ID,Key pair and send it to the Client
*    Create also a new Connect() class to handle packets with this Id,Key pair
*
* data of an UDP packed:
* 1. first byte is the ID
* 2. second byte is the Key
* 3. third byte is the SIGNAL (see Signal.Java)
* 4. the other data could be anything, Connect() read the SIGNAL and decide what kind of data
*    is stored in the packet.
*
* Compressions:
*  for each SpriteSet for each Element in the Level, each SpriteSet is sent only ONE TIME.
*  Repeated SpriteSet are not sent, SpriteSet of an element that is not yet shown is not sent.
*
*  when the client request the content of a string an array of byte is sent, the logical data is:
*  - for each Element in the screen: (int)x,(int)y,(int)angle,(int)SpriteSetId,(int)forFutureUse
*   (forFutureUse it could become the speed in pixel units for local interpolations)
*  - these 4 int, that use almost TEN BITS, are compressed in 5 byte
*
*  the Client send the user input (stored in KeyFlag) once when repeated
*  so the Server, send to Game the last input that the client had sent.
*  for example
*  - Client says the Left key was pressed
*  - Server says to Game that Left Key was pressed until Client don't change it.
*  For this reason is NECESSARY that client send an EMPTY KeyFlag when the user
*  stop pressing keys
*
*  Sprite and Element s:
*  each Element in the Level should use a SpriteSet that contains an Array of Sprite.
*  there are a LOT of elements with equal SpriteSet (usually statics)
*  when the Client request the Screen, Server check if in the screen there are NEW SpriteSet
*  that had not been sent yet,( these SpriteSet are stored in ArrayList <SpriteSet> buffer; )
*  if yes it send the new SpriteSet and add it to "buffer", the Client should do the same
*  and they buffer index should be sincronized.
*  FOR EACH SpriteSet is used an UDP with the Signal.SPRITE
*
*  For each Element in the Screen the server send the coordinates, the angle and the SpriteSetID stored in buffer.
*
* @author Antonio Ragagnin
*
*/

class Connect {
  public Game game;
  private ArrayList<SpriteSet> buffer;
  public byte Key;
  public byte Id;
  DatagramSocket server;
  SocketAddress addr;
  ElementPlayer p;
  ArrayList<ShownElement> screen;
  ArrayList<Integer> indexToSend;
  KeyFlag lastInput;
  String username="noname";
  public long lastSignal=0;
 
  Connect(Game g, DatagramSocket s, byte k,byte id, SocketAddress a)
  throws IOException {
    lastSignal=Calendar.getInstance().getTimeInMillis();;
    Key = k;
    server = s;
    game = g;
    Id=id;
    addr = a;
    Common.info(7, "Connect: delay=");
    buffer = new ArrayList<SpriteSet>();
    screen = new ArrayList<ShownElement>();
    indexToSend = new ArrayList<Integer>();
    lastInput = new KeyFlag((short) 0);
   
  }

  public  synchronized void process(byte[] data, byte signal) throws SocketException,
  IOException, ClassNotFoundException {
    lastSignal=Calendar.getInstance().getTimeInMillis();
    // Common.info(1, "client processing data.length:"+data.length);
    if (signal == Signal.LOGIN) {
      Login login;
      login = (Login) Signal.toObject(data);
      // Common.info(1, "loginclass:"+login);
      String name = login.userName;
      username=name;
      String type = login.playerName;
      if(game.playerList.contains(type))
      type = Common.playersPackage + type;
      else
      type = Common.playersPackage + game.playerList.get(0);

      try {
        @SuppressWarnings("unchecked")
        Class c = Class.forName(type);
        p = (ElementPlayer) c.newInstance();
      } catch (Exception e) {

        Common.info(1, "Connect.process() LOGIN: failed loading " + type);
        close();

      }
      p.username=name;
      game.addPlayer(p);

      Common.info(1, "Connect.run(): new player: " + p + "  name=" + name
      + " type=" + type);
    }
    if (signal == Signal.WELCOME) {
      LevelInfo li = new LevelInfo();
      Common.info(1,"received a WELCOME!");

      List <String> lp=game.playerList;
      List <String> lg=game.gunList;
      li.MOTD = "Welcome to PlAr server v"+Common.version+" \n";
      li.MOTD += " \n";
      li.MOTD += " The game is running the level \""+game.level.description+"\"\n";
      li.MOTD += " \n";
      li.MOTD += " The are "+game.level.getPlayers().size()+" players online: \n";

      li.MOTD += " \n";
      li.MOTD += " The are "+lg.size()+" aviable guns in the game:\n";
      li.MOTD += Common.joinAL(lg);
      li.MOTD += " \n";
      li.MOTD += " The are "+lg.size()+" aviable players \n";
      li.MOTD += Common.joinAL(lp);
      li.MOTD += " \n";
      li.MOTD += "Please enter the kind of player (case sensitive) \n";

      li.resolution = game.screenResolution;
      li.scaleX = game.scale.x;
      li.scaleY = game.scale.y;
      //li.guns=game.gunList;


      server.send(Signal.send(li, (byte) 0, (byte) 0, Signal.LEVELINFO, addr));
      Common.info(1,"sent a WELCOME!");
    } else if (signal == Signal.SPRITE || signal == Signal.SPRITEANDSCREEN) {
      // Common.info(8,"REQUESTED SPRITES!");

      screen = game.getScreen(p);
      indexToSend = new ArrayList<Integer>();
      ArrayList<Integer> indexToBeAdded = new ArrayList<Integer>();
      for (int i = 0; i < screen.size(); i++) {
        ShownElement e = screen.get(i);
        boolean exist = false;
        for (int j = 0; (j < buffer.size() && !exist); j++) {
          if (e.spriteSet.equals(buffer.get(j))) {
            exist = true;
            // Common.info("found element in position "+j);
            indexToSend.add(j);
          }
        }
        if (!exist) {
          buffer.add(e.spriteSet);
          indexToBeAdded.add(buffer.size() - 1);
          indexToSend.add(buffer.size() - 1);
        }
      }
      for (int i = 0; i < indexToBeAdded.size(); i++) {
        Object o = buffer.get(indexToBeAdded.get(i));
        server.send(Signal.send(o, (byte) 0, (byte) 0, Signal.SPRITE,
        addr));
      }

    } else if (signal == Signal.DIRECTION) {

      byte x = data[0];
      byte y = data[1];
      float dx = (float) x;
      float dy = (float) y;
      dx /= 127;
      dy /= 127;
      Common.info(1, "Connect.run(): someone clicked the mouse: dirx" + dx
      + " diry" + dy + "");
      p.directionX = dx;
      p.directionY = dy;
    } else if (signal == Signal.MESSAGE) {
      String c = (String) Signal.toObject(data);
      game.sendChat(p, c);
      Common.info(1,"<"+p.username+">" +c);
    } else if (signal == Signal.INFO) {
      if(p==null)
      {
        sendMessage("OPS! Server received signals in the wrong order. (LOGIN,INFO)");
      }else{
      Info info = new Info();
      info.chat = game.getChat();
      info.life = p.energy;
      info.score = p.kills;
      info.spawns=p.killed;
      info.guns=game.getGuns(p);
      info.remaning=game.remaning;
      //info.chat="";
      server.send(Signal
      .send(info, (byte) 0, (byte) 0, Signal.INFO, addr));
      }
    }
    if (signal == Signal.SCREEN || signal == Signal.SPRITEANDSCREEN) {
      // Common.info(8,"REQUESTED SKREEN");
      //int N = 5;
      // int N = 6;
      int N = 4;
      byte[] cData = new byte[screen.size() * N *2];
      // cData[0] = (byte) screen.size();
      // Common.info(8,"screen size:"+screen.size()+" with"+(screen.size()*6));
      for (int i = 0; i < screen.size(); i++) {
        ShownElement s = screen.get(i);
        int Itheta = (((int) (1000 * s.theta)) % 6282);

        float Ptheta = Itheta / 100;
        float Ntheta = Ptheta / 6.2831f;

        int[] by = { s.position.x, s.position.y, Itheta,
          indexToSend.get(i), 0
        };
       
        /*
        byte[] c = Signal.from5IntTo6Bytes(by);
        cData[0 + i * N] = c[0];
        cData[1 + i * N] = c[1];
        cData[2 + i * N] = c[2];
        cData[3 + i * N] = c[3];
        cData[4 + i * N] = c[4];
        */
        cData[0 + i *2* N] = Signal.From1ShortToFByte((short)s.position.x);
        cData[1 + i *2* N ] = Signal.From1ShortToSByte((short)s.position.x);
        cData[2 + i *2* N] = Signal.From1ShortToFByte((short)s.position.y);
        cData[3 + i *2* N ] = Signal.From1ShortToSByte((short)s.position.y);
        cData[4 + i *2* N] = Signal.From1ShortToFByte((short)Itheta);
        cData[5 + i *2* N ] = Signal.From1ShortToSByte((short)Itheta);
        cData[6 + i *2* N] = Signal.From1ShortToFByte((short)((int)indexToSend.get(i)));
        cData[7 + i *2* N ] = Signal.From1ShortToSByte((short)((int)indexToSend.get(i)));
        //        cData[3 + i * N] = (short)((int)indexToSend.get(i));
        /*  Common.info(1,"sending: "+s.position.x+":["+cData[0 + i * 2*N]+" "+cData[1 + i *2* N]+"]"
                    +s.position.y+":["+cData[2 + i *2* N]+" "+cData[3 + i *2* N]+"]"
                    +0+":["+cData[4 + i *2* N]+" "+cData[5 + i *2* N]+"]"
                    +((int)indexToSend.get(i))+":["+cData[6 + i *2* N]+" "+cData[7 + i *2* N]+"]"
                    );
        */
        //cData[4 + i * N] = c[4];
        // cData[5+i*N]=c[5];

      }
      server.send(Signal.send(cData, (byte) 0, (byte) 0, Signal.SCREEN,
      addr));
    } else if (signal == Signal.INPUT) {
      KeyFlag strc = null;
      try {
        short key = (Short) Signal.toObject(data);
        strc = new KeyFlag(key);
      } catch (Exception e) {
        strc = lastInput;
      }

      lastInput = strc;
      game.sendInput(p, strc);
    }


    if (signal == Signal.STOP) {
      close();

    }
  }
  public  synchronized  void sendMessage(String s)
  {
    try{
      server.send(Signal
      .send(s, (byte) 0, (byte) 0, Signal.MESSAGE, addr));
    }catch(Exception e)
    {
      Common.info(1," ERROR sendin message "+s+" to "+Id);
    }
  }
  public  synchronized  void close() {
    game.sendChat( username + " has quit the game!");
    if(p!=null)    game.level.delElement(p);
  }

}

public class Server extends Thread {
  public boolean active = true;
  public DatagramSocket dateServer;
  public Game g;
  public Level level;
  public HashMap<String, ElementPlayer> playerList;
  public HashMap<Byte, Connect> connections;
  public long refresh = 10;
  public long timeout = 60*1000;
  public Point resolution;
  public long duration;
  public long now;
  public long start;
 

  public Server() {
    super();
    resolution = new Point(700, 700);
    connections = new HashMap<Byte, Connect>();
    duration=60*1000*(new Long(Common.getConfig().getProperty("time")));
    now=Common.now();
    start=Common.now();
  }

 
  public void close() {
    active = false;
    for (Byte b : connections.keySet()) {
      connections.get(b).close();

    }
    dateServer.close();
    this.interrupt();

  }

  public boolean listen(int port) {
    try {

      dateServer = new DatagramSocket(port);

      System.out.println("Server listening on port " + port + ".");
      this.start();
    } catch (Exception e) {
      System.out.println("failed listeninng.");
      return false;
    }
    return true;
  }

  public void newGame(Level l) {
    g = new Game();
    g.screenResolution = resolution;

    g.setLevel(l);
    g.startGame();
  }
  public String getScore()
  {
    Collection cc = connections.values();
    String finale="<html><table><tr><td>Username</td><td>Score</td><td>Deaths</td>";
    {
      Iterator itr = cc.iterator();
     
      while(itr.hasNext())
      {
       
        Connect c = (Connect)itr.next();
        if(c!=null){
          finale+="<tr><td>["+c.p.username+"]</td><td> "+(c.p.kills)+"</td><td> "+(c.p.killed)+"</td></tr>";
         
        }
      }
      finale+="</html>";
    }
    return finale;
   
  }
  public void sendBroadcast(String finale){
   
    Collection cc = connections.values();
    Iterator itr = cc.iterator();
    while(itr.hasNext())
    {
     
      Connect c = (Connect)itr.next();
      if(c!=null){
        c.sendMessage(finale);
       
      }
    }
   
  }
  public void restartGame()
  {
    String msg="Game Finished!\n\nHighscores:\n"+getScore();
   
    sendBroadcast(msg);        
   
    g.restart();
    start=Common.now();
   
  }
  public void loadGame() {
    class autoupdate extends Thread {

      public void run() {

        try {
          while (active) {
           
            g.run();
            Thread.sleep(refresh);
          }
        } catch (Exception e) {
          Common.info(1," --------------------------------------- ");
          e.printStackTrace();
        }
      }
    }
   
   
   
   
    class FindTimeout extends Thread {

      public void run() {

       
        try {
          while (active) {
            Thread.sleep(timeout);
            syncrun();
           
           
          }
        }          catch (InterruptedException e) {

          e.printStackTrace();
        }
      }
     
      public void syncrun()
      {
        synchronized (connections)
        {
         
          Collection cc = connections.values();
          Iterator itr = cc.iterator();

          //iterate through HashMap values iterator
          while(itr.hasNext())
          {
           
            Connect c = (Connect)itr.next();
            if(c!=null){
             
             
              if((c.lastSignal+timeout)<now) {
                Common.info(1,"FOUND GHOST!");
                c.close();
                connections.remove(c.Id);
              }
            }else Common.info(1,"* c="+c);
         
        }
       
       
      }
     
    }

    class RestartMatch extends Thread {

      public void run() {
        try {
         
          while (active) {
           
            Thread.sleep(duration);
            syncrun();
          }
        }catch (InterruptedException e) {

          e.printStackTrace();
        }
       
       
      }
     
     
     
     
      public void syncrun()
      {
       
        synchronized(connections)
        {
          restartGame();
         
        }
       
       
      }
    }
    Thread t1 = new autoupdate();
    t1.setName("Thread principale");
    t1.setPriority(10);
    t1.start();
   
    Thread t2 = new FindTimeout();
    t2.setName("Thread ghost");
    t2.setPriority(10);
    t2.start();
   
    Thread t3 = new RestartMatch();
    t3.setName("Thread mathes");
    t3.setPriority(10);
    t3.start();

    Common.info(1, "Server.newGame() refreshtime=" + refresh + " level="
    + level);
  }

  public void run() {
    try {
      while (active) {
        syncrun();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

  }
  public void syncrun() throws Exception
  {
   
    now=Common.now();
    g.remaning=start+duration-now;
    byte[] data = new byte[dateServer.getSendBufferSize()];
    DatagramPacket dp = new DatagramPacket(data, 2048);
    // System.out.println("Waiting for datagrams");
    dateServer.receive(dp);
    data = Arrays.copyOf(data, dp.getLength());
    // System.out.println("Waiting for datagrams"+dp.getData().length);
    // = dp.getData();
    if (data.length >= 2) {
      byte ID = Signal.getID(data);
      byte Key = Signal.getKey(data);
      // Common.info(1,
      // "received a packet. ID="+ID+" KEY="+Key+" from:"+dp.getSocketAddress());
      synchronized (connections){
        if (connections.containsKey(ID)
            && connections.get(ID).Key == Key) {
          // Common.info(1, "it matches in the database");
          byte sig = Signal.getSignal(data);
          // Common.info(1, "signal = "+sig);
          byte[] newData = Signal.getData(data);
          connections.get(ID).process(newData, sig);
        } else if (Key == 0) {

          // log the new client
          byte newID = (byte) (connections.size() + 1);
          byte newKey = (byte) (connections.size());
          Common.info(1, "created new connection:  ID=" + newID
          + " KEY=" + newKey);

          dateServer.send(Signal.send(new byte[0], newID, newKey,
          (byte) 0, dp.getSocketAddress()));
          Connect c = new Connect(g, dateServer, newKey,newID, dp
          .getSocketAddress());
          connections.put(newID, c);
        }
      }
    }
   
  }

}
TOP

Related Classes of plar.ClientServer.Connect

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.