Multiplayer: syncronize settings between server and clients

master
EmaMaker 2020-05-03 22:56:24 +02:00
parent 607bf7b91e
commit 98d66880a8
11 changed files with 139 additions and 82 deletions

View File

@ -1,10 +1,11 @@
package com.emamaker.amazeing.manager;
import java.util.ArrayList;
import java.util.Random;
import java.util.Set;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.Plane.PlaneSide;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.utils.viewport.ScreenViewport;
import com.emamaker.amazeing.AMazeIng;
@ -17,10 +18,6 @@ import com.emamaker.amazeing.ui.screens.PreGameScreen;
import com.emamaker.voxelengine.block.CellId;
import com.emamaker.voxelengine.player.Player;
import java.util.ArrayList;
import java.util.Random;
import java.util.Set;
public class GameManager {
AMazeIng main;

View File

@ -21,6 +21,8 @@ import com.emamaker.amazeing.manager.network.NetworkCommon.RemovePlayer;
import com.emamaker.amazeing.manager.network.NetworkCommon.StartGame;
import com.emamaker.amazeing.manager.network.NetworkCommon.UpdateMap;
import com.emamaker.amazeing.manager.network.NetworkCommon.UpdatePlayerTransform;
import com.emamaker.amazeing.manager.network.NetworkCommon.UpdateSettings;
import com.emamaker.amazeing.maze.settings.MazeSettings;
import com.emamaker.amazeing.player.MazePlayer;
import com.emamaker.amazeing.player.MazePlayerLocal;
import com.emamaker.amazeing.player.MazePlayerRemote;
@ -126,6 +128,17 @@ public class GameClient {
gameManager.anyoneWon = true;
showPreGame = true;
}
} else if (object instanceof UpdateSettings) {
System.out.println("Update received for setting n." + ((UpdateSettings) object).index);
if (!main.server.isRunning()) {
MazeSettings.settings.get(((UpdateSettings) object).index)
.parseOptionString(((UpdateSettings) object).value);
}else {
System.out.println("Ignoring settings update since we are running on a server");
}
//We don't mind if we are client or server, just set the flag to update pregamescreen
showPreGame = true;
}
}

View File

@ -19,6 +19,7 @@ import com.emamaker.amazeing.manager.network.NetworkCommon.LoginAO2;
import com.emamaker.amazeing.manager.network.NetworkCommon.RemovePlayer;
import com.emamaker.amazeing.manager.network.NetworkCommon.StartGame;
import com.emamaker.amazeing.manager.network.NetworkCommon.UpdatePlayerTransform;
import com.emamaker.amazeing.manager.network.NetworkCommon.UpdateSettings;
import com.emamaker.amazeing.maze.settings.MazeSettings;
import com.emamaker.amazeing.player.MazePlayer;
import com.emamaker.amazeing.player.MazePlayerRemote;
@ -76,16 +77,17 @@ public class GameServer {
public void received(Connection c, Object object) {
ConnectionPlayer connection = (ConnectionPlayer) c;
if(object instanceof JustConnected) {
//Notify the newly connected client about all other clients already present here
System.out.println("New client just connected, updating it with info about other clients!");
if (object instanceof JustConnected) {
// Notify the newly connected client about all other clients already present
// here
System.out.println("New client just connected, updating it with info about other clients!");
AddNewPlayer response = new AddNewPlayer();
for (String s : remotePlayers.keySet()) {
response.uuid = s;
c.sendTCP(response);
System.out.println("Updated about: " + s);
}
}else if (object instanceof LoginAO) {
}
} else if (object instanceof LoginAO) {
// Give player its UUID and wait for response. Once the LoginAO2 response is
// received, move the
// UUID to the list of players, create a new one and notify clients about it
@ -103,12 +105,15 @@ public class GameServer {
remotePlayers.put(((LoginAO2) object).uuid,
new MazePlayerRemote(((LoginAO2) object).uuid, false));
System.out.println(
"Client with UUID " + ((LoginAO2) object).uuid + " is connected and ready to play :)");
updateSettingForClient(c);
System.out.println("Client with UUID " + ((LoginAO2) object).uuid
+ " is connected and ready to play :)");
AddNewPlayer response = new AddNewPlayer();
response.uuid = ((LoginAO2) object).uuid;
server.sendToAllTCP(response);
} else {
// Send connection refused
c.sendTCP(new ConnectionRefused());
@ -122,8 +127,9 @@ public class GameServer {
System.out.println("Client with UUID " + connection.uuid + " is leaving the server :(");
server.sendToAllTCP(object);
}else {
System.out.println("Server received delete message for player with UUID " + connection.uuid + " but player wasn't playing");
} else {
System.out.println("Server received delete message for player with UUID " + connection.uuid
+ " but player wasn't playing");
}
} else if (object instanceof UpdatePlayerTransform) {
UpdatePlayerTransform transform = (UpdatePlayerTransform) object;
@ -189,29 +195,34 @@ public class GameServer {
// 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() {
public boolean startGame() {
if (serverRunning) {
update();
// Start game stuff
this.gameManager = new GameManager(main, GameType.SERVER);
this.gameManager.generateMaze(new HashSet<MazePlayer>(remotePlayers.values()));
endGameCalled = false;
StartGame request = new StartGame();
request.map = this.gameManager.mazeGen.runLenghtEncode();
server.sendToAllTCP(request);
if (!remotePlayers.isEmpty()) {
// Start game stuff
this.gameManager = new GameManager(main, GameType.SERVER);
this.gameManager.generateMaze(new HashSet<MazePlayer>(remotePlayers.values()));
endGameCalled = false;
if (gameManager.gameStarted)
for (String p : remotePlayers.keySet())
updatePlayer(p, remotePlayers.get(p), true);
StartGame request = new StartGame();
request.map = this.gameManager.mazeGen.runLenghtEncode();
server.sendToAllTCP(request);
if (main.getScreen() != null) {
main.getScreen().hide();
main.setScreen(null);
if (gameManager.gameStarted)
for (String p : remotePlayers.keySet())
updatePlayer(p, remotePlayers.get(p), true);
if (main.getScreen() != null) {
main.getScreen().hide();
main.setScreen(null);
}
return true;
}
} else {
System.out.println("Server not started yet, game cannot start");
}
return false;
}
public void updatePlayer(String uuid, MazePlayerRemote p, boolean force) {
@ -263,6 +274,28 @@ public class GameServer {
public boolean isRunning() {
return serverRunning;
}
UpdateSettings s = new UpdateSettings();
// Send updates about settings to the clients
public void updateSettingForAll() {
if(isRunning())
for (int i = 0; i < MazeSettings.settings.size(); i++) {
s.index = i;
s.value = MazeSettings.settings.get(i).getOptionString();
server.sendToAllTCP(s);
}
}
public void updateSettingForClient(Connection c) {
if(isRunning())
for (int i = 0; i < MazeSettings.settings.size(); i++) {
s.index = i;
s.value = MazeSettings.settings.get(i).getOptionString();
c.sendTCP(s);
}
}
}
class ConnectionPlayer extends Connection {

View File

@ -7,7 +7,7 @@ public class NetworkCommon {
// This registers objects that are going to be sent over the network.
static public void register(EndPoint endPoint) {
public static void register(EndPoint endPoint) {
Kryo kryo = endPoint.getKryo();
kryo.register(JustConnected.class);
kryo.register(LoginAO.class);
@ -21,51 +21,57 @@ public class NetworkCommon {
kryo.register(StartGame.class);
kryo.register(EndGame.class);
kryo.register(UpdateMap.class);
kryo.register(UpdateSettings.class);
}
//Login stuff
static public class JustConnected {
public static class JustConnected {
}
static public class LoginAO {
public static class LoginAO {
}
static public class LoginAO2 {
public static class LoginAO2 {
String uuid;
}
static public class ConnectionRefused {
public static class ConnectionRefused {
String uuid;
}
static public class LoginUUID {
public static class LoginUUID {
String uuid;
}
//Player stuff
static public class AddNewPlayer {
public static class AddNewPlayer {
String uuid;
}
static public class RemovePlayer {
public static class RemovePlayer {
String uuid;
}
static public class UpdatePlayerTransform {
public static class UpdatePlayerTransform {
String uuid;
float tx, ty, tz, rx, ry, rz, rw;
}
static public class UpdatePlayerTransformServer {
public static class UpdatePlayerTransformServer {
String uuid;
float tx, ty, tz, rx, ry, rz, rw;
}
static public class StartGame{
public static class StartGame{
//Use this to notify clients of a newly started game
//A Run-lenght-encoded representation of the map can be appended, this can be avoided but it's not recommended
String map;
}
static public class EndGame{
public static class EndGame{
//Use this to notify clients when a game ends
}
static public class UpdateMap{
public static class UpdateMap{
//Use this to notify clients of a modification of the map
//Run-lenght-encoded representation of the map
String map;
}
public static class UpdateSettings {
int index;
String value;
}
}

View File

@ -44,7 +44,7 @@ public class MazeSetting {
//Build the Table which will be later used to add this to the screen
table = new Table();
nameLabel = new Label(this.name+"\t\t\t", uiManager.skin);
nameLabel = new Label(this.name, uiManager.skin);
currentOptLabel = new Label(this.options[currentOption], uiManager.skin);
backBtn = new TextButton("<", uiManager.skin);
forthBtn = new TextButton(">", uiManager.skin);
@ -89,7 +89,9 @@ public class MazeSetting {
}
public void update() {
preUpdate();
currentOption = (currentOption+options.length)%options.length;
currentOptLabel.setText(options[currentOption]);
parseOptionString(options[currentOption]);
}
public void saveState() {
@ -123,9 +125,9 @@ public class MazeSetting {
table.add(resetBtn).fillX().expandX();
}
public void preUpdate() {
this.currentOption = (this.currentOption+this.options.length)%this.options.length;
this.currentOptLabel.setText(this.options[currentOption]);
public void parseOptionString(String opt) {}
public String getOptionString() {
return options[currentOption];
}
}

View File

@ -16,11 +16,9 @@ public class MazeSettingDimension extends MazeSetting{
public MazeSettingDimension(String name_, String[] options_, UIManager uiManager_) {
super(name_, options_, uiManager_);
}
@Override
public void update(){
super.update();
String opt = options[currentOption];
public void parseOptionString(String opt) {
super.parseOptionString(opt);
String[] split = opt.split("x");
MazeSettings.MAZEX = Integer.valueOf(split[0]);
MazeSettings.MAZEZ = Integer.valueOf(split[1]);

View File

@ -14,9 +14,8 @@ public class MazeSettingMaxPlayers extends MazeSetting{
}
@Override
public void update(){
super.update();
MazeSettings.MAXPLAYERS = Integer.valueOf(options[currentOption]);
public void parseOptionString(String opt) {
MazeSettings.MAXPLAYERS = Integer.valueOf(opt);
}
}

View File

@ -92,7 +92,7 @@ public class MyScreen implements Screen {
update();
stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
stage.act();
stage.draw();
}

View File

@ -102,7 +102,7 @@ public class PlayerChooseScreen extends MyScreen {
for (MazePlayer p : players)
p.dispose();
players.clear();
for (int i = 0; i < buttons.length; i++)
if (buttons[i].isChecked())
players.add(new MazePlayerLocal(new Touchpad(0f, uiManager.skin), i));

View File

@ -1,5 +1,7 @@
package com.emamaker.amazeing.ui.screens;
import java.util.Arrays;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.ui.Container;
@ -9,13 +11,12 @@ import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.emamaker.amazeing.manager.GameType;
import com.emamaker.amazeing.maze.settings.MazeSettings;
import com.emamaker.amazeing.player.MazePlayer;
import com.emamaker.amazeing.ui.UIManager;
public class PreGameScreen extends MyScreen {
Label[] labels;
MazePlayer[] players;
// MazePlayer[] players;
int nPlayers, nPlayersOld;
// GameType we are runnig. assuming server for default. If client, the StartGame
@ -29,7 +30,7 @@ public class PreGameScreen extends MyScreen {
Label instLab, helpDlgText;
TextButton backBtn, setBtn, helpBtn, playBtn, helpDlgOkBtn;
Dialog helpDlg;
public PreGameScreen(UIManager uiManager_) {
super(uiManager_);
chmult = 0.8f;
@ -40,8 +41,6 @@ public class PreGameScreen extends MyScreen {
super.createTable();
thisScreen = this;
labels = new Label[MazeSettings.MAXPLAYERS];
players = new MazePlayer[MazeSettings.MAXPLAYERS];
nPlayers = 0;
nPlayersOld = 0;
@ -61,8 +60,9 @@ public class PreGameScreen extends MyScreen {
+ "If you're a server, wait for players and start the game pressing the \"Start the match!\" button.\n"
+ "How to join (for both client and server):\n"
+ "On a computer players can join or leave the game pressing WASD, Arrow buttons or\n"
+ "a button on the controller\n" + "On mobile players can be toggled using the buttons below.", uiManager.skin);
helpDlg.text(helpDlgText);
+ "a button on the controller\n" + "On mobile players can be toggled using the buttons below.",
uiManager.skin);
helpDlg.text(helpDlgText);
helpDlgOkBtn = new TextButton("OK", uiManager.skin);
helpDlg.button(helpDlgOkBtn);
helpDlgOkBtn.addListener(new InputListener() {
@ -76,11 +76,6 @@ public class PreGameScreen extends MyScreen {
if (type == GameType.CLIENT)
instLab.setText("Waiting for server to start the game...");
// Labels to know if players joined
for (int i = 0; i < labels.length; i++) {
labels[i] = new Label("-- empty slot --", uiManager.skin);
}
// Add actions to the buttons
backBtn.addListener(new InputListener() {
@Override
@ -100,8 +95,8 @@ public class PreGameScreen extends MyScreen {
playBtn.addListener(new InputListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
hide();
uiManager.main.server.startGame();
if (uiManager.main.server.startGame())
hide();
return true;
}
});
@ -112,7 +107,6 @@ public class PreGameScreen extends MyScreen {
hide();
uiManager.setScreen.setPrevScreen(thisScreen);
uiManager.main.setScreen(uiManager.setScreen);
System.out.println("Bup");
return true;
}
});
@ -134,21 +128,31 @@ public class PreGameScreen extends MyScreen {
@Override
public void buildTable() {
super.buildTable();
firstRowTable.clear();
firstRowTable.clear();
float d = containerDiagonal();
float labScale = d * .00090f;
float buttonDim = d * 0.05f;
labels = new Label[MazeSettings.MAXPLAYERS];
// Labels to know if players joined
for (int i = 0; i < labels.length; i++) {
labels[i] = new Label("-- empty slot --", uiManager.skin);
}
System.out.println(Arrays.toString(labels));
firstRowContainer.setSize(cw, ch * 0.2f);
firstRowContainer.setPosition(tableContainer.getX(), ch * 0.1f);
firstRowContainer.fill();
helpDlg.setSize(cw*0.65f, ch*0.4f);
helpDlg.setPosition((sw-helpDlg.getWidth())/2, (sh-helpDlg.getHeight())/2);
helpDlgText.setFontScale(labScale*0.9f);
helpDlgOkBtn.getLabel().setFontScale(labScale*0.9f);
helpDlg.setSize(cw * 0.65f, ch * 0.4f);
helpDlg.setPosition((sw - helpDlg.getWidth()) / 2, (sh - helpDlg.getHeight()) / 2);
helpDlgText.setFontScale(labScale * 0.9f);
helpDlgOkBtn.getLabel().setFontScale(labScale * 0.9f);
instLab.setFontScale(labScale);
backBtn.getLabel().setFontScale(labScale);
setBtn.getLabel().setFontScale(labScale);
@ -162,6 +166,7 @@ public class PreGameScreen extends MyScreen {
firstRowTable.add(helpBtn).fillX().expandX().space(cw * 0.005f).width(buttonDim).height(buttonDim);
table.row().colspan(MazeSettings.MAXPLAYERS == 2 ? 2 : 4);
table.row().colspan(4);
table.add(firstRowContainer);
for (int i = 0; i < labels.length; i++) {
@ -184,13 +189,12 @@ public class PreGameScreen extends MyScreen {
// server
nPlayers = type == GameType.SERVER ? uiManager.main.server.remotePlayers.values().size()
: uiManager.main.client.players.size();
if (nPlayers != nPlayersOld) {
if (labels.length > 0) {
// Update Labels
for (int i = 0; i < labels.length; i++) {
labels[i].setText(i < nPlayers ? "-- Player Ready! --" : "-- empty slot --");
}
}
nPlayersOld = nPlayers;
}
public void setGameType(GameType t) {

View File

@ -8,6 +8,7 @@ import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.ScrollPane;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.emamaker.amazeing.AMazeIng;
import com.emamaker.amazeing.maze.settings.MazeSetting;
import com.emamaker.amazeing.maze.settings.MazeSettings;
import com.emamaker.amazeing.ui.UIManager;
@ -137,8 +138,12 @@ public class SettingsScreen extends MyScreen {
saveBtn.addListener(new InputListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
//If we are running server, we must send update to clients
AMazeIng.getMain().server.updateSettingForAll();
hide();
uiManager.main.setScreen(prevScreen == null ? uiManager.titleScreen : prevScreen);
return true;
}
});