Issues regarding multiplayer networking: input

108 views Asked by At

We are making a six-player multi-player game for school and I am responsible for a networking class. The game incorporates Slick 2D. In the game there are entities (code shown below)

    package Game; 

import org.newdawn.slick.*;

public abstract class Entity {
    double maxHealth, health, speed, damage, width, height, locationX, locationY;
    Animation standing, walkingLeft, walkingRight, attacking, jumping, current;
    long pastTime = 0;

    public boolean isReadyToAttack(long delta) {
        if(pastTime < 2 * 500) { //multiply by 1000 to get milliseconds
            pastTime += delta;
            return false;
        }else{
            pastTime = 0;
            return true;
        }
    }

    public void setMaxHealth(double d){
        maxHealth = d;
    }
    public void setHealth(double d){
        health = d;
    }

    public void setSpeed(double x){
        speed = x;
    }

    public void setDamage(double x){
        damage = x;
    }
    public void setLocation(double x, double y){
        locationX = x;
        locationY = y;
    }
    public void setSprites(Animation s, Animation r){
        standing = s;
        walkingRight = r;
    }
    public void setCurrent(Animation a){
        current = a;
    }
    public double getMaxHealth(){
        return maxHealth;
    }
    public double getHealth(){
        return health;
    }

    public double getSpeed(){
        return speed;
    }

    public double getDamage(){
        return damage;
    }

    public double getLocationX(){
        return locationX;
    }

    public double getLocationY(){
        return locationY;
    }
    public Animation getStanding(){
        return standing;
    }
    public Animation getRight(){
        return walkingRight;
    }
    public Animation getCurrent(){
        return current;
    }
    public abstract double getAttackRange();
}

The entities class is also extended by the Heroes class which is further extended by the Ares class. Ares is one of the six characters in our game and when you start the game you can choose one of the six in the start menu.

Here is the Hero class:

    package Game;

import org.newdawn.slick.Animation;

public abstract class Hero extends Entity{

    static double gravity = 0.1;
    double velocity;
    int level;
    boolean disabled, grounded = true;

    public void setLevel(int i){
        level = i;
    }
    public void setVelocity(double d){
        velocity = d;
    }
    public void setDisabled(boolean b){
        disabled = b;
    }
    public void setGrounded(boolean b){
        grounded = b;
    }
    public int getLevel(){
        return level;
    }
    public double getVelocity(){
        return velocity;
    }
    public boolean getDisabled(){
        return disabled;
    }
    public boolean getGrounded(){
        return grounded;
    }
    public double getGravity(){
        return gravity;
    }

    public boolean isAlive(){
        if(getHealth() > 0)
            return true;
        return false;
    }

    public void attack(Entity gettingAttacked, Hero attacking) {
        gettingAttacked.setHealth(gettingAttacked.getHealth() - attacking.getDamage());
    }

    public boolean isReadyToRegenerate(long delta) {
        if(pastTime < 1 * 1000){
            pastTime += delta;
            return false;
        }
        else{
            pastTime = 0;
            return true;
        }
    }   

    public static void regenerate(Hero h, long delta){
        if(h.getHealth() >= 0 && h.getHealth() < h.getMaxHealth())
            if(h.isReadyToRegenerate(delta))
                h.setHealth(h.getHealth() + (h.getMaxHealth()) * 0.01);
    }
    public abstract void levelUp();
}

Here is the Ares class:

   package Game;

import org.newdawn.slick.Animation;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.SpriteSheet;

public class Ares extends Hero {

    public Ares() throws SlickException {
        setHealth(500);
        setSpeed(0.4);
        setDamage(70);
        setLocation(850,625);
        setSprites(new Animation(new SpriteSheet("res/Ares.png", 191, 275), 200), new Animation(new SpriteSheet("res/AresWalk.png", 191, 275), 200));
    }

    public void levelUp() {
        level += 1;
        health *= 1.1;
        damage *= 1.1;
    }

    public double getAttackRange() {
        return 250;
    }

    public double maxHealth() {
        return 500;
    }

}

Here is the Battle code, which contains the main gameplay code, the code is currently uncompleted and only has movement:

 package Game;

import java.util.ArrayList;

import org.lwjgl.input.*;
import org.newdawn.slick.*;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.state.*;

public class Battle extends BasicGameState {

    private ArrayList<Entity> entities = new ArrayList<Entity>();
    private Hero player;
    private Image background;
    private int midScreen, ground;

    public void init(GameContainer gc, StateBasedGame sbg) throws SlickException{
        gc.setVSync(true);
        player = new Ares();
        player.setCurrent(player.getStanding());
        entities.add(player);
        background = new Image("res/Background.png");
        midScreen = 800;
        ground = 625;
    }


    public void render(GameContainer gc, StateBasedGame sbg, Graphics g) throws SlickException{
        g.translate((float)-player.getLocationX() + midScreen, (float)-player.getLocationY() + ground);
        background.draw(0, -920);
        for(int i = 0; i < entities.size(); i++)
            entities.get(i).getCurrent().draw((float)entities.get(i).getLocationX(), (float)entities.get(i).getLocationY());
    }

    public void update(GameContainer gc, StateBasedGame sbg, int delta) throws SlickException {
            player.getCurrent().update(delta); // ensures
            Input kb = gc.getInput();

            if (kb.isKeyDown(Input.KEY_SPACE) && player.getGrounded()) {
                player.setGrounded(false);
                player.setVelocity(7);
               player.setLocation(player.getLocationX(), player.getLocationY() - player.getVelocity());
                player.setCurrent(player.getStanding());
            }
            else if (kb.isKeyDown(Input.KEY_A) && player.getLocationX() > 800) {
                player.setLocation(player.getLocationX() - player.getSpeed() * delta, player.getLocationY());
                if(player.grounded)
                    player.setCurrent(player.getRight());
            }
            else if (kb.isKeyDown(Input.KEY_D) && player.getLocationX() < 8580) {
                player.setLocation(player.getLocationX() + player.getSpeed() * delta, player.getLocationY());
                if(player.grounded)
                    player.setCurrent(player.getRight());
            }
            else
                player.setCurrent(player.getStanding());

            if (Math.abs(player.getLocationY() - ground) < .01)
                player.setGrounded(true);
            if (player.getGrounded() == false) {
                player.setVelocity(player.getVelocity() - player.getGravity());
                player.setLocation(player.getLocationX(), player.getLocationY() - player.getVelocity());
            }
            //else if (kb.isKeyDown(Input.KEY_A)) //&& (!(aresRect.intersects(turretRect))))
            //{
            //  x -= 0.3 * delta;
            //}        
            //if(kb.isKeyDown(Input.KEY_S)) 
            //  y += 0.3 * delta;
            //else if (kb.isKeyDown(Input.KEY_W)) //&& (!(aresRect.intersects(turretRect))))
            //    y -= 0.3 * delta;   
    }    

    public int getID(){
        return 1;
    }
}

What I am having difficulty with is creating a server and client that gets the input from the players, such as location, who chose which player, etc. I'm wondering that if in Battle, when Player objects are made, how would I be able to get location and other specific attributes of the objects and give them to the other computers?

So far, the furthest I have gotten with networking code is a chat server, but I am confused as to how to apply parts of that information into the game to include getting input from a user. I have looked at a bunch of tutorials on creating a multi-player network, but I'm still confused. As for my experience, I've had around 2 years experience coding (only one with Java) but this is my first time working with networking. I experimented with a UDP connection, here is my Server:

    package net;


import java.io.*;
import java.net.*;
import java.util.*;

import main.Game;
import packets.*;
import packets.Packet.PacketTypes;

public class GameServer extends Thread{
    private DatagramSocket socket;
    private Game game;
    private List<PlayerMP> connectedPlayers = new ArrayList <PlayerMP>();

    public GameServer(Game game){ 
        this.game = game;
        try {
            socket = new DatagramSocket(7777);
        } catch (SocketException e) {
            e.printStackTrace();
        }
    }

    public void run(){
        while(true){
            byte[] data = new byte[1024];
            DatagramPacket packet = new DatagramPacket(data, data.length);
            try {
                socket.receive(packet);
            } catch(IOException e) {
                e.printStackTrace();
            }
            parsePacket(packet.getData(), packet.getAddress(), packet.getPort());

        }
    }


    private void parsePacket(byte[] data, InetAddress address, int port) {
        String message = new String(data).trim();
        PacketTypes type = Packet.lookupPacket(message.substring(0,  2));
        Packet packet = null;
        switch(type) {
        default :
        case INVALID:
            break;
        case LOGIN:
            packet = new Packet00Login(data);
            System.out.println("[" + address.getHostAddress() + ":" + port + "] " + ((Packet00Login) packet).getUsername() + 
                    " has connected.");
            PlayerMP player = new PlayerMP(address, port);
            this.addConnection(player, ((Packet00Login)packet));
            this.connectedPlayers.add(player);
            break;
        case DISCONNECT:
            break;
        case MOVE:
            packet = new Packet02Move(data);
            System.out.println(((Packet02Move)packet).getName() +"has moved to" + ((Packet02Move)packet).getX() + "," + ((Packet02Move)packet).getY());
            this.handleMove((Packet02Move)packet);
        }

    }

    private void handleMove(Packet02Move packet) {
        if (getPlayerMP(packet.getUsername()) != null){
            int index = getPlayerMPIndex(getName());
            this.connectedPlayers.get(index).locationY = packet.getX();
            this.connectedPlayers.get(index).locationX = packet.getX();
            packet.writeData(this);

        }
    }

    public void addConnection(PlayerMP player, Packet00Login packet) {
        boolean alreadyConnected = false;
        for(PlayerMP p : this.connectedPlayers) {
            if (player.getUsername().equalsIgnoreCase(p.getUsername())) {
                if(p.ipAddress == null){
                    p.ipAddress = player.ipAddress;
                }
                if(p.port == -1) {
                    p.port = player.port;
                }
                alreadyConnected = true;
            }  else {
                sendData(packet.getData(), p.ipAddress, p.port);
                packet = new Packet00Login(p.getUsername());
                sendData(packet.getData(), player.ipAddress, player.port);
            }
            if (alreadyConnected){
                this.connectedPlayers.add(player);
                packet.writeData(this);
            }
        }
    }

    public void sendData(byte[] data, InetAddress ipAddress, int port) {
        DatagramPacket packet = new DatagramPacket(data, data.length, ipAddress, port);
        try {
            socket.send(packet);
        } catch(IOException e) {
            e.printStackTrace();
        }
    }

    public void sendDataToAllClients(byte[] data) {
        for(PlayerMP p : connectedPlayers)
            sendData(data, p.ipAddress, p.port);
    }
}

I could not figure out how to get much further than sending a message back and forth with a TCP connection:

    import java.io.*;
import java.net.*;

public class Server {
    private static ServerSocket serverSocket;
    private static Socket socket;
    private static DataOutputStream out;
    private static Users[] user = new Users[6];
    private static DataInputStream in;

    public static void main(String[] args) throws Exception{
        System.out.println("Starting server");
        serverSocket = new ServerSocket(0);
        System.out.println("Server Started");
        while(true){
        socket = serverSocket.accept();
        for (int i = 0; i < 6; i++){
        System.out.println("Connection from: " + socket.getInetAddress());
        out = new DataOutputStream(socket.getOutputStream());
        in = new DataInputStream(socket.getInputStream());
        if (user[i] == null){
            user[i] = new Users(out, in, user);
            Thread thread = new Thread();
            thread.start();
            break;
        }
        }
        }
    }
}

class Users implements Runnable{
    private DataOutputStream out;
    private DataInputStream in;
    private Users[] user = new Users[6];
    public Users(DataOutputStream fromOut, DataInputStream goingIn, Users[] userArr){
        out = fromOut;
        in = goingIn;
        user = userArr;
    }
    public void run(){
        while(true){
            try{
                String message = in.readUTF();
                for (int i = 0; i < 6; i++){
                    if (user[i] != null)
                        user[i].out.writeUTF(message);
                }
            }catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

I would like to figure this out. I am not even completely sure where to start or whether or not I am on the right track.

Note: I am coding in Java, and trying to use a TCP connection to code this. I am open to UDP if you think it may be easier.

0

There are 0 answers