Merge branch 'SerainaSpiellogik'

This commit is contained in:
Seraina 2022-04-14 08:17:36 +02:00
commit b8c970801c
29 changed files with 871 additions and 246 deletions

View File

@ -6,6 +6,8 @@ Implemented:
* CPING Ping from client to server.
* PINGB Pingback from client to server.
* NAMEC$name Change name to whatever is specified
* STGAM start the Game
* CVOTE$position$vote Client at position has voted for position
Future / planned:
* CRTGM Create a new game
@ -22,8 +24,8 @@ Server Commands:
Implemented:
* SPING Ping from server to client
* PINGB Pingback from client to server.
* GVOTR Ghosts: Please vote now
* HVOTR Humans: Please vote now
* GVOTR$position$msg Ghosts: Please vote now
* HVOTR$position$msg Humans: Please vote now
Future / planned:
* MSGRS "Message received": Paramaters: a string detailing to the client that and what the server received as command.

View File

@ -19,7 +19,7 @@ public class BudaLogConfig {
LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME);
loggerConfig.setLevel(Level.DEBUG); // change level here
loggerConfig.setLevel(Level.INFO); // change level here
ctx.updateLoggers(); // This causes all Loggers to refetch information from their LoggerConfig.
}

View File

@ -11,10 +11,23 @@ import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
public class ClientGameInfoHandler {
/**
* sends a msg "" to Server stating who voted for who, this being the Client that votes
* @param position the position of the passenger that is voted for
* All messages that are used in VoteHandler
* TODO(Seraina&Alex): Adjust strings to be more meaningful
*/
public void vote(int position) {
//relevant:
public static final String ghostVoteRequest = "Vote on who to ghostify!";
public static final String humanVoteRequest = "Vote for a ghost to kick off!";
public static final String noiseNotification = "You heard some noise";
public static final String gameOverHumansWin = "Game over: humans win!";
public static final String gameOverGhostsWin = "Game over: ghosts win!";
//just messages
public static final String itsNightTime = "Please wait, ghosts are active";
public static final String youGotGhostyfied = "You are now a ghost!";
public static final String itsDayTime = "Please wait, humans are active";
public static final String humansVotedFor = "Humans voted for: ";
public static final String isAHuman = " but they're a human!";
public static final String gotKickedOff = " is a Ghost and got kicked off";
}
}

View File

@ -0,0 +1,47 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
import java.util.Arrays;
/**
* Data structure that is used to store clientVotes in an array, where the index correponds to the
* position in the train of the client.
*/
public class ClientVoteData {
private int[] vote; //saves vote of clientHandler for later transmission to passenger, by default MAX_VALUE, index corresponds to Passenger position
private boolean[] hasVoted; //saves hasVoted status of clientHandler for later transmission to passenger, by default false, index corresponds to Passenger position
public ClientVoteData() {
int[] h = new int[6];
Arrays.fill(h,Integer.MAX_VALUE);
this.vote = h;
this.hasVoted = new boolean[6];
}
public int[] getVote() {
return vote;
}
public boolean[] getHasVoted() {
return hasVoted;
}
/**
* Sets a vote value at the right position in the vote array
* @param position the index of the array
* @param vote the vote value
*/
public void setVote(int position, int vote) {
this.vote[position] = vote;
}
/**
* Sets true or false at the right position in the hasVoted array
* @param position the index of the array
* @param hasVoted the vote state value
*/
public void setHasVoted(int position, boolean hasVoted) {
this.hasVoted[position] = hasVoted;
}
}

View File

@ -2,19 +2,29 @@ package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostNPC;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostPlayer;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.HumanNPC;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.HumanPlayer;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import java.util.HashSet;
import org.apache.logging.log4j.*;
public class Game {
public class Game implements Runnable {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
/**
* Can be extended for optional Game-settings
**/
protected int nrOfPlayers; //sets the length of the train
protected int nrOfGhosts; // sets how many Ghosts we start witch
protected final int nrOfPlayers; //sets the length of the train
protected final int nrOfGhosts; // sets how many Ghosts we start witch
protected int nrOfUsers; // safes how many clients are active in this Game
protected GameFunctions gameFunctions;
protected GameState gameState;
protected boolean isDay = false; //false means it is night, it is night by default
protected VoteHandler voteHandler = new VoteHandler();
private ClientHandler clientHandler;
//TODO: Figure out where Day/Night game state is saved maybe think about a game state class or smt.
/**
* Constructs a Game instance where:
@ -23,26 +33,107 @@ public class Game {
* @param nrOfGhosts is the number of OG Ghosts you want to start with and
* @param nrOfUsers is the number of active users at the time (non NPCs)
*/
Game(int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
public Game(ClientHandler clientHandler, int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
throws TrainOverflow { //ToDo: Who handles Exception how and where
this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers;
this.gameFunctions = new GameFunctions(nrOfPlayers, nrOfGhosts, nrOfUsers);
this.gameState = new GameState(nrOfPlayers, nrOfGhosts, nrOfUsers);
this.clientHandler = clientHandler;
}
public GameState getGameState() {
return gameState;
}
public static void main(String[] args) {
public int getNrOfGhosts() {
return nrOfGhosts;
}
try {
public int getNrOfPlayers() {
return nrOfPlayers;
}
Game game1 = new Game(6, 1, 1);
public int getNrOfUsers() {
return nrOfUsers;
}
public ClientHandler getClientHandler() {
return clientHandler;
}
public boolean getIsDay() {return isDay;}
public void setDay(boolean day) {
isDay = day;
}
/**
* Starts a new game, creates a passenger array and saves it in gameState, sets the OG
* currently at gameState.train[3] fills the passengerTrain moving from left to rigth in the
* gameState.train array it connects clientHandlers witch the passengers in those positions
* (Players) and fills the rest with NPC's
* TODO: set ghost in a random position(i), gameState.train[i] so that a lone player can also start as a Ghost maybe use Train class
*/
@Override
public void run() {
LOGGER.info("the run-method has been called");
int i = 0;
HashSet<ClientHandler> clients = ClientHandler.getConnectedClients();
String gameOverCheck = "";
int[] order = gameState.getTrain().orderOfTrain;
Passenger[] passengerTrain = gameState.getPassengerTrain();
LOGGER.info(gameState.toString());
for (ClientHandler client : clients) {
int index = order[i];
if (passengerTrain[index].getIsGhost()) { //if there is a ghost
GhostPlayer ghostPlayer = new GhostPlayer(passengerTrain[index].getPosition(),
client.getClientUserName(), client, passengerTrain[index].getIsOG());
gameState.getPassengerTrain()[index] = ghostPlayer;
} else {
HumanPlayer humanPlayer = new HumanPlayer(passengerTrain[index].getPosition(),
client.getClientUserName(), client, passengerTrain[index].getIsOG());
gameState.getPassengerTrain()[index] = humanPlayer;
}
i++;
}
while (i < order.length) {
int index = order[i];
if (passengerTrain[index].getIsGhost()) { //if they are a ghost
GhostNPC ghostNPC = new GhostNPC(passengerTrain[index].getPosition(), "NPC" + passengerTrain[index].getPosition(),passengerTrain[index].getIsOG());
gameState.getPassengerTrain()[index] = ghostNPC;
} else {
//ToDo: give NPC nice usernames
HumanNPC humanNPC = new HumanNPC(passengerTrain[index].getPosition(), "NPC" + passengerTrain[index].getPosition());
gameState.getPassengerTrain()[index] = humanNPC;
}
i++;
}
LOGGER.info(gameState.toString());
i = 0;
while (true) { //ToDo: was ist die Abbruchbedingung? VoteHandler muss das schicken.
if (!isDay) {
LOGGER.info("NIGHT");
voteHandler.ghostVote(gameState.getPassengerTrain(), this);
setDay(true);
} else {
LOGGER.info("DAY");
gameOverCheck = voteHandler.humanVote(gameState.getPassengerTrain(), this);
setDay(false);
}
if (gameOverCheck.equals(ClientGameInfoHandler.gameOverGhostsWin) || gameOverCheck.equals(
ClientGameInfoHandler.gameOverHumansWin)) {
clientHandler.broadcastAnnouncement(gameOverCheck);
return;
}
}
} catch (TrainOverflow e) {
System.out.println(e.getMessage());
}
}
}

View File

@ -1,60 +0,0 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Human;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GameFunctions {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
/**
* Can be extended for optional Game-settings
**/
int nrOfPlayers; //sets the length of the train
int nrOfGhosts; // sets how many Ghosts we start witch
int nrOfUsers; // safes how many clients are active in this Game
Train train; // safes who sits where
public Passenger[] passengerTrain;
/**
* Constructs a GameFunctions instance where nrOfPlayers >= nrOfUsers. Fills passengerTrain with
* only humans
*
* @param nrOfPlayers is the length of the Train
* @param nrOfGhosts is the number of OG Ghosts you want to start with and
* @param nrOfUsers is the number of active users at the time (non NPCs)
* @throws TrainOverflow if nrOfPlayers < nrOfUsers
*/
GameFunctions(int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
throws TrainOverflow { //ToDo: where will Exception be handled?
this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers;
this.train = new Train(nrOfPlayers, nrOfUsers);
Passenger[] passengerTrain = new Passenger[nrOfPlayers]; //Creates an array with Passengers with correlation positions (Train)
for (int i = 0; i < nrOfPlayers; i++) {
Human h = new Human();
h.setPosition(train.orderOfTrain[i]);
passengerTrain[train.orderOfTrain[i]] = h;
}
this.passengerTrain = passengerTrain;
}
public int getNrOfGhosts() {
return nrOfGhosts;
}
public int getNrOfPlayers() {
return nrOfPlayers;
}
public int getNrOfUsers() {
return nrOfUsers;
}
}

View File

@ -0,0 +1,142 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Human;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class GameState {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
/**
* Can be extended for optional Game-settings
**/
public final int nrOfPlayers; //sets the length of the train
public final int nrOfGhosts; // sets how many Ghosts we start witch
public final int nrOfUsers; // safes how many clients are active in this Game
public final Train train; // safes who sits where
/**
* contains all Passengers on train, needs to be updated
*/
private Passenger[] passengerTrain;
/**
* Saves ClientVoteData, might not be used
*/
private ClientVoteData clientVoteData;
/**
* Constructs a GameState instance where nrOfPlayers >= nrOfUsers. Fills passengerTrain with
* only humans
*
* @param nrOfPlayers is the length of the Train
* @param nrOfGhosts is the number of OG Ghosts you want to start with and
* @param nrOfUsers is the number of active users at the time (non NPCs)
* @throws TrainOverflow if nrOfPlayers < nrOfUsers
*/
GameState(int nrOfPlayers, int nrOfGhosts, int nrOfUsers)
throws TrainOverflow { //ToDo: where will Exception be handled?
this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers;
this.train = new Train(nrOfPlayers, nrOfUsers);
clientVoteData = new ClientVoteData();
Passenger[] passengerTrain = new Passenger[nrOfPlayers]; //Creates an array with Passengers with correlation positions (Train)
for (int i = 0; i < nrOfPlayers; i++) {
if (i == 3) { //TODO: randomize via Train.ghostposition
Ghost g = new Ghost();
g.setPosition(train.orderOfTrain[i]);
g.setGhost();
g.setIsOG(true);
passengerTrain[train.orderOfTrain[i]] = g;
} else {
Human h = new Human();
h.setPosition(train.orderOfTrain[i]);
passengerTrain[train.orderOfTrain[i]] = h;
}
}
this.passengerTrain = passengerTrain;
}
public Passenger[] getPassengerTrain() {
return passengerTrain;
}
public Train getTrain() {
return train;
}
/**
* Takes a given Passenger and puts it into the passengerTrain at a certain position
* @param passenger the new passenger being put into the train
* @param position the position of the passenger
*/
public void addNewPassenger(Passenger passenger, int position) {
passengerTrain[position] = passenger;
}
/**
* Converts the data in this passengerTrain into a human-readable string,
* where one can see who is a ghost and who is a human, who is a player and who an NPC
* @return a String that displays passengerTrain
*/
public String toString() {
Passenger[] array = passengerTrain;
StringBuilder stringBuilder = new StringBuilder();
String[] print = new String[6];
for (int i = 0; i < array.length; i++) {
if (array[i].getKickedOff()) {
print[i] = "-| kicked off: " + array[i].getPosition() + "|-";
} else {
if (array[i].getIsPlayer()) {
if (array[i].getIsGhost()) {
print[i] = "-| ghostPlayer: " + array[i].getPosition() + "|-";
} else {
print[i] = "-| humanPlayer: " + array[i].getPosition() + "|";
}
} else {
if (array[i].getIsGhost()) {
print[i] = "-| ghostNPC: " + array[i].getPosition() + "|-";
} else {
print[i] = "-| humanNPC: " + array[i].getPosition() + "|-";
}
}
}
}
for (int i = 0; i < array.length; i++) {
stringBuilder.append(print[i]);
}
return stringBuilder.toString();
}
/**
* Converts the data in this passengerTrain into a human-readable string, but it is anonymised for
* human players, so it is not obvious who is a human and who a ghost, only names and positions
* are displayed
* @return the String displaying an anonymised passengerTrain
*/
public String humanToString() {
Passenger[] array = passengerTrain;
StringBuilder stringBuilder = new StringBuilder();
String[] print = new String[6];
for (int i = 0; i < array.length; i++) {
print[i] = "-| " + array[i].getName() + ": " + array[i].getPosition() + "|-";
}
for (int i = 0; i < array.length; i++) {
stringBuilder.append(print[i]);
}
return stringBuilder.toString();
}
}

View File

@ -2,6 +2,7 @@ package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostNPC;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostPlayer;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import org.apache.logging.log4j.LogManager;
@ -19,12 +20,24 @@ public class GhostifyHandler {
public Ghost ghost(Passenger p, Game game) { //TODO: Adjust for not only players but also npcs
LOGGER.debug("Passenger Position " + p.getPosition());
p.setGhost();
Ghost g;
g = new Ghost();
g.setGhost();
g.setPosition(p.getPosition());
game.gameFunctions.passengerTrain[g.getPosition()] = g;
if (p.getIsPlayer()) {
p.setGhost();
GhostPlayer ghostPlayer;
ghostPlayer = new GhostPlayer(p.getPosition(), p.getName(), p.getClientHandler(),p.getIsOG());
ghostPlayer.setGhost();
ghostPlayer.setPosition(p.getPosition());
g = ghostPlayer;
} else {
p.setGhost();
GhostNPC ghostNPC;
ghostNPC = new GhostNPC(p.getPosition(), p.getName(),p.getIsOG());
ghostNPC.setGhost();
ghostNPC.setPosition(p.getPosition());
g = ghostNPC;
}
game.gameState.addNewPassenger(g, g.getPosition());
LOGGER.info("Passenger at position " + p.getPosition() + " has been ghostified");
return g;
}

View File

@ -1,10 +1,37 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
/**
* Determines who heard something (via Passenger Array currently in GameFunctions 'passengerTrain')
* and broadcasts noise message to them (via ServerGameInfoHandler)
*/
public class NoiseHandler {
/**
* Notifies passengers in the train about a ghost walking by them. Differentiates between two
* cases: if the active ghost (predator) is to the right of his victim, the Passenger array is
* being walked through from right to left (from the predator's position back to the victim's
* position), otherwise the other way around. One call of noiseNotifier only deals with one
* predator infecting a victim, so if there are already multiple ghosts in the game, the method
* should be called for each of them individually.
*
* @param passengers passengers of the train the game is played in
* @param predator ghost that has infected a human player during this night (called upon as
* passenger for convenience reasons)
* @param victim human player who has been turned into a ghost this night
* @param game current game instance
*/
public void noiseNotifier(Passenger[] passengers, Passenger predator, Ghost victim, Game game) {
if (predator.getPosition() - victim.getPosition()
> 0) { // if predator is to the right of victim
for (int i = predator.getPosition() - 1; i > victim.getPosition(); i--) {
passengers[i].send(ClientGameInfoHandler.noiseNotification, game);
}
} else { // if predator is to the left of victim
for (int i = predator.getPosition() + 1; i < victim.getPosition(); i++) {
passengers[i].send(ClientGameInfoHandler.noiseNotification, game);
}
}
}
}

View File

@ -1,6 +1,14 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostNPC;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.HumanNPC;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* Handles all communications Server to Client concerning game state or game state related requests
@ -10,26 +18,89 @@ import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
*/
public class ServerGameInfoHandler {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
/**
* TODO(Seraina): Handle NPC's Maybe do that in Passenger send methode!
* Send a message "GVOTR" to a passenger urging them to vote for a human to infect
* Currently only handles only Players, so send a message to corresponding client
* @param passenger the passenger the message is meant to, should be a Ghost
* Gets a string msg from somewhere and formats it into protocol messages
* @param msg the message to be formatted
* @return a message in a protocol format
*/
public void sendVoteRequestGhosts(Passenger passenger){
passenger.getClientHandler().sendMsgToClient("GVOTR");
public static String format(String msg, Passenger p, Game game) {
switch (msg) {
case ClientGameInfoHandler.ghostVoteRequest:
msg = Protocol.serverRequestsGhostVote + "$" + p.getPosition() +"$" + game.gameState.toString();
break;
case ClientGameInfoHandler.humanVoteRequest:
msg = Protocol.serverRequestsHumanVote + "$" + p.getPosition() +"$"+ game.gameState.humanToString();
break;
default:
msg = Protocol.printToClientConsole + "$"+ msg;
}
LOGGER.debug(msg);
return msg;
}
/**
* TODO(Seraina): Handle NPC's
* Send a message "HVOTR" to a passenger urging them to vote for sm to kick off the train.
* Currently only handles only Players, so send a message to corresponding client
* @param passenger the passenger the message is meant to, can be either human or ghost
* Chooses for an NPC what they want to say, so they don't sound the same all the time
* @return a String saying that sm heard sm noise
*/
public void sendVoteRequestHumans(Passenger passenger){
passenger.getClientHandler().sendMsgToClient("HVOTR");
public static String noiseRandomizer() {
String a = "I heard some noise tonight";
String b = "noise";
String c = "I heard smt strange tonight";
String d = "Me, noise!";
String e = "Uuuuh, spoky noises";
int number = (int)(Math.random()*4);
switch (number) {
case 0:
return a;
case 1:
return d;
case 2:
return c;
case 3:
return e;
default:
return b;
}
}
/**
* decides which action an GhostNpc needs to take, based on a message
* @param npc the GhostNpc needing to do smt
* @param msg the msg containing the information on what to do
* @param game the game the GhostNpc lives in (in gameState.passengerTrain)
*/
public static void ghostNpcParser(GhostNPC npc, String msg, Game game) {
switch (msg) {
case ClientGameInfoHandler.noiseNotification:
String outMsg = npc.getName() + ": " + noiseRandomizer();
game.getClientHandler().broadcastNpcChatMessage(outMsg);
break;
case ClientGameInfoHandler.ghostVoteRequest:
npc.vote(game);
}
}
/**
* decides which action an HumanNpc needs to take, based on a message
* @param npc the HumanNpc needing to do smt
* @param msg the msg containing the information on what to do
* @param game the game the HumanNpc lives in (in gameState.passengerTrain)
*/
public static void humanNpcParser(HumanNPC npc, String msg, Game game) {
switch (msg) {
case ClientGameInfoHandler.noiseNotification:
String outMsg = npc.getName() + ": " + noiseRandomizer();;
game.getClientHandler().broadcastNpcChatMessage(outMsg);
break;
case ClientGameInfoHandler.humanVoteRequest:
npc.vote();
}
}
}

View File

@ -9,7 +9,7 @@ public class Train {
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
int[] orderOfTrain; //gives the random order in which the passengers enter the train
int positionOfGhost;
int positionOfGhost; // useful for randomization of og ghost position
/**
* Constructs a Train with orderOfTrain of the size nrOfPlayers, filled with a Random order of the
@ -68,11 +68,7 @@ public class Train {
}
public static void main(String[] args) {
try {
Train t = new Train(6, 1);
} catch (TrainOverflow e) {
LOGGER.error(e.getMessage());
System.out.println("Hallo");
}
}
}

View File

@ -1,5 +1,8 @@
package ch.unibas.dmi.dbis.cs108.gamelogic;
/**
* An exception that is thrown, if for some reason to many clients want to start a game
*/
public class TrainOverflow extends Exception {
private static final String message = "Too many users are logged on";

View File

@ -2,9 +2,7 @@ package ch.unibas.dmi.dbis.cs108.gamelogic;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.GhostPlayer;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Passenger;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -23,6 +21,20 @@ public class VoteHandler {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
private static ClientVoteData clientVoteData = new ClientVoteData();
public static ClientVoteData getClientVoteData() {
return clientVoteData;
}
public static void setClientVoteData(ClientVoteData clientVoteData) {
clientVoteData = clientVoteData;
}
/**
* Handles the ghost vote during nighttime: passengers who are ghosts are being asked on who to
@ -31,7 +43,10 @@ public class VoteHandler {
*
* @param passengers: passengers on the train
*/
public void ghostVote(Passenger[] passengers, Game game) {
LOGGER.debug("ghostVote has been called");
LOGGER.info(game.getGameState().toString());
// array to collect votes for all players during voting, i.e. votes for player 1 (passengers[0])
// are saved in
@ -39,60 +54,60 @@ public class VoteHandler {
int[] votesForPlayers = new int[6];
// Walk through entire train, ask ghosts to ghostify and humans to wait
// TODO(Seraina): Messages in for-loop should probably be handled by ServerGameInfoHandler
for (Passenger passenger : passengers) {
if (passenger.getIsGhost()) {
passenger.send("Vote on who to ghostify!");
passenger.send(ClientGameInfoHandler.ghostVoteRequest, game);
} else {
passenger.send(
"Please wait, ghosts are active"); // TODO(Seraina): make sure whatever clients send in
ClientGameInfoHandler.itsNightTime, game);
// this time, except chat is ignored
}
}
for (Passenger passenger : passengers) {
// collecting the votes - distribute them among the vote counters for all players
// Note: Each voting collects votes for all players even though some might not be concerned
// (i.e. ghosts during ghost vote). Those players will then get 0 votes so it doesn't matter.
// TODO: Perhaps the vote results should be handled by ClientGameInfoHandler
if (passenger.getHasVoted()) {
for (int i = 0; i < votesForPlayers.length; i++) {
if (passenger.getVote() == i) {
votesForPlayers[i]++;
LOGGER.info(passengers[i] + " has received the most votes");
}
}
}
try { // waits 20 seconds before votes get collected
Thread.sleep(30*1000);
} catch (InterruptedException e) {
LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted");
}
// count the votes - determine which player has the most votes by going through the
// votesForPlayers array
int currentMax = 0;
for (int votesForPlayer : votesForPlayers) {
if (votesForPlayer > currentMax) {
currentMax = votesForPlayer;
}
}
LOGGER.info("Most votes: " + currentMax + " vote");
int currentMax = voteEvaluation(passengers, votesForPlayers, clientVoteData, game);
LOGGER.debug("Most votes: " + currentMax + " vote");
// ghostify the player with most votes
int ghostPosition = 0;
for (int i = 0; i < votesForPlayers.length; i++) {
if (votesForPlayers[i] == currentMax) { // if player at position i has most votes
ghostPosition = i;
LOGGER.info("Most votes for Passenger " + i);
LOGGER.debug("Most votes for Passenger " + i);
}
}
LOGGER.debug("ghostPosition: " + ghostPosition);
LOGGER.info("Most votes for: " + ghostPosition);
GhostifyHandler gh = new GhostifyHandler();
Ghost g = gh.ghost(passengers[ghostPosition], game);
passengers[ghostPosition] = g;
passengers[ghostPosition].send(
"You are now a ghost!"); // TODO: ServerGameInfoHandler might deal with this one
ClientGameInfoHandler.youGotGhostyfied, game); // TODO: ServerGameInfoHandler might deal with this one
try { // waits 20 seconds before votes get collected
Thread.sleep(10);
} catch (InterruptedException e) {
LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted");
}
/* notify passengers the ghosts passed by - for each ghost that ghostified a player, an instance of NoiseHandler
is being created and the passengers this ghost passed by are being notified. The player who's just been ghostified
is ignored since he didn't participate in this night's ghostification. */
for (int i = 0; i < passengers.length; i++) {
if (passengers[i].getIsGhost() && i != ghostPosition) {
NoiseHandler n = new NoiseHandler();
n.noiseNotifier(passengers, passengers[i], g, game);
}
}
LOGGER.info(game.getGameState().toString());
// set hasVoted to false for all passengers for future votings
for (Passenger passenger : passengers) {
passenger.setHasVoted(false);
@ -104,11 +119,12 @@ public class VoteHandler {
* ghosts are waiting. Votes are being collected, vote results are being handled in three possible
* ways: if passenger who was voted for is a human, continue with next ghost vote; if it's a
* normal ghost, kick him off; if it's the OG ghost, end game, humans win.
*
* @param passengers: train passengers
* @return Returns an empty String by default, returns a complex string when game is over:
* "Game over: ghosts win!" or "Game over: humans win!"
* @param passengers train passengers
*/
public void humanVote(Passenger[] passengers, Game game) {
public String humanVote(Passenger[] passengers, Game game) {
LOGGER.info(game.getGameState().toString());
// array to collect votes for all players during voting, i.e. votes for player 1 are saved in
// votesForPlayers[0]
@ -118,51 +134,39 @@ public class VoteHandler {
// TODO: Messages in for-loop should probably be handled by ServerGameInfoHandler
for (Passenger passenger : passengers) {
if (passenger.getIsGhost()) {
passenger.send("Please wait, humans are active");
passenger.send(ClientGameInfoHandler.itsDayTime, game);
} else {
passenger.send("Vote for a ghost to kick off!");
passenger.send(ClientGameInfoHandler.humanVoteRequest, game);
}
}
for (Passenger passenger : passengers) {
// collecting the votes - distribute them among the vote counters for all players
// TODO: Perhaps the vote results should be handled by ClientGameInfoHandler
if (passenger.getHasVoted()) {
for (int i = 0; i < votesForPlayers.length; i++) {
if (passenger.getVote() == i) {
votesForPlayers[i]++;
}
}
}
try { // waits 20 seconds before votes get collected
Thread.sleep(20*1000);
} catch (InterruptedException e) {
LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted");
}
// count the votes - determine which player has the most votes by going through the
// votesForPlayers array
int currentMax = 0;
for (int votesForPlayer : votesForPlayers) {
if (votesForPlayer > currentMax) {
currentMax = votesForPlayer;
LOGGER.info("Max amount of votes: " + currentMax);
}
}
int currentMax = voteEvaluation(passengers, votesForPlayers, clientVoteData, game);
// deal with voting results
int voteIndex = 0;
for (int i = 0; i < votesForPlayers.length; i++) {
if (votesForPlayers[i] == currentMax) { // if player has most votes
voteIndex = i;
}
}
LOGGER.info("Player " + voteIndex + " has the most votes");
}
}
if (!passengers[voteIndex]
.getIsGhost()) { // if player with most votes is human, notify everyone about it
for (Passenger passenger : passengers) {
passenger.send(
"You voted for a human!"); // TODO: ServerGameInfoHandler might be better to use here
ClientGameInfoHandler.humansVotedFor + voteIndex + ClientGameInfoHandler.isAHuman, game); // TODO: ServerGameInfoHandler might be better to use here
}
}
if (passengers[voteIndex].getIsGhost()) { // if player is a ghost
if (passengers[voteIndex].getIsOG()) { // if ghost is OG --> end game, humans win
System.out.println("Game over: humans win!"); // TODO: correctly handle end of game
System.out.println(ClientGameInfoHandler.gameOverHumansWin); // TODO: correctly handle end of game
return ClientGameInfoHandler.gameOverHumansWin;
} else {
/* Special case: if ghost is not OG and if only one human is left (--> last human didn't vote for OG ghost),
ghosts win.
@ -174,20 +178,23 @@ public class VoteHandler {
}
}
if (humans == 1) {
System.out.println("Game over: ghosts win!");
System.out.println(ClientGameInfoHandler.gameOverGhostsWin);
return ClientGameInfoHandler.gameOverGhostsWin;
}
// Usual case: there is more than one human left and a normal ghost has been voted for -->
// kick this ghost off
passengers[voteIndex].setKickedOff(true);
for (Passenger passenger : passengers) {
passenger.send("Player " + voteIndex + " has been kicked off!");
passenger.send("Player " + voteIndex + ClientGameInfoHandler.gotKickedOff, game);
}
}
}
LOGGER.info(game.getGameState().toString());
// set hasVoted to false for all passengers for future voting
for (Passenger passenger : passengers) {
passenger.setHasVoted(false);
}
return "";
}
/**
@ -216,38 +223,34 @@ public class VoteHandler {
}
public static void main(String[] args) {
try {
Game game = new Game(6,1, 6);
VoteHandler voteHandler = new VoteHandler();
Passenger[] testArray = game.gameFunctions.passengerTrain;
Passenger ghost = new Ghost();
testArray[3] = ghost;
testArray[3].setGhost();
testArray[3].setIsOg();
testArray[3].setPosition(3);
print(testArray);
LOGGER.info("NIGHT");
voteHandler.ghostVote(testArray,game);
print(testArray);
LOGGER.info("Day");
voteHandler.humanVote(testArray, game);
print(testArray);
LOGGER.info("NIGHT");
voteHandler.ghostVote(testArray,game);
print(testArray);
LOGGER.info("Day");
voteHandler.humanVote(testArray, game);
print(testArray);
} catch (TrainOverflow e) {
LOGGER.warn(e.getMessage());
}
/**
* Collecting the votes - distribute them among the vote counters for all players. Note: each voting collects
* votes for all players even though some might not be concerned (i.e. ghosts during ghost vote). Those players
* will then get 0 votes so it dosen't matter. Returns the max amount of votes a player received.
* @param passengers train passengers
* @param votesForPlayers array collecting the votes each player received during a voting
* @param data deals with Client votes
* @param game current game instance
*/
int voteEvaluation(Passenger[] passengers, int[] votesForPlayers, ClientVoteData data, Game game) {
for (Passenger passenger : passengers) {
passenger.getVoteFromGameState(data, game);
if (passenger.getHasVoted()) {
for (int i = 0; i < votesForPlayers.length; i++) {
if (passenger.getVote() == i) {
votesForPlayers[i]++;
}
}
}
}
/* count the votes - determine which player has the most votes by going through the
votesForPlayers array */
int currentMax = 0;
for (int votesForPlayer : votesForPlayers) {
if (votesForPlayer > currentMax) {
currentMax = votesForPlayer;
}
}
return currentMax;
}
}

View File

@ -1,6 +1,7 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -8,6 +9,8 @@ public class Ghost extends Passenger {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
public boolean getIsOG() {
return isOG;
}

View File

@ -1,6 +1,10 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.GameState;
import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler;
import ch.unibas.dmi.dbis.cs108.gamelogic.TrainOverflow;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -28,4 +32,43 @@ public class GhostNPC extends Ghost {
this.name = name;
}
}
@Override
public void send(String msg, Game game) {
ServerGameInfoHandler.ghostNpcParser(this, msg, game);
}
/**
* Sets vote of this Ghost position on a number between 0 and 5,
* but only for positions where there aren't any ghosts and sets hasVoted to true
* TODO: Make NPC smarter
*/
public void vote(Game game){
int ghostCounter = 0;
Passenger[] train = game.getGameState().getPassengerTrain();
for(Passenger passenger : train) {
if(passenger.isGhost) {
ghostCounter++;
}
}
int[] humanPositions = new int[game.getNrOfPlayers() - ghostCounter ];
int j = 0;
for (int i = 0; i < train.length; i++) {
if (!train[i].isGhost) { //is human
humanPositions[j] = train[i].getPosition();
j++;
}
}
int randomPosition = (int) (Math.random()*humanPositions.length);
vote = humanPositions[randomPosition];
hasVoted = true;
LOGGER.info("GhostNPC at Position: " + this.getPosition() + " has voted for: " + vote);
}
/**
* Decides what to do when a noise ist heard, currently just always broadcasts it
* TODO: Make NPC smarter
*/
}

View File

@ -1,6 +1,9 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.ClientVoteData;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -12,7 +15,6 @@ public class GhostPlayer extends Ghost {
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost.
*
* @param position position on the train
* @param name name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost.
@ -31,8 +33,36 @@ public class GhostPlayer extends Ghost {
}
}
/**
* Sends a message to the client handled bye this client handler
* TODO: does this also work with 2 clients?
* @param msg the message that is sent to this player.
* @param game the game the GhostPlayer lives on (in game.gameState.passengerTrain)
*/
@Override
public void send(String msg, Game game) {
String formattedMsg = ServerGameInfoHandler.format(msg, this, game);
clientHandler.sendMsgToClient(formattedMsg);
}
public void send(String msg) {
//todo(Jonas): pass message along to client.
/**
* Gets the voting information vote and hasVoted from clientHandler and this values to those values.
* Sets clientHandler fields to default: vote = Integer.MAX_VALUE , hasVoted = false
*/
@Override
public void getVoteFromGameState(ClientVoteData clientVoteData, Game game) {
vote = clientVoteData.getVote()[position];
hasVoted = clientVoteData.getHasVoted()[position];
clientVoteData.setVote(position,Integer.MAX_VALUE);
clientVoteData.setHasVoted(position,false);
LOGGER.info("Ghost at Pos: " + position + " has voted for: " + vote);
/*
* if vote wasn't valid, make sure, the passenger field hasVoted == false, probably redundant but better be safe than sorry
*/
if(vote == Integer.MAX_VALUE) {
hasVoted = false;
}
}
}

View File

@ -1,6 +1,7 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -8,4 +9,5 @@ public class Human extends Passenger {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
}

View File

@ -1,10 +1,13 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class HumanNPC extends Human {
public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
@ -26,4 +29,27 @@ public class HumanNPC extends Human {
this.name = name;
}
}
/**
* Sends a msg to the ServerGameInfoHandler.humanNpcParser to decide what has to happen now
*
* @param msg the message that is sent to this player.
* @param game the game the HumanNPC lives on (in game.gameState.passengerTrain)
*/
@Override
public void send(String msg, Game game) {
ServerGameInfoHandler.humanNpcParser(this, msg, game);
}
/**
* Currently returns a random integer for voting
*
* @return integer between 0 and 5
*/
public void vote() {
int randomNr = (int) (Math.random() * 6);
vote = randomNr;
hasVoted = true;
LOGGER.info("HumanNPC at Position: " + this.getPosition() + " has voted for: " + vote);
}
}

View File

@ -1,7 +1,11 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.ClientVoteData;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -12,7 +16,6 @@ public class HumanPlayer extends Human {
/**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost.
*
* @param position position on the train
* @param name name. if null, then a default name is used.
*/
@ -29,5 +32,39 @@ public class HumanPlayer extends Human {
}
}
/**
* Sends a message to the client handled bye this client handler
* TODO: does this also work with 2 clients?
* @param msg the message that is sent to this player.
* @param game the game the HumanPlayer lives on (in game.gameState.passengerTrain)
*/
@Override
public void send(String msg, Game game) {
String formattedMsg = ServerGameInfoHandler.format(msg,this, game);
clientHandler.sendMsgToClient(formattedMsg);
}
/**
* Gets the voting information vote and hasVoted from clientHandler and this values to those values.
* Sets clientHandler fields to default: vote = Integer.MAX_VALUE , hasVoted = false
*/
@Override
public void getVoteFromGameState(ClientVoteData clientVoteData, Game game) {
if(game.getIsDay()) {
LOGGER.debug(Arrays.toString(clientVoteData.getVote()));
LOGGER.debug("method was called by: " + position);
vote = clientVoteData.getVote()[position];
LOGGER.info("Human at Pos: " + position + " has voted for: " + vote);
hasVoted = clientVoteData.getHasVoted()[position];
LOGGER.debug(Arrays.toString(clientVoteData.getVote()));
clientVoteData.setVote(position, Integer.MAX_VALUE);
clientVoteData.setHasVoted(position, false);
/*
* if vote wasn't valid, make sure, the passenger field hasVoted == false, probably redundant but better be safe than sorry
*/
if (vote == Integer.MAX_VALUE) {
hasVoted = false;
}
}
}
}

View File

@ -1,6 +1,8 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.ClientVoteData;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.ServerGameInfoHandler;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager;
@ -19,32 +21,7 @@ public class Passenger {
protected ClientHandler clientHandler;//the socket for the client associated with this Passenger, for NPCs, this can be null.
protected boolean hasVoted; //true if the player gave his vote during voting time
protected int vote; //saves the number of the player this passenger voted for during voting (0-5)
int sendcounter = 0;
/**
* Sends a protocol message to the respective player or NPC.
* @param msg the message that is sent to this player.
**/
public void send(String msg) {
sendcounter++;
if (msg.equals("Vote on who to ghostify!")) {
vote = 1*sendcounter;
hasVoted = true; // for testing, when is it set to false again?
LOGGER.info("Voted for Position " + vote);
} else if(msg.equals("Vote for a ghost to kick off!")) {
vote = (int) (0.5*sendcounter);
hasVoted = true; // for testing, when is it set to false again?
LOGGER.info("Voted for Position " + vote);
} else {
LOGGER.debug(msg);
}
/*if (isPlayer) {
//TODO: maybe put a formatter here, so protocol msg are only send between sever-client
clientHandler.sendMsgToClient(msg); //ToDO(Seraina): Make sure only the right kind of messages are sent
} else { //is a NPC
//TODO: call a method that identifies message for NPC and calls respective methode NPCParser
}*/
}
/**
* sets the Position of this passenger
@ -82,6 +59,10 @@ public class Passenger {
hasVoted = voted;
}
public void setVote(int vote) {
this.vote = vote;
}
public void setIsOg() {
isOG = true;
}
@ -116,4 +97,24 @@ public class Passenger {
public ClientHandler getClientHandler() {
return clientHandler;
}
/**
* When called by NPC nothing should happen, because clientHandler = null
*/
public void getVoteFromGameState(ClientVoteData clientVoteData,Game game) {
LOGGER.debug("a NPC called this method hopefully: " + position);
}
/**
* Sends a protocol message to the respective player or NPC.
* @param msg the message that is sent to this player.
**/
public void send(String msg, Game game) {
if (isPlayer) {
String formattedMsg = ServerGameInfoHandler.format(msg,this,game);
clientHandler.sendMsgToClient(formattedMsg);
}
LOGGER.warn("This object should not just be a passenger. Position:" + position);
}
}

View File

@ -5,11 +5,14 @@ import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ClientPinger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.JServerProtocolParser;
import java.net.Socket;
import java.io.*;
import java.net.UnknownHostException;
import java.util.Objects;
import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -23,6 +26,11 @@ public class Client {
private BufferedWriter out;
public ClientPinger clientPinger;
/**
* Saves the position of the client, gets refreshed everytime the client gets a vote request.
*/
int position = Integer.MAX_VALUE;
public Client(Socket socket) {
try {
this.socket = socket;
@ -58,7 +66,7 @@ public class Client {
try {
if (bfr.ready()) {
String msg = bfr.readLine();
String formattedMSG = MessageFormatter.formatMsg(msg);
String formattedMSG = MessageFormatter.formatMsg(msg, position);
sendMsgToServer(formattedMSG);
}
Thread.sleep(5);
@ -73,6 +81,30 @@ public class Client {
}
/**
* Tells user to enter a position to vote for passenger at that position
*/
public void positionSetter(String msg) {
LOGGER.info("Im in thread:" + Thread.currentThread());
int msgIndex = msg.indexOf('$');
String pos = msg.substring(0, msgIndex);
try {
position = Integer.parseInt(pos);
} catch (NumberFormatException e) {
LOGGER.warn("Position got scrabbled on the way here");
}
String justMsg = msg.substring(msgIndex + 1);
System.out.println(justMsg);
System.out.println("Please enter your vote");
//LOGGER.debug("just checked next line");
}
/**
* Starts a thread which listens for incoming chat messages / other messages that the user
* has to see
@ -92,7 +124,7 @@ public class Client {
} else { System.out.println("chatMsg is null"); throw new IOException();}
} catch (IOException e) {
//e.printStackTrace();
LOGGER.debug("Exception while trying to read message: " + e.getMessage());
LOGGER.warn("Exception while trying to read message: " + e.getMessage());
disconnectFromServer();
}

View File

@ -43,11 +43,15 @@ public class JClientProtocolParser {
c.disconnectFromServer();
break;
case Protocol.serverRequestsGhostVote:
System.out.println("Ghost received Vote request");
LOGGER.debug("Ghost received Vote request");
System.out.println("Ghost Vote:");
c.positionSetter(msg.substring(6));
//TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input
break;
case Protocol.serverRequestsHumanVote:
System.out.println("Human received Vote request");
LOGGER.debug("Human received Vote request");
System.out.println("Human Vote:");
c.positionSetter(msg.substring(6));
//TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input
break;
default:

View File

@ -18,7 +18,7 @@ public class MessageFormatter {
* @return the reformatted message in the form HEADR$msg
*/
public static String formatMsg(String msg) {
public static String formatMsg(String msg, int position) {
String header = ""; //header is first two characters
StringBuilder stringBuilder = new StringBuilder();
String s = ""; // just a friendly helper to save message in
@ -59,10 +59,20 @@ public class MessageFormatter {
stringBuilder.append(Protocol.listLobbies + "$");
s = ""; //Command has no parameters
break;
case "/v":
try {
s = msg.substring(3);
LOGGER.debug("substring: " + s);
} catch (Exception e) {
System.out.println("invalid vote");
}
stringBuilder.append(Protocol.votedFor + "$" + position + "$");
break;
default:
s = msg;
}
stringBuilder.append(s);
LOGGER.debug(stringBuilder.toString());
return stringBuilder.toString();
}

View File

@ -35,6 +35,7 @@ public class ClientPinger implements Runnable {
@Override
public void run() {
Thread.currentThread().setPriority(10);
try {
Thread.sleep(20000);
while (socket.isConnected() && !socket.isClosed()) {

View File

@ -92,6 +92,16 @@ public class Protocol {
*/
public static final String listLobbies = "LISTL";
/**
* A Client decides to start the game.
*/
public static final String startANewGame = "STGAM";
/**
* Client informs server that they have voted and delivers this vote in the form of "CVOTE$position$vote"
*/
public static final String votedFor = "CVOTE";
//SERVER TO CLIENT COMMANDS
@ -117,7 +127,8 @@ public class Protocol {
public static final String serverConfirmQuit = "QUITC";
/**
* The server requests the client (who should be a ghost) to vote on the victim.
* The server requests the client (who should be a ghost) to vote on the victim. in the format GVOTR$string
* the current train will be shown as a string to the client
*/
public static final String serverRequestsGhostVote = "GVOTR";
@ -128,4 +139,6 @@ public class Protocol {
public static final String serverRequestsHumanVote = "HVOTR";
}

View File

@ -11,6 +11,7 @@ Implemented:
* PINGB Pingback from client to server.
* NAMEC$name Change name to whatever is specified
* CRTGM Create a new game
*
Future / planned:

View File

@ -37,6 +37,7 @@ public class ServerPinger implements Runnable {
@Override
public void run() {
Thread.currentThread().setPriority(10);
try {
Thread.sleep(2000);
while (socket.isConnected() && !socket.isClosed()) {
@ -55,9 +56,8 @@ public class ServerPinger implements Runnable {
System.out.println(
"Lost connection to user " + c.getClientUserName() + ". Waiting to reconnect...");
} else {
c.disconnectClient();
LOGGER.debug(
"gotPingBack has not been set to true and isConnected has been set to false before");
//c.disconnectClient(); TODO: is that ever necessary?
//LOGGER.debug("gotPingBack has not been set to true and isConnected has been set to false before");
}
}
}

View File

@ -1,11 +1,15 @@
package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.TrainOverflow;
import ch.unibas.dmi.dbis.cs108.gamelogic.VoteHandler;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger;
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Scanner;
import org.apache.logging.log4j.LogManager;
@ -21,6 +25,7 @@ public class ClientHandler implements Runnable {
private Socket socket;
private InetAddress ip;
/**
* notes if the client has formally logged in yet. If connecting through the normal Client class,
* the client is logged in automatically, if connecting though some external application, the
@ -70,6 +75,10 @@ public class ClientHandler implements Runnable {
return socket;
}
/**
* Needed to fill a train with client TODO: how do lobbies fit here?
* @return the HashSet of Connected Clients
*/
public static HashSet<ClientHandler> getConnectedClients() {
return connectedClients;
}
@ -86,14 +95,14 @@ public class ClientHandler implements Runnable {
return loggedIn;
}
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
//Setters:
public String getClientUserName() {
return clientUserName;
}
//Setters:
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
@Override
@ -149,6 +158,17 @@ public class ClientHandler implements Runnable {
}
}
/**
* Broadcasts a pseudo chat Message from a NPC to all active clients
*
* @param msg the Message to be broadcast
*/
public void broadcastNpcChatMessage(String msg) {
for (ClientHandler client : connectedClients) {
client.sendMsgToClient(Protocol.printToClientConsole + "$" + msg);
}
}
/**
* Broadcasts a non-chat Message to all active clients. This can be used for server
* messages / announcements rather than chat messages. The message will be printed to the user
@ -180,6 +200,44 @@ public class ClientHandler implements Runnable {
}
}
/**
* Takes a msg of the form position$vote and extracts vote and position from it and saves it
* in VoteHandler.getClientVoteData
* @param msg the messaged to decode
*/
public void decodeVote(String msg){
int msgIndex = msg.indexOf('$');
int vote = Integer.MAX_VALUE;
int position = 0;
LOGGER.debug("Message is " + msg);
try {
position = Integer.parseInt(msg.substring(0,msgIndex));
vote = Integer.parseInt(msg.substring(msgIndex + 1));
LOGGER.debug("Vote is:" + vote);
} catch (Exception e) {
LOGGER.warn("Invalid vote " + e.getMessage());
}
LOGGER.debug("Vote is:" + vote);
if(vote != Integer.MAX_VALUE) { //gets MAX_VALUE when the vote wasn't valid
VoteHandler.getClientVoteData().setVote(position,vote);
LOGGER.debug("Player vote: " + vote);
VoteHandler.getClientVoteData().setHasVoted(position,true); //TODO: move clientVoteData to gamestate
}
}
/**
* Initializes a new Game instance and starts its run method in a new thread
*/
public void startNewGame() {
try {
Game game = new Game(this,6,1, ClientHandler.getConnectedClients().size());
Thread t = new Thread(game);
t.start();
} catch (TrainOverflow e) {
LOGGER.warn(e.getMessage());
}
}
/**
* Removes & disconnects the client. To be used if a severe connection loss is detected (i.e. if trying to
* send / receive a message throws an exception, not just if ping-pong detects a connection loss).

View File

@ -2,6 +2,11 @@ package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.ClientVoteData;
import ch.unibas.dmi.dbis.cs108.gamelogic.Game;
import ch.unibas.dmi.dbis.cs108.gamelogic.GameState;
import ch.unibas.dmi.dbis.cs108.gamelogic.TrainOverflow;
import ch.unibas.dmi.dbis.cs108.gamelogic.VoteHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol;
@ -30,6 +35,9 @@ public class JServerProtocolParser {
String header = ""; //"header" is the first 5 characters, i.e. the protocol part
try {
header = msg.substring(0, 5);
if(!header.equals(Protocol.pingBack) &&!header.equals(Protocol.pingFromClient)) { //for debuging without constant pings
LOGGER.debug("got message: " + msg + ".");
}
} catch (IndexOutOfBoundsException e) {
System.out.println("Received unknown command");
}
@ -65,7 +73,15 @@ public class JServerProtocolParser {
break;
case Protocol.listLobbies:
//TODO: add action
LOGGER.debug(Protocol.listLobbies + " command recieved from: " + h.getClientUserName());
LOGGER.debug(Protocol.listLobbies + " command received from: " + h.getClientUserName());
break;
case Protocol.votedFor:
LOGGER.debug("Made it here");
msg = msg.substring(6);
h.decodeVote(msg);
break;
case Protocol.startANewGame:
h.startNewGame();
break;
default:
System.out.println("Received unknown command");