diff --git a/core/src/com/emamaker/amazeing/manager/GameClient.java b/core/src/com/emamaker/amazeing/manager/GameClient.java new file mode 100644 index 0000000..d9aeafe --- /dev/null +++ b/core/src/com/emamaker/amazeing/manager/GameClient.java @@ -0,0 +1,63 @@ +package com.emamaker.amazeing.manager; + +import java.io.IOException; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Net.Protocol; +import com.badlogic.gdx.net.Socket; +import com.badlogic.gdx.net.SocketHints; +import com.emamaker.amazeing.AMazeIng; + +public class GameClient { + + volatile boolean clientRunning = false; + public Socket socket; + public String addr; + public int port; + Thread clientThread; + public AMazeIng main; + + public GameClient(AMazeIng main_) { + main = main_; + } + + public void start(String addr_, int port_) { + port = port_; + addr = addr_; + clientThread = new Thread(new Runnable() { + + @Override + public void run() { + clientRunning = true; + while (clientRunning) { + /* + * https://www.gamefromscratch.com/post/2014/03/11/LibGDX-Tutorial-10-Basic- + * networking.aspx + */ + if (socket == null) { + SocketHints socketHints = new SocketHints(); + socketHints.connectTimeout = 0; + socket = Gdx.net.newClientSocket(Protocol.TCP, addr, port, socketHints); + System.out.println("Connected to server!"); + } else { + if (socket.isConnected()) { + // Send messages to the server: + try { + // write our entered message to the stream + socket.getOutputStream().write("AO\n".getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + } + }); + clientThread.start(); + + } + + public void stop() { + clientRunning = false; + } +} diff --git a/core/src/com/emamaker/amazeing/manager/GameManager.java b/core/src/com/emamaker/amazeing/manager/GameManager.java index aa32ad0..760406b 100644 --- a/core/src/com/emamaker/amazeing/manager/GameManager.java +++ b/core/src/com/emamaker/amazeing/manager/GameManager.java @@ -5,28 +5,32 @@ import java.util.Random; import java.util.Set; import com.badlogic.gdx.Game; -import com.badlogic.gdx.math.Vector3; import com.emamaker.amazeing.AMazeIng; import com.emamaker.amazeing.maze.MazeGenerator; import com.emamaker.amazeing.maze.settings.MazeSettings; import com.emamaker.amazeing.player.MazePlayer; import com.emamaker.voxelengine.block.CellId; import com.emamaker.voxelengine.player.Player; -import com.emamaker.voxelengine.utils.VoxelSettings; public class GameManager { + + /*Local manager for local games and server host in multiplayer games*/ public MazeGenerator mazeGen; public boolean gameStarted = false; Random rand = new Random(); + public GameServer server; + public GameClient client; AMazeIng main; + ArrayList players = new ArrayList(); public GameManager(Game main_) { main = (AMazeIng) main_; gameStarted = false; - + server = new GameServer(main); + client = new GameClient(main); // Maze Generation mazeGen = new MazeGenerator(main, MazeSettings.MAZEX, MazeSettings.MAZEZ); } @@ -41,13 +45,15 @@ public class GameManager { main.world.render(); main.world.modelBatch.begin(main.world.cam); - for (MazePlayer p : players) { - p.render(main.world.modelBatch, main.world.environment); - anyoneWon = false; - if (checkWin(p)) { - anyoneWon = true; - gameStarted = false; - break; + if(players != null) { + for (MazePlayer p : players) { + p.render(main.world.modelBatch, main.world.environment); + anyoneWon = false; + if (checkWin(p)) { + anyoneWon = true; + gameStarted = false; + break; + } } } @@ -63,21 +69,23 @@ public class GameManager { // Only add new players and dispose the old ones //Check if actually there are players to be deleted - for (MazePlayer p : players) - if (!pl.contains(p)) - toDelete.add(p); - - //Check if new players have to be added - for (MazePlayer p : pl) - if (!players.contains(p)) - players.add(p); - - //Fianlly delete players. A separated step is needed to remove the risk of a ConcurrentModificationException - for(MazePlayer p : toDelete) { - p.dispose(); - players.remove(p); + if(pl != null) { + for (MazePlayer p : players) + if (!pl.contains(p)) + toDelete.add(p); + + //Check if new players have to be added + for (MazePlayer p : pl) + if (!players.contains(p)) + players.add(p); + + //Fianlly delete players. A separated step is needed to remove the risk of a ConcurrentModificationException + for(MazePlayer p : toDelete) { + p.dispose(); + players.remove(p); + } + toDelete.clear(); } - toDelete.clear(); // destroyPlayers(); // players.addAll(p); @@ -169,6 +177,8 @@ public class GameManager { } public void dispose() { + client.stop(); + server.stop(); for (MazePlayer p : players) p.dispose(); } diff --git a/core/src/com/emamaker/amazeing/manager/GameServer.java b/core/src/com/emamaker/amazeing/manager/GameServer.java new file mode 100644 index 0000000..6b1f1b0 --- /dev/null +++ b/core/src/com/emamaker/amazeing/manager/GameServer.java @@ -0,0 +1,98 @@ +package com.emamaker.amazeing.manager; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Arrays; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.Net.Protocol; +import com.badlogic.gdx.net.ServerSocket; +import com.badlogic.gdx.net.ServerSocketHints; +import com.badlogic.gdx.net.Socket; +import com.badlogic.gdx.net.SocketHints; +import com.badlogic.gdx.utils.GdxRuntimeException; +import com.emamaker.amazeing.AMazeIng; + +public class GameServer { + + volatile boolean serverRunning = false; + String nextMessage = "oa"; + + public ServerSocket serverSocket = null; + public int port; + ArrayList clientSockets = new ArrayList(); + Thread serverThread; + public AMazeIng main; + Socket s; + + SocketHints socketHints = new SocketHints(); + ServerSocketHints serverSocketHint = new ServerSocketHints(); + + public GameServer(AMazeIng main_) { + main = main_; + } + + public void startServer(int port_) { + port = port_; + serverSocketHint.acceptTimeout = 100; + serverThread = new Thread(new Runnable() { + + @Override + public void run() { + 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); + } + + } catch (GdxRuntimeException se) { + // System.out.println("No new clients connected in the last 100 milliseconds"); + } + for (Socket s : clientSockets) { + System.out.println(s); + // Receive messages from the server + try { + 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()); + } catch (IOException e) { +// e.printStackTrace(); + } + } + } + } + + }); + serverThread.start(); + } + + public void startGame() { + // 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 + // To spread the map we can just encode in a string the todraw[][] array in + // MazeGenerator, in future run-lenght encoding should be considered + main.gameManager.generateMaze(null); + nextMessage = "Map" + Arrays.deepToString(main.gameManager.mazeGen.todraw); + } + + public void stop() { + if (serverSocket != null) { + serverSocket.dispose(); + serverSocket = null; + } + serverRunning = false; + } +} diff --git a/core/src/com/emamaker/amazeing/maze/MazeGenerator.java b/core/src/com/emamaker/amazeing/maze/MazeGenerator.java index 99aa083..505f223 100755 --- a/core/src/com/emamaker/amazeing/maze/MazeGenerator.java +++ b/core/src/com/emamaker/amazeing/maze/MazeGenerator.java @@ -108,7 +108,7 @@ public class MazeGenerator { currentCell.current = true; } } - prepareShow(); + prepareShow(todraw); } //Setup end point in a random location. At a distance of EP_DIST from every player @@ -128,7 +128,7 @@ public class MazeGenerator { main.world.worldManager.setCell(WINX, 0, WINZ, CellId.ID_WOOD); } - public void prepareShow() { + public void prepareShow(int[][] todraw_) { for (int i = 0; i < w; i++) { for (int j = 0; j < h; j++) { todraw[i][j] = 1; @@ -143,20 +143,20 @@ public class MazeGenerator { int y = 2 * j + 1; // current cell - todraw[x][y] = 0; + todraw_[x][y] = 0; // up wall if (!cellsGrid[i][j].walls[0]) - todraw[x][y - 1] = 0; + todraw_[x][y - 1] = 0; // down wall if (!cellsGrid[i][j].walls[2]) - todraw[x][y + 1] = 0; + todraw_[x][y + 1] = 0; // left wall if (!cellsGrid[i][j].walls[3]) - todraw[x - 1][y] = 0; + todraw_[x - 1][y] = 0; // right all if (!cellsGrid[i][j].walls[1]) - todraw[x + 1][y] = 0; + todraw_[x + 1][y] = 0; } } @@ -164,7 +164,7 @@ public class MazeGenerator { for (int j = 0; j < h; j++) { for (int i = 0; i < w; i++) { main.world.worldManager.setCell(i, 0, j, CellId.ID_GRASS); - if (todraw[i][j] == 1) + if (todraw_[i][j] == 1) main.world.worldManager.setCell(i, 1, j, CellId.ID_LEAVES); // if (todraw[i][j] == 2) // main.world.worldManager.setCell(i, 0, j, CellId.ID_WOOD); diff --git a/core/src/com/emamaker/amazeing/ui/screens/ServerJoinScreen.java b/core/src/com/emamaker/amazeing/ui/screens/ServerJoinScreen.java index 1d5ee8f..24e8215 100644 --- a/core/src/com/emamaker/amazeing/ui/screens/ServerJoinScreen.java +++ b/core/src/com/emamaker/amazeing/ui/screens/ServerJoinScreen.java @@ -1,13 +1,8 @@ package com.emamaker.amazeing.ui.screens; -import java.io.IOException; - import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Net.Protocol; -import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.Screen; -import com.badlogic.gdx.net.Socket; -import com.badlogic.gdx.net.SocketHints; +import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; @@ -20,37 +15,38 @@ import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.emamaker.amazeing.ui.UIManager; -public class ServerJoinScreen implements Screen{ +public class ServerJoinScreen implements Screen { Stage stage; UIManager uiManager; - + public ServerJoinScreen(UIManager uiManager_) { - + uiManager = uiManager_; - + stage = new Stage(new ScreenViewport()); Container tableContainer = new Container
(); Table table = new Table(); float cw = stage.getWidth(); float ch = stage.getHeight(); - + tableContainer.setSize(cw, ch); tableContainer.setPosition(0, 0); - + Label instLab = new Label("Enter ip address and port and connect to the server!", uiManager.skin); TextButton backBtn = new TextButton("Main menu", uiManager.skin); TextButton connectBtn = new TextButton("Connect to the server!", uiManager.skin); TextButton helpBtn = new TextButton("?", uiManager.skin); - final TextArea srvIp = new TextArea("Server IP Address", uiManager.skin); - final TextArea srvPort = new TextArea("Server Port", uiManager.skin); + final TextArea srvIp = new TextArea("localhost", uiManager.skin); + final TextArea srvPort = new TextArea("9999", uiManager.skin); // Add actions to the buttons backBtn.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { hide(); + uiManager.main.gameManager.client.stop(); uiManager.main.setScreen(uiManager.titleScreen); return true; } @@ -65,28 +61,20 @@ public class ServerJoinScreen implements Screen{ connectBtn.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { - /*https://www.gamefromscratch.com/post/2014/03/11/LibGDX-Tutorial-10-Basic-networking.aspx*/ - SocketHints socketHints = new SocketHints(); - // Socket will time our in 4 seconds - socketHints.connectTimeout = 4000; - //create the socket and connect to the server entered in the text box ( x.x.x.x format ) on port 9021 - Socket socket = Gdx.net.newClientSocket(Protocol.TCP, srvIp.getText(), Integer.valueOf(srvPort.getText()), socketHints); - try { - // write our entered message to the stream - socket.getOutputStream().write("AO\n".getBytes()); - } catch (IOException e) { - e.printStackTrace(); - } - return true; + try { + uiManager.main.gameManager.client.start(srvIp.getText(), Integer.valueOf(srvPort.getText())); + }catch(Exception e) { + System.out.println("Please input a valid ip address and port"); + } + + return true; } }); - - - + Table firstRowTable = new Table(); - firstRowTable.add(backBtn).fillX().expandX().space(cw*0.005f); - firstRowTable.add(instLab).height(50).fillX().expandX().space(cw*0.25f); - firstRowTable.add(helpBtn).width(50).height(50).fillX().expandX().space(cw*0.005f); + firstRowTable.add(backBtn).fillX().expandX().space(cw * 0.005f); + firstRowTable.add(instLab).height(50).fillX().expandX().space(cw * 0.25f); + firstRowTable.add(helpBtn).width(50).height(50).fillX().expandX().space(cw * 0.005f); firstRowTable.setOrigin(Align.center | Align.top); table.row().colspan(4); @@ -96,10 +84,10 @@ public class ServerJoinScreen implements Screen{ table.add(srvIp).fillX().expandX(); table.row().colspan(4); table.add(srvPort).fillX().expandX(); - + table.row().colspan(4); table.add(connectBtn).fillX().expandX(); - + tableContainer.setActor(table); stage.addActor(tableContainer); } @@ -111,21 +99,22 @@ public class ServerJoinScreen implements Screen{ @Override public void render(float delta) { - Gdx.gl.glClearColor(0,0,0,0); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - stage.act(); - stage.draw(); + Gdx.gl.glClearColor(0, 0, 0, 0); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + stage.act(); + stage.draw(); } @Override public void resize(int width, int height) { stage.getViewport().update(width, height, true); } - + @Override public void hide() { - uiManager.main.multiplexer.removeProcessor(stage); + uiManager.main.multiplexer.removeProcessor(stage); } + @Override public void pause() { } diff --git a/core/src/com/emamaker/amazeing/ui/screens/ServerLaunchScreen.java b/core/src/com/emamaker/amazeing/ui/screens/ServerLaunchScreen.java index 1d80248..c39770c 100644 --- a/core/src/com/emamaker/amazeing/ui/screens/ServerLaunchScreen.java +++ b/core/src/com/emamaker/amazeing/ui/screens/ServerLaunchScreen.java @@ -1,16 +1,8 @@ package com.emamaker.amazeing.ui.screens; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Net.Protocol; import com.badlogic.gdx.Screen; import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.net.ServerSocket; -import com.badlogic.gdx.net.ServerSocketHints; -import com.badlogic.gdx.net.Socket; import com.badlogic.gdx.scenes.scene2d.InputEvent; import com.badlogic.gdx.scenes.scene2d.InputListener; import com.badlogic.gdx.scenes.scene2d.Stage; @@ -23,36 +15,38 @@ import com.badlogic.gdx.utils.Align; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.emamaker.amazeing.ui.UIManager; -public class ServerLaunchScreen implements Screen{ +public class ServerLaunchScreen implements Screen { Stage stage; UIManager uiManager; public ServerLaunchScreen(UIManager uiManager_) { - + uiManager = uiManager_; - + stage = new Stage(new ScreenViewport()); Container
tableContainer = new Container
(); Table table = new Table(); float cw = stage.getWidth(); float ch = stage.getHeight(); - + tableContainer.setSize(cw, ch); tableContainer.setPosition(0, 0); - + Label instLab = new Label("Enter the port the server should start on", uiManager.skin); - TextButton backBtn = new TextButton("Main menu", uiManager.skin); + TextButton backBtn = new TextButton("Main menu and quit server", uiManager.skin); TextButton connectBtn = new TextButton("Launch the server!", uiManager.skin); + TextButton gameBtn = new TextButton("Launch the game!", uiManager.skin); TextButton helpBtn = new TextButton("?", uiManager.skin); - final TextArea srvPort = new TextArea("Server Port", uiManager.skin); + final TextArea srvPort = new TextArea("9999", uiManager.skin); // Add actions to the buttons backBtn.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { hide(); + uiManager.main.gameManager.server.stop(); uiManager.main.setScreen(uiManager.titleScreen); return true; } @@ -67,49 +61,22 @@ public class ServerLaunchScreen implements Screen{ connectBtn.addListener(new InputListener() { @Override public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { - /*https://www.gamefromscratch.com/post/2014/03/11/LibGDX-Tutorial-10-Basic-networking.aspx*/ - - // Now we create a thread that will listen for incoming socket connections - new Thread(new Runnable(){ - - @Override - public void run() { - ServerSocketHints serverSocketHint = new ServerSocketHints(); - // 0 means no timeout. Probably not the greatest idea in production! - serverSocketHint.acceptTimeout = 0; - - // Create the socket server using TCP protocol and listening on 9021 - // Only one app can listen to a port at a time, keep in mind many ports are reserved - // especially in the lower numbers ( like 21, 80, etc ) - ServerSocket serverSocket = Gdx.net.newServerSocket(Protocol.TCP, Integer.valueOf(srvPort.getText()), serverSocketHint); - - // Loop forever - while(true){ - // Create a socket - Socket socket = serverSocket.accept(null); - - // Read data from the socket into a BufferedReader - BufferedReader buffer = new BufferedReader(new InputStreamReader(socket.getInputStream())); - try { - // Read to the next newline (\n) and display that text on labelMessage - System.out.println("Server received message!"); - System.out.println(buffer.readLine()); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - }).start(); // And, start the thread running - return true; + uiManager.main.gameManager.server.startServer(Integer.valueOf(srvPort.getText())); + return true; } }); - - - + gameBtn.addListener(new InputListener() { + @Override + public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) { + uiManager.main.gameManager.server.startGame(); + return true; + } + }); + Table firstRowTable = new Table(); - firstRowTable.add(backBtn).fillX().expandX().space(cw*0.005f); - firstRowTable.add(instLab).height(50).fillX().expandX().space(cw*0.25f); - firstRowTable.add(helpBtn).width(50).height(50).fillX().expandX().space(cw*0.005f); + firstRowTable.add(backBtn).fillX().expandX().space(cw * 0.005f); + firstRowTable.add(instLab).height(50).fillX().expandX().space(cw * 0.25f); + firstRowTable.add(helpBtn).width(50).height(50).fillX().expandX().space(cw * 0.005f); firstRowTable.setOrigin(Align.center | Align.top); table.row().colspan(4); @@ -117,9 +84,12 @@ public class ServerLaunchScreen implements Screen{ table.row().colspan(4); table.add(srvPort).fillX().expandX(); - + table.row().colspan(4); table.add(connectBtn).fillX().expandX(); + + table.row().colspan(4); + table.add(gameBtn).fillX().expandX(); tableContainer.setActor(table); stage.addActor(tableContainer); @@ -132,21 +102,22 @@ public class ServerLaunchScreen implements Screen{ @Override public void render(float delta) { - Gdx.gl.glClearColor(0,0,0,0); - Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - stage.act(); - stage.draw(); + Gdx.gl.glClearColor(0, 0, 0, 0); + Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); + stage.act(); + stage.draw(); } @Override public void resize(int width, int height) { stage.getViewport().update(width, height, true); } - + @Override public void hide() { - uiManager.main.multiplexer.removeProcessor(stage); + uiManager.main.multiplexer.removeProcessor(stage); } + @Override public void pause() { }