Server-client connection protocol using UUIDs

master
EmaMaker 2020-04-18 19:38:09 +02:00
parent 8f02390188
commit 0a85762a93
8 changed files with 568 additions and 270 deletions

View File

@ -46,13 +46,13 @@ public class AMazeIng extends Game {
}
public void setupGUI() {
uiManager = new UIManager(this);
System.out.println("Setup UI Manager");
uiManager = new UIManager(this);
}
public void setupGameManager() {
gameManager = new GameManager(this);
System.out.println("Setup Game Manager");
gameManager = new GameManager(this);
}
float delta;

View File

@ -3,6 +3,7 @@ package com.emamaker.amazeing.manager;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.UUID;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net.Protocol;
@ -12,14 +13,19 @@ import com.emamaker.amazeing.AMazeIng;
public class GameClient {
public AMazeIng main;
Thread clientThread;
volatile boolean clientRunning = false;
volatile String nextMessage = "";
public Socket socket;
String s = "", s1 = "";
public String addr;
public int port;
Thread clientThread;
public AMazeIng main;
public Socket socket;
volatile String nextMessage = "";
String s = "", s1 = "", rMsg = "";
UUID uuid;
int connectionStep;
public GameClient(AMazeIng main_) {
main = main_;
@ -36,40 +42,34 @@ public class GameClient {
while (clientRunning) {
if (socket == null) {
SocketHints socketHints = new SocketHints();
socketHints.connectTimeout = 0;
socketHints.connectTimeout = 10000;
socket = Gdx.net.newClientSocket(Protocol.TCP, addr, port, socketHints);
System.out.println("Connected to server!");
sendMessagetoClients("AO");
sendMessageToSocket(socket, "AO");
connectionStep = 0;
} else {
if (socket.isConnected()) {
// Receive messages from the server
try {
if (socket.getInputStream().available() > 0) {
BufferedReader buffer = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
rMsg = receiveMessageFromSocket(socket);
// Read to the next newline (\n) and display that text on labelMessage
s = buffer.readLine().toString();
System.out.println("Received message from server: " + socket + "!\n" + s);
if(s.startsWith("Map")) {
s1 = s.replace("Map", "");
main.gameManager.generateMaze(main.gameManager.mazeGen.runLenghtDecode(s1));
//System.out.println(s1);
}
}
} catch (IOException e) {
if (rMsg.startsWith("UUID")) {
// UUID received from server, go on
//it doesn't matter if this already happened, just start again
//No new UUID will be generated
s1 = rMsg.replace("UUID", "");
uuid = UUID.fromString(s1);
connectionStep = 1;
System.out.println("Got UUID from server: " + uuid.toString());
sendMessageToSocket(socket, "AO2");
}
// Send messages to the client
if (!nextMessage.equals(""))
try {
// write our entered message to the stream
socket.getOutputStream().write((nextMessage + "\n").getBytes());
} catch (IOException e) {
// e.printStackTrace();
if (rMsg.equals("AO3")) {
if (connectionStep == 1) {
connectionStep = 2;
sendMessageToSocket(socket, "AO4");
System.out.println("Connected with server!");
}
}
}
nextMessage = "";
}
}
}
@ -77,11 +77,71 @@ public class GameClient {
clientThread.start();
}
public void sendMessagetoClients(String s) {
// Update must be called from Main thread and used for applications on main
// thread
public void update() {
}
public void sendMessagetoServer(String s) {
nextMessage += (s + "\n");
}
public void stop() {
clientRunning = false;
}
void sendMessageToSocket(Socket s, String msg) {
if (s != null && s.isConnected())
try {
// write our entered message to the stream
System.out.println("Client sending msg: " + msg + " to " + s);
s.getOutputStream().write((msg + "\n").getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
String tmprs;
String receiveMessageFromSocket(Socket s) {
if (s != null && s.isConnected())
// Receive messages from the client
try {
if (s.getInputStream().available() > 0) {
BufferedReader buffer = new BufferedReader(new InputStreamReader(s.getInputStream()));
// Read to the next newline (\n) and display that text on labelMessage
tmprs = buffer.readLine();
System.out.println("Client received message From: " + s + "!\n" + tmprs);
return tmprs;
}
} catch (IOException e) {
}
return "";
}
// // Receive messages from the server
// try {
// if (socket.getInputStream().available() > 0) {
// BufferedReader buffer = new BufferedReader(
// new InputStreamReader(socket.getInputStream()));
//
// // Read to the next newline (\n) and display that text on labelMessage
// s = buffer.readLine().toString();
// //System.out.println("Received message from server: " + socket + "!\n" + s);
// if(s.startsWith("Map")) {
// s1 = s.replace("Map", "");
// main.gameManager.generateMaze(main.gameManager.mazeGen.runLenghtDecode(s1));
//
// }
// }
// } catch (IOException e) {
// }
// // Send messages to the client
// if (!nextMessage.equals(""))
// try {
// // write our entered message to the stream
// socket.getOutputStream().write((nextMessage + "\n").getBytes());
// } catch (IOException e) {
//// e.printStackTrace();
// }
}

View File

@ -37,6 +37,9 @@ public class GameManager {
boolean anyoneWon = false;
public void update() {
server.update();
client.update();
if (gameStarted) {
main.world.cam.position.set(mazeGen.w / 2, (MazeSettings.MAZEX + MazeSettings.MAZEZ) * 0.45f,
mazeGen.h / 2);
@ -189,6 +192,7 @@ public class GameManager {
public void generateMaze(int todraw[][]) {
generateMaze(null, todraw);
}
public void generateMaze(Set<MazePlayer> pl) {
generateMaze(pl, null);
}

View File

@ -4,6 +4,9 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.UUID;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Net.Protocol;
@ -13,18 +16,38 @@ import com.badlogic.gdx.net.Socket;
import com.badlogic.gdx.net.SocketHints;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.emamaker.amazeing.AMazeIng;
import com.emamaker.amazeing.player.MazePlayer;
import com.emamaker.amazeing.player.MazePlayerRemote;
public class GameServer {
public AMazeIng main;
volatile boolean serverRunning = false;
volatile String nextMessage = "";
volatile String rMsg;
public ServerSocket serverSocket = null;
public int port;
ArrayList<Socket> clientSockets = new ArrayList<Socket>();
Thread serverThread;
public AMazeIng main;
Socket s;
Thread serverThread;
// An HashTable that stores remote players, starting from a UUID and the
// relative socket
// Player must be added here only once received the last acknowledgment message
// from client
volatile Hashtable<Socket, FullPlayerEntry> remotePlayers = new Hashtable<>();
// An HashTable that stores remote players waiting to be accepted, starting from
// a UUID and the
// relative socket. The integer is needed to store the current phase the
// acknowledgment is at (Starting from 0), if it's not equals to the expected
// one the connectiond gets dropped and socket removed from here
volatile Hashtable<Socket, TmpPlayerEntry> tmpPlayers = new Hashtable<>();
// New players to be added into remotePlayers in the main thread, holds Sockets
// used to get player info from tmpPlayers
volatile ArrayList<Socket> newPlayers = new ArrayList<>();
volatile TmpPlayerEntry currentTmpEntry;
SocketHints socketHints = new SocketHints();
ServerSocketHints serverSocketHint = new ServerSocketHints();
@ -35,7 +58,8 @@ public class GameServer {
public void startServer(int port_) {
port = port_;
serverSocketHint.acceptTimeout = 100;
serverSocketHint.acceptTimeout = 1000;
socketHints.connectTimeout = 10000;
serverThread = new Thread(new Runnable() {
@Override
@ -43,46 +67,75 @@ public class GameServer {
serverRunning = true;
while (serverRunning) {
if (serverSocket == null) {
serverSocket = Gdx.net.newServerSocket(Protocol.TCP, port, serverSocketHint);
System.out.println("Server started and listening for connections!");
}
try {
Socket socket = serverSocket.accept(null);
if (socket != null && !clientSockets.contains(socket)) {
clientSockets.add(socket);
System.out.println("Accepted new client: " + socket);
Socket socket = serverSocket.accept(socketHints);
if (socket != null && !tmpPlayers.contains(socket)
&& !remotePlayers.keySet().contains(socket)) {
tmpPlayers.put(socket, new TmpPlayerEntry(socket, UUID.randomUUID(), 0));
System.out.println(
"Accepted new tmp client: " + socket + " with ip " + socket.getRemoteAddress());
}
} catch (GdxRuntimeException se) {
// System.out.println("No new clients connected in the last 100 milliseconds");
// System.out.println("No new clients connected in the last 1000 milliseconds");
}
for (Socket s : clientSockets) {
System.out.println(Arrays.toString(tmpPlayers.keySet().toArray()));
System.out.println(Arrays.toString(remotePlayers.keySet().toArray()));
if(!main.gameManager.gameStarted) {
for (Socket s : tmpPlayers.keySet()) {
if (s.isConnected()) {
// Receive messages from the client
try {
if (s.getInputStream().available() > 0) {
BufferedReader buffer = new BufferedReader(
new InputStreamReader(s.getInputStream()));
// Read to the next newline (\n) and display that text on labelMessage
System.out
.println("Server received message From: " + s + "!\n" + buffer.readLine());
rMsg = receiveMessageFromSocket(s);
System.out.println(rMsg);
if (rMsg.equals("AO")) {
// Step 0
if (tmpPlayers.get(s).step == 0) {
// We're at the right step, send uuid to client
sendMessageToSocket(s, "UUID" + tmpPlayers.get(s).uuid.toString());
} else {
// Wrong step
System.out.println("Client already connected, ignoring request");
}
} catch (IOException e) {
// If receiving multiple AOs, just start back from this point
tmpPlayers.get(s).step = 1;
}
// Send messages to the client
if (!nextMessage.equals(""))
try {
// write our entered message to the stream
s.getOutputStream().write((nextMessage + "\n").getBytes());
} catch (IOException e) {
e.printStackTrace();
if (rMsg.equals("AO2")) {
if (tmpPlayers.get(s).step == 1) {
System.out.println("Client " + s + " accepted uuid");
sendMessageToSocket(s, "AO3");
tmpPlayers.get(s).step = 2;
}
}
if (rMsg.equals("AO4")) {
if (tmpPlayers.get(s).step == 2) {
// Player can now be added to the remote player list, but this has to be done
// inside the main thread, and this bit of code is executed outside, so pass it
// to another function for use
newPlayers.add(s);
}
}
} else {
// Cliented timed out, remove it from the list :(
tmpPlayers.remove(s);
}
}
nextMessage = "";
}
for (Socket s : tmpPlayers.keySet()) {
if (s.isConnected()) {
} else {
// Cliented timed out, remove it from the list :(
remotePlayers.remove(s);
}
}
}
}
@ -94,13 +147,29 @@ public class GameServer {
nextMessage += (s + "\n");
}
// Update must be called from Main thread and used for applications on main
// thread, such as spawning new players
public void update() {
// Spawn new players if needed
for (Socket s : newPlayers) {
remotePlayers.put(s, new FullPlayerEntry(s, tmpPlayers.get(s).uuid, new MazePlayerRemote(main, s)));
System.out.println("Accepted new player: " + s.toString() + " " + tmpPlayers.get(s).uuid.toString());
tmpPlayers.remove(s);
}
newPlayers.clear();
}
// Once the server has started accepting connections from other players, the
// host should decide when to start the gmae
// A proper ui should be added, but for now we can just start the game without
// showing any players and just show the map across all the clients
public void startGame() {
main.gameManager.generateMaze();
sendMessagetoClients("Map" + main.gameManager.mazeGen.runLenghtEncode());
// ArrayList<MazePlayer> players = new ArrayList<>();
// for(MazePlayer p : remotePlayers.values() ) players.add(p);
// players.add(new MazePlayerLocal(main, Keys.W, Keys.S, Keys.A, Keys.D));
// System.out.println(Arrays.toString(players.toArray()));
// main.gameManager.generateMaze(new HashSet<MazePlayer>(players));
// sendMessagetoClients("Map" + main.gameManager.mazeGen.runLenghtEncode());
}
public void stop() {
@ -110,4 +179,66 @@ public class GameServer {
}
serverRunning = false;
}
void sendMessageToSocket(Socket s, String msg) {
if (s != null && s.isConnected())
if (!msg.equals(""))
try {
// write our entered message to the stream
System.out.println("Server sending msg: " + msg + " to " + s);
s.getOutputStream().write((msg + "\n").getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
String tmprs;
String receiveMessageFromSocket(Socket s) {
if (s != null && s.isConnected()) {
// Receive messages from the client
try {
if (s.getInputStream().available() > 0) {
BufferedReader buffer = new BufferedReader(new InputStreamReader(s.getInputStream()));
// Read to the next newline (\n) and display that text on labelMessage
tmprs = buffer.readLine();
System.out.println("Server received message From: " + s + "! " + tmprs);
return tmprs;
}
} catch (IOException e) {
}
}
return "";
}
}
class TmpPlayerEntry {
public UUID uuid;
public Socket socket;
public int step = 0;
public long lastTimeHeard;
/*
* (Look at the notes) Step 0: AO client->server first request Step 1: UUID
* server->client response with uuid Step 2: AO2 client->server client accepted
* uuid Step 3: AO3 server->client server accepted client
*/
public TmpPlayerEntry(Socket s, UUID u, int t) {
uuid = u;
socket = s;
step = t;
lastTimeHeard = System.currentTimeMillis();
}
}
class FullPlayerEntry {
public UUID uuid;
public Socket socket;
public MazePlayer player;
public FullPlayerEntry(Socket s, UUID u, MazePlayer p) {
uuid = u;
socket = s;
player = p;
}
}

View File

@ -3,8 +3,6 @@ package com.emamaker.amazeing.player;
import java.util.Random;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.Environment;
@ -15,206 +13,59 @@ import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.model.Node;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.bullet.collision.btBoxShape;
import com.badlogic.gdx.physics.bullet.collision.btBroadphaseProxy;
import com.badlogic.gdx.physics.bullet.collision.btCollisionObject;
import com.badlogic.gdx.physics.bullet.collision.btConvexShape;
import com.badlogic.gdx.physics.bullet.collision.btPairCachingGhostObject;
import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btKinematicCharacterController;
import com.emamaker.amazeing.AMazeIng;
import com.emamaker.voxelengine.physics.GameObject;
public class MazePlayer {
public abstract class MazePlayer {
AMazeIng main;
static Random rand = new Random();
// MazePlayer model building stuff
public Model MazePlayerModel;
public Model mazePlayerModel;
public ModelInstance instance;
ModelBuilder modelBuilder = new ModelBuilder();
MeshPartBuilder meshBuilder;
static int meshAttr = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal;
public GameObject obj;
btConvexShape ghostShape;
public btPairCachingGhostObject ghostObject;
public btKinematicCharacterController characterController;
Matrix4 characterTransform;
Vector3 characterDirection = new Vector3();
Vector3 walkDirection = new Vector3();
public Controller ctrl;
// Physics using LibGDX's bullet wrapper
public int kup, kdown, ksx, kdx;
float startx, starty, startz;
String name;
AMazeIng main;
boolean disposing = false;
public MazePlayer(Game main_, int up_, int down_, int sx_, int dx_) {
this(main_, up_, down_, sx_, dx_, 0, 0, 0);
MazePlayer(Game main_) {
this(main_, String.valueOf((char) (65 + rand.nextInt(26))));
disposing = false;
}
public MazePlayer(Game main_, int up_, int down_, int sx_, int dx_, String name) {
this(main_, up_, down_, sx_, dx_, 0, 0, 0, name);
}
public MazePlayer(Game main_, int up_, int down_, int sx_, int dx_, float startx, float starty, float startz) {
this(main_, up_, down_, sx_, dx_, startx, starty, startz, String.valueOf((char) (65 + rand.nextInt(26))));
}
public MazePlayer(Game main_, int up_, int down_, int sx_, int dx_, float startx, float starty, float startz,
String name) {
this.kup = up_;
this.kdown = down_;
this.ksx = sx_;
this.kdx = dx_;
this.startx = startx;
this.starty = starty;
this.startz = startz;
this.main = (AMazeIng) main_;
MazePlayer(Game main_, String name) {
main = (AMazeIng) main_;
setName(name);
buildModel();
initPhysics();
}
public MazePlayer(Game main_, Controller ctrl_) {
this(main_, ctrl_, 0, 0, 0);
}
public MazePlayer(Game main_, Controller ctrl_, String name) {
this(main_, ctrl_, 0, 0, 0, name);
}
public MazePlayer(Game main_, Controller crtl_, float startx, float starty, float startz) {
this(main_, crtl_, startx, starty, startz, String.valueOf((char) (65 + rand.nextInt(26))));
}
public MazePlayer(Game main_, Controller ctrl_, float startx, float starty, float startz, String name) {
this.ctrl = ctrl_;
this.startx = startx;
this.starty = starty;
this.startz = startz;
setName(name);
buildModel();
initPhysics();
}
// public void buildModel() {
// modelBuilder.begin();
// Node n = modelBuilder.node();
// n.id = "MazePlayer";
// modelBuilder.part("MazePlayer", GL20.GL_TRIANGLES, meshAttr, new Material()).cone(1, 1, 1, 20);
// MazePlayerModel = modelBuilder.end();
// }
//
// public void initPhysics() {
// GameObject.Constructor construct = new GameObject.Constructor(MazePlayerModel, "MazePlayer", new btConeShape(1,1),
// 30f);
// obj = construct.construct();
// obj.transform.trn(VoxelSettings.chunkSize/2, VoxelSettings.chunkSize/2, VoxelSettings.chunkSize/2);
// obj.body.proceedToTransform(obj.transform);
// obj.body.setCollisionFlags(
// obj.body.getCollisionFlags() | btCollisionObject.CollisionFlags.CF_CUSTOM_MATERIAL_CALLBACK);
// VoxelSettings.voxelWorld.dynamicsWorld.addRigidBody(obj.body);
//// obj.body.setContactCallbackFlag(VoxelSettings.OBJECT_FLAG);
//// obj.body.setContactCallbackFilter(VoxelSettings.GROUND_FLAG);
// }
//
// public void render(ModelBatch b, Environment e) {
// b.render(obj, e);
// }
public void buildModel() {
modelBuilder.begin();
Node n = modelBuilder.node();
n.id = "MazePlayer";
modelBuilder.part("MazePlayer", GL20.GL_TRIANGLES, meshAttr, new Material()).box(0.6f, 0.6f, 0.6f);
MazePlayerModel = modelBuilder.end();
instance = new ModelInstance(MazePlayerModel);
}
public void initPhysics() {
characterTransform = instance.transform; // Set by reference
characterTransform.set(startx, starty, startz, 0, 0, 0, 0);
// Create the physics representation of the character
ghostObject = new btPairCachingGhostObject();
ghostObject.setWorldTransform(characterTransform);
ghostShape = new btBoxShape(new Vector3(0.3f, 0.3f, 0.3f));
ghostObject.setCollisionShape(ghostShape);
ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT);
characterController = new btKinematicCharacterController(ghostObject, ghostShape, .05f, Vector3.Y);
// And add it to the physics world
main.world.dynamicsWorld.addCollisionObject(ghostObject,
(short) btBroadphaseProxy.CollisionFilterGroups.CharacterFilter,
(short) (btBroadphaseProxy.CollisionFilterGroups.StaticFilter
| btBroadphaseProxy.CollisionFilterGroups.DefaultFilter));
((btDiscreteDynamicsWorld) (main.world.dynamicsWorld)).addAction(characterController);
}
public void render(ModelBatch b, Environment e) {
if (!disposing) {
inputs();
b.render(instance, e);
}
}
public void inputs() {
// If the left or right key is pressed, rotate the character and update its
// physics update accordingly.
if (Gdx.input.isKeyPressed(ksx)) {
characterTransform.rotate(0, 1, 0, 2.5f);
ghostObject.setWorldTransform(characterTransform);
}
if (Gdx.input.isKeyPressed(kdx)) {
characterTransform.rotate(0, 1, 0, -2.5f);
ghostObject.setWorldTransform(characterTransform);
}
// Fetch which direction the character is facing now
characterDirection.set(-1, 0, 0).rot(characterTransform).nor();
// Set the walking direction accordingly (either forward or backward)
walkDirection.set(0, 0, 0);
if (Gdx.input.isKeyPressed(kup))
walkDirection.add(characterDirection);
if (Gdx.input.isKeyPressed(kdown))
walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z);
walkDirection.scl(3f * Gdx.graphics.getDeltaTime());
// And update the character controller
characterController.setWalkDirection(walkDirection);
// Now we can update the world as normally
// And fetch the new transformation of the character (this will make the model
// be rendered correctly)
ghostObject.getWorldTransform(characterTransform);
}
public Vector3 getPos() {
return instance.transform.getTranslation(new Vector3());
}
public void setPlaying() {
disposing = false;
}
public void setPos(Vector3 v) {
if (!disposing) {
characterTransform.set(v.x, v.y, v.z, 0, 0, 0, 0);
ghostObject.setWorldTransform(characterTransform);
}
if (!disposing)
setPos(v.x, v.y, v.z);
}
public void setPos(float x, float y, float z) {
if (!disposing) {
characterTransform.set(x, y, z, 0, 0, 0, 0);
ghostObject.setWorldTransform(characterTransform);
}
if (!disposing)
setTransform(x, y, z, 0, 0, 0, 0);
}
public void setTransform(float x, float y, float z, float i, float j, float k, float l) {
if (!disposing)
instance.transform.set(x, y, z, i, j, k, l);
}
public void setName(String name_) {
@ -225,19 +76,26 @@ public class MazePlayer {
return name;
}
public void setPlaying() {
disposing = false;
public void render(ModelBatch b, Environment e) {
if (!disposing) {
update();
b.render(instance, e);
}
}
public void buildModel() {
modelBuilder.begin();
Node n = modelBuilder.node();
n.id = "MazePlayer";
modelBuilder.part("MazePlayer", GL20.GL_TRIANGLES, meshAttr, new Material()).box(0.6f, 0.6f, 0.6f);
mazePlayerModel = modelBuilder.end();
instance = new ModelInstance(mazePlayerModel);
}
public void update() {
}
public void dispose() {
disposing = true;
main.world.dynamicsWorld.removeAction(characterController);
main.world.dynamicsWorld.removeCollisionObject(ghostObject);
characterController.dispose();
ghostObject.dispose();
ghostShape.dispose();
MazePlayerModel.dispose();
disposing = false;
}
}

View File

@ -0,0 +1,170 @@
package com.emamaker.amazeing.player;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.physics.bullet.collision.btBoxShape;
import com.badlogic.gdx.physics.bullet.collision.btBroadphaseProxy;
import com.badlogic.gdx.physics.bullet.collision.btCollisionObject;
import com.badlogic.gdx.physics.bullet.collision.btConvexShape;
import com.badlogic.gdx.physics.bullet.collision.btPairCachingGhostObject;
import com.badlogic.gdx.physics.bullet.dynamics.btDiscreteDynamicsWorld;
import com.badlogic.gdx.physics.bullet.dynamics.btKinematicCharacterController;
public class MazePlayerLocal extends MazePlayer {
/*
* Player controlled on local machine with mouse and kbd, touch or controller
* (in a remote future=
*/
btConvexShape ghostShape;
public btPairCachingGhostObject ghostObject;
public btKinematicCharacterController characterController;
Matrix4 characterTransform;
Vector3 characterDirection = new Vector3();
Vector3 walkDirection = new Vector3();
public Controller ctrl;
// Physics using LibGDX's bullet wrapper
public int kup, kdown, ksx, kdx;
float startx, starty, startz;
public MazePlayerLocal(Game main_, int up_, int down_, int sx_, int dx_) {
this(main_, up_, down_, sx_, dx_, 0, 0, 0);
}
public MazePlayerLocal(Game main_, int up_, int down_, int sx_, int dx_, String name) {
this(main_, up_, down_, sx_, dx_, 0, 0, 0, name);
}
public MazePlayerLocal(Game main_, int up_, int down_, int sx_, int dx_, float startx, float starty, float startz) {
this(main_, up_, down_, sx_, dx_, startx, starty, startz, String.valueOf((char) (65 + rand.nextInt(26))));
}
public MazePlayerLocal(Game main_, int up_, int down_, int sx_, int dx_, float startx, float starty, float startz,
String name) {
super(main_, name);
this.kup = up_;
this.kdown = down_;
this.ksx = sx_;
this.kdx = dx_;
this.startx = startx;
this.starty = starty;
this.startz = startz;
initPhysics();
}
public MazePlayerLocal(Game main_, Controller ctrl_) {
this(main_, ctrl_, 0, 0, 0);
}
public MazePlayerLocal(Game main_, Controller ctrl_, String name) {
this(main_, ctrl_, 0, 0, 0, name);
}
public MazePlayerLocal(Game main_, Controller crtl_, float startx, float starty, float startz) {
this(main_, crtl_, startx, starty, startz, String.valueOf((char) (65 + rand.nextInt(26))));
}
public MazePlayerLocal(Game main_, Controller ctrl_, float startx, float starty, float startz, String name) {
super(main_);
this.ctrl = ctrl_;
this.startx = startx;
this.starty = starty;
this.startz = startz;
initPhysics();
}
public void initPhysics() {
characterTransform = instance.transform; // Set by reference
characterTransform.set(startx, starty, startz, 0, 0, 0, 0);
// Create the physics representation of the character
ghostObject = new btPairCachingGhostObject();
ghostObject.setWorldTransform(characterTransform);
ghostShape = new btBoxShape(new Vector3(0.3f, 0.3f, 0.3f));
ghostObject.setCollisionShape(ghostShape);
ghostObject.setCollisionFlags(btCollisionObject.CollisionFlags.CF_CHARACTER_OBJECT);
characterController = new btKinematicCharacterController(ghostObject, ghostShape, .05f, Vector3.Y);
// And add it to the physics world
main.world.dynamicsWorld.addCollisionObject(ghostObject,
(short) btBroadphaseProxy.CollisionFilterGroups.CharacterFilter,
(short) (btBroadphaseProxy.CollisionFilterGroups.StaticFilter
| btBroadphaseProxy.CollisionFilterGroups.DefaultFilter));
((btDiscreteDynamicsWorld) (main.world.dynamicsWorld)).addAction(characterController);
}
public void inputs() {
// If the left or right key is pressed, rotate the character and update its
// physics update accordingly.
if (Gdx.input.isKeyPressed(ksx)) {
characterTransform.rotate(0, 1, 0, 2.5f);
ghostObject.setWorldTransform(characterTransform);
}
if (Gdx.input.isKeyPressed(kdx)) {
characterTransform.rotate(0, 1, 0, -2.5f);
ghostObject.setWorldTransform(characterTransform);
}
// Fetch which direction the character is facing now
characterDirection.set(-1, 0, 0).rot(characterTransform).nor();
// Set the walking direction accordingly (either forward or backward)
walkDirection.set(0, 0, 0);
if (Gdx.input.isKeyPressed(kup))
walkDirection.add(characterDirection);
if (Gdx.input.isKeyPressed(kdown))
walkDirection.add(-characterDirection.x, -characterDirection.y, -characterDirection.z);
walkDirection.scl(3f * Gdx.graphics.getDeltaTime());
// And update the character controller
characterController.setWalkDirection(walkDirection);
// Now we can update the world as normally
// And fetch the new transformation of the character (this will make the model
// be rendered correctly)
ghostObject.getWorldTransform(characterTransform);
if (main.gameManager.client.socket != null && main.gameManager.client.socket.isConnected()) {
main.gameManager.client.sendMessagetoServer(characterTransform.toString());
}
}
@Override
public void update() {
inputs();
}
@Override
public void setPos(Vector3 v) {
if (!disposing)
this.setPos(v.x, v.y, v.z);
}
@Override
public void setPos(float x, float y, float z) {
if (!disposing) {
characterTransform.set(x, y, z, 0, 0, 0, 0);
ghostObject.setWorldTransform(characterTransform);
}
}
@Override
public void dispose() {
disposing = true;
main.world.dynamicsWorld.removeAction(characterController);
main.world.dynamicsWorld.removeCollisionObject(ghostObject);
characterController.dispose();
ghostObject.dispose();
ghostShape.dispose();
mazePlayerModel.dispose();
disposing = false;
}
}

View File

@ -0,0 +1,72 @@
package com.emamaker.amazeing.player;
import java.util.Random;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.graphics.VertexAttributes;
import com.badlogic.gdx.graphics.g3d.Model;
import com.badlogic.gdx.graphics.g3d.ModelInstance;
import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
import com.badlogic.gdx.net.Socket;
import com.emamaker.amazeing.AMazeIng;
public class MazePlayerRemote extends MazePlayer{
/*Remote controlled player to show other players on the server*/
static Random rand = new Random();
// MazePlayer model building stuff
public Model mazePlayerModel;
public ModelInstance instance;
ModelBuilder modelBuilder = new ModelBuilder();
MeshPartBuilder meshBuilder;
static int meshAttr = VertexAttributes.Usage.Position | VertexAttributes.Usage.Normal;
public Controller ctrl;
// Physics using LibGDX's bullet wrapper
public int kup, kdown, ksx, kdx;
float startx, starty, startz;
String name;
AMazeIng main;
Socket socket;
boolean disposing = false;
public MazePlayerRemote(Game main_, Socket s) {
super(main_);
socket = s;
}
public void updateRemoteTransform(String s) {
float x = 0, y = 0, z = 0, i = 0, j = 0, k = 0, l = 0;
setTransform(x, y, z, i, j, k, l);
}
public void setName(String name_) {
this.name = name_;
}
public String getName() {
return name;
}
public void setPlaying() {
disposing = false;
}
@Override
public void update() {
}
@Override
public void dispose() {
disposing = true;
mazePlayerModel.dispose();
disposing = false;
}
}

View File

@ -6,8 +6,6 @@ import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.controllers.Controller;
import com.badlogic.gdx.controllers.Controllers;
import com.badlogic.gdx.controllers.mappings.Xbox;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
@ -20,6 +18,7 @@ import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.emamaker.amazeing.maze.settings.MazeSettings;
import com.emamaker.amazeing.player.MazePlayer;
import com.emamaker.amazeing.player.MazePlayerLocal;
import com.emamaker.amazeing.ui.UIManager;
public class PlayerChooseScreen implements Screen {
@ -31,7 +30,7 @@ public class PlayerChooseScreen implements Screen {
Label[] labels;
int currentLabel = 0;
HashMap<MazePlayer, Label> players = new HashMap<MazePlayer, Label>();
Screen thisScreen;
public PlayerChooseScreen(UIManager uiManager_) {
@ -110,7 +109,7 @@ public class PlayerChooseScreen implements Screen {
@Override
public void show() {
thisScreen = this;
labels = new Label[MazeSettings.MAXPLAYERS];
stage = new Stage(new ScreenViewport());
Container<Table> tableContainer = new Container<Table>();
@ -201,7 +200,7 @@ public class PlayerChooseScreen implements Screen {
uiManager.main.multiplexer.removeProcessor(stage);
}
MazePlayer p;
MazePlayerLocal p;
@Override
public void render(float delta) {
@ -216,27 +215,27 @@ public class PlayerChooseScreen implements Screen {
|| Gdx.input.isKeyJustPressed(Keys.S) || Gdx.input.isKeyJustPressed(Keys.D)) {
p = getPlayerWithKeys(Keys.W, Keys.S, Keys.A, Keys.D);
if (p == null)
p = new MazePlayer(uiManager.main, Keys.W, Keys.S, Keys.A, Keys.D);
p = new MazePlayerLocal(uiManager.main, Keys.W, Keys.S, Keys.A, Keys.D);
togglePlayer(p);
}
if (Gdx.input.isKeyJustPressed(Keys.UP) || Gdx.input.isKeyJustPressed(Keys.LEFT)
|| Gdx.input.isKeyJustPressed(Keys.DOWN) || Gdx.input.isKeyJustPressed(Keys.RIGHT)) {
p = getPlayerWithKeys(Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT);
if (p == null)
p = new MazePlayer(uiManager.main, Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT);
p = new MazePlayerLocal(uiManager.main, Keys.UP, Keys.DOWN, Keys.LEFT, Keys.RIGHT);
togglePlayer(p);
}
for (Controller c : Controllers.getControllers()) {
if (c.getButton(Xbox.Y)) {
p = getPlayerWithCtrl(c);
if (p == null)
p = new MazePlayer(uiManager.main, c);
togglePlayer(p);
}
}
// for (Controller c : Controllers.getControllers()) {
// if (c.getButton(Xbox.Y)) {
// p = getPlayerWithCtrl(c);
// if (p == null)
// p = new MazePlayerLocal(uiManager.main, c);
// togglePlayer(p);
// }
// }
}
public void togglePlayer(MazePlayer p) {
public void togglePlayer(MazePlayerLocal p) {
try {
if (alreadyAddedPlayer(p)) {
players.get(p).setText("Not Joined Yet");
@ -254,15 +253,17 @@ public class PlayerChooseScreen implements Screen {
return getPlayerWithKeys(keys) != null;
}
public boolean alreadyAddedPlayer(MazePlayer p) {
public boolean alreadyAddedPlayer(MazePlayerLocal p) {
return players.containsKey(p);
}
public MazePlayer getPlayerWithKeys(int... keys) {
public MazePlayerLocal getPlayerWithKeys(int... keys) {
for (MazePlayer p : players.keySet()) {
if(p instanceof MazePlayerLocal) {
for (int k : keys) {
if (p.kup == k || p.kdown == k || p.ksx == k || p.kdx == k)
return p;
if (((MazePlayerLocal)p).kup == k || ((MazePlayerLocal)p).kdown == k || ((MazePlayerLocal)p).ksx == k || ((MazePlayerLocal)p).kdx == k)
return (MazePlayerLocal)p;
}
}
}
return null;
@ -272,10 +273,12 @@ public class PlayerChooseScreen implements Screen {
return getPlayerWithCtrl(ctrl) != null;
}
public MazePlayer getPlayerWithCtrl(Controller ctrl) {
public MazePlayerLocal getPlayerWithCtrl(Controller ctrl) {
for (MazePlayer p : players.keySet()) {
if (p.ctrl == ctrl)
return p;
if (p instanceof MazePlayerLocal) {
if (((MazePlayerLocal)p).ctrl == ctrl)
return (MazePlayerLocal) p;
}
}
return null;
}