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. * CPING Ping from client to server.
* PINGB Pingback from client to server. * PINGB Pingback from client to server.
* NAMEC$name Change name to whatever is specified * NAMEC$name Change name to whatever is specified
* STGAM start the Game
* CVOTE$position$vote Client at position has voted for position
Future / planned: Future / planned:
* CRTGM Create a new game * CRTGM Create a new game
@ -22,8 +24,8 @@ Server Commands:
Implemented: Implemented:
* SPING Ping from server to client * SPING Ping from server to client
* PINGB Pingback from client to server. * PINGB Pingback from client to server.
* GVOTR Ghosts: Please vote now * GVOTR$position$msg Ghosts: Please vote now
* HVOTR Humans: Please vote now * HVOTR$position$msg Humans: Please vote now
Future / planned: Future / planned:
* MSGRS "Message received": Paramaters: a string detailing to the client that and what the server received as command. * 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); LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
Configuration config = ctx.getConfiguration(); Configuration config = ctx.getConfiguration();
LoggerConfig loggerConfig = config.getLoggerConfig(LogManager.ROOT_LOGGER_NAME); 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. 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 { public class ClientGameInfoHandler {
/** /**
* sends a msg "" to Server stating who voted for who, this being the Client that votes * All messages that are used in VoteHandler
* @param position the position of the passenger that is voted for * 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.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.*; import org.apache.logging.log4j.*;
public class Game { public class Game implements Runnable {
public static final Logger LOGGER = LogManager.getLogger(); public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER); public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
/** /**
* Can be extended for optional Game-settings * Can be extended for optional Game-settings
**/ **/
protected int nrOfPlayers; //sets the length of the train protected final int nrOfPlayers; //sets the length of the train
protected int nrOfGhosts; // sets how many Ghosts we start witch protected final int nrOfGhosts; // sets how many Ghosts we start witch
protected int nrOfUsers; // safes how many clients are active in this Game 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. //TODO: Figure out where Day/Night game state is saved maybe think about a game state class or smt.
/** /**
* Constructs a Game instance where: * 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 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) * @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 throws TrainOverflow { //ToDo: Who handles Exception how and where
this.nrOfPlayers = nrOfPlayers; this.nrOfPlayers = nrOfPlayers;
this.nrOfGhosts = nrOfGhosts; this.nrOfGhosts = nrOfGhosts;
this.nrOfUsers = nrOfUsers; 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;
}
} catch (TrainOverflow e) { public ClientHandler getClientHandler() {
System.out.println(e.getMessage()); 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;
}
} }
} }
} }

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

View File

@ -1,10 +1,37 @@
package ch.unibas.dmi.dbis.cs108.gamelogic; 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') * Determines who heard something (via Passenger Array currently in GameFunctions 'passengerTrain')
* and broadcasts noise message to them (via ServerGameInfoHandler) * and broadcasts noise message to them (via ServerGameInfoHandler)
*/ */
public class NoiseHandler { 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; 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.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 * 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 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! * Gets a string msg from somewhere and formats it into protocol messages
* Send a message "GVOTR" to a passenger urging them to vote for a human to infect * @param msg the message to be formatted
* Currently only handles only Players, so send a message to corresponding client * @return a message in a protocol format
* @param passenger the passenger the message is meant to, should be a Ghost
*/ */
public void sendVoteRequestGhosts(Passenger passenger){ public static String format(String msg, Passenger p, Game game) {
passenger.getClientHandler().sendMsgToClient("GVOTR"); 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 * Chooses for an NPC what they want to say, so they don't sound the same all the time
* Send a message "HVOTR" to a passenger urging them to vote for sm to kick off the train. * @return a String saying that sm heard sm noise
* 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
*/ */
public void sendVoteRequestHumans(Passenger passenger){ public static String noiseRandomizer() {
passenger.getClientHandler().sendMsgToClient("HVOTR"); 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); public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
int[] orderOfTrain; //gives the random order in which the passengers enter the train 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 * 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) { public static void main(String[] args) {
try { System.out.println("Hallo");
Train t = new Train(6, 1);
} catch (TrainOverflow e) {
LOGGER.error(e.getMessage());
}
} }
} }

View File

@ -1,5 +1,8 @@
package ch.unibas.dmi.dbis.cs108.gamelogic; 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 { public class TrainOverflow extends Exception {
private static final String message = "Too many users are logged on"; 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.BudaLogConfig;
import ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur.Ghost; 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.gamelogic.klassenstruktur.Passenger;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -23,6 +21,20 @@ public class VoteHandler {
public static final Logger LOGGER = LogManager.getLogger(); public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER); 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 * 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 * @param passengers: passengers on the train
*/ */
public void ghostVote(Passenger[] passengers, Game game) { 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]) // array to collect votes for all players during voting, i.e. votes for player 1 (passengers[0])
// are saved in // are saved in
@ -39,60 +54,60 @@ public class VoteHandler {
int[] votesForPlayers = new int[6]; int[] votesForPlayers = new int[6];
// Walk through entire train, ask ghosts to ghostify and humans to wait // 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) { for (Passenger passenger : passengers) {
if (passenger.getIsGhost()) { if (passenger.getIsGhost()) {
passenger.send("Vote on who to ghostify!"); passenger.send(ClientGameInfoHandler.ghostVoteRequest, game);
} else { } else {
passenger.send( passenger.send(
"Please wait, ghosts are active"); // TODO(Seraina): make sure whatever clients send in ClientGameInfoHandler.itsNightTime, game);
// this time, except chat is ignored // this time, except chat is ignored
} }
} }
for (Passenger passenger : passengers) { try { // waits 20 seconds before votes get collected
// collecting the votes - distribute them among the vote counters for all players Thread.sleep(30*1000);
// Note: Each voting collects votes for all players even though some might not be concerned } catch (InterruptedException e) {
// (i.e. ghosts during ghost vote). Those players will then get 0 votes so it doesn't matter. LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted");
// 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");
}
}
}
} }
// count the votes - determine which player has the most votes by going through the int currentMax = voteEvaluation(passengers, votesForPlayers, clientVoteData, game);
// votesForPlayers array
int currentMax = 0; LOGGER.debug("Most votes: " + currentMax + " vote");
for (int votesForPlayer : votesForPlayers) {
if (votesForPlayer > currentMax) {
currentMax = votesForPlayer;
}
}
LOGGER.info("Most votes: " + currentMax + " vote");
// ghostify the player with most votes // ghostify the player with most votes
int ghostPosition = 0; int ghostPosition = 0;
for (int i = 0; i < votesForPlayers.length; i++) { for (int i = 0; i < votesForPlayers.length; i++) {
if (votesForPlayers[i] == currentMax) { // if player at position i has most votes if (votesForPlayers[i] == currentMax) { // if player at position i has most votes
ghostPosition = i; 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(); GhostifyHandler gh = new GhostifyHandler();
Ghost g = gh.ghost(passengers[ghostPosition], game); Ghost g = gh.ghost(passengers[ghostPosition], game);
passengers[ghostPosition] = g; passengers[ghostPosition] = g;
passengers[ghostPosition].send( 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 // set hasVoted to false for all passengers for future votings
for (Passenger passenger : passengers) { for (Passenger passenger : passengers) {
passenger.setHasVoted(false); 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 * 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 * 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. * normal ghost, kick him off; if it's the OG ghost, end game, humans win.
* * @return Returns an empty String by default, returns a complex string when game is over:
* @param passengers: train passengers * "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 // array to collect votes for all players during voting, i.e. votes for player 1 are saved in
// votesForPlayers[0] // votesForPlayers[0]
@ -118,51 +134,39 @@ public class VoteHandler {
// TODO: Messages in for-loop should probably be handled by ServerGameInfoHandler // TODO: Messages in for-loop should probably be handled by ServerGameInfoHandler
for (Passenger passenger : passengers) { for (Passenger passenger : passengers) {
if (passenger.getIsGhost()) { if (passenger.getIsGhost()) {
passenger.send("Please wait, humans are active"); passenger.send(ClientGameInfoHandler.itsDayTime, game);
} else { } else {
passenger.send("Vote for a ghost to kick off!"); passenger.send(ClientGameInfoHandler.humanVoteRequest, game);
} }
} }
for (Passenger passenger : passengers) { try { // waits 20 seconds before votes get collected
// collecting the votes - distribute them among the vote counters for all players Thread.sleep(20*1000);
// TODO: Perhaps the vote results should be handled by ClientGameInfoHandler } catch (InterruptedException e) {
if (passenger.getHasVoted()) { LOGGER.warn("Thread " + Thread.currentThread() + " was interrupted");
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 int currentMax = voteEvaluation(passengers, votesForPlayers, clientVoteData, game);
// votesForPlayers array
int currentMax = 0;
for (int votesForPlayer : votesForPlayers) {
if (votesForPlayer > currentMax) {
currentMax = votesForPlayer;
LOGGER.info("Max amount of votes: " + currentMax);
}
}
// deal with voting results // deal with voting results
int voteIndex = 0; int voteIndex = 0;
for (int i = 0; i < votesForPlayers.length; i++) { for (int i = 0; i < votesForPlayers.length; i++) {
if (votesForPlayers[i] == currentMax) { // if player has most votes if (votesForPlayers[i] == currentMax) { // if player has most votes
voteIndex = i; voteIndex = i;
LOGGER.info("Player " + voteIndex + " has the most votes");
} }
} }
LOGGER.info("Player " + voteIndex + " has the most votes");
if (!passengers[voteIndex] if (!passengers[voteIndex]
.getIsGhost()) { // if player with most votes is human, notify everyone about it .getIsGhost()) { // if player with most votes is human, notify everyone about it
for (Passenger passenger : passengers) { for (Passenger passenger : passengers) {
passenger.send( 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].getIsGhost()) { // if player is a ghost
if (passengers[voteIndex].getIsOG()) { // if ghost is OG --> end game, humans win 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 { } else {
/* Special case: if ghost is not OG and if only one human is left (--> last human didn't vote for OG ghost), /* Special case: if ghost is not OG and if only one human is left (--> last human didn't vote for OG ghost),
ghosts win. ghosts win.
@ -174,20 +178,23 @@ public class VoteHandler {
} }
} }
if (humans == 1) { 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 --> // Usual case: there is more than one human left and a normal ghost has been voted for -->
// kick this ghost off // kick this ghost off
passengers[voteIndex].setKickedOff(true); passengers[voteIndex].setKickedOff(true);
for (Passenger passenger : passengers) { 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 // set hasVoted to false for all passengers for future voting
for (Passenger passenger : passengers) { for (Passenger passenger : passengers) {
passenger.setHasVoted(false); passenger.setHasVoted(false);
} }
return "";
} }
/** /**
@ -216,38 +223,34 @@ public class VoteHandler {
} }
public static void main(String[] args) { /**
try { * Collecting the votes - distribute them among the vote counters for all players. Note: each voting collects
Game game = new Game(6,1, 6); * votes for all players even though some might not be concerned (i.e. ghosts during ghost vote). Those players
VoteHandler voteHandler = new VoteHandler(); * will then get 0 votes so it dosen't matter. Returns the max amount of votes a player received.
* @param passengers train passengers
Passenger[] testArray = game.gameFunctions.passengerTrain; * @param votesForPlayers array collecting the votes each player received during a voting
Passenger ghost = new Ghost(); * @param data deals with Client votes
testArray[3] = ghost; * @param game current game instance
testArray[3].setGhost(); */
testArray[3].setIsOg(); int voteEvaluation(Passenger[] passengers, int[] votesForPlayers, ClientVoteData data, Game game) {
testArray[3].setPosition(3); for (Passenger passenger : passengers) {
print(testArray); passenger.getVoteFromGameState(data, game);
LOGGER.info("NIGHT"); if (passenger.getHasVoted()) {
voteHandler.ghostVote(testArray,game); for (int i = 0; i < votesForPlayers.length; i++) {
print(testArray); if (passenger.getVote() == i) {
votesForPlayers[i]++;
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());
} }
/* 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; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.LogManager;
import org.apache.logging.log4j.Logger; 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 Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER); public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
public boolean getIsOG() { public boolean getIsOG() {
return isOG; return isOG;
} }

View File

@ -1,6 +1,10 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -28,4 +32,43 @@ public class GhostNPC extends Ghost {
this.name = name; 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; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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 ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -12,8 +15,7 @@ public class GhostPlayer extends Ghost {
/** /**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a * Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost. * ghost.
* * @param position position on the train
* @param position position on the train
* @param name name. if null, then a default name is used. * @param name name. if null, then a default name is used.
* @param isOG true if the ghost is the original ghost. * @param isOG true if the ghost is the original ghost.
*/ */
@ -31,8 +33,36 @@ public class GhostPlayer extends Ghost {
} }
} }
/**
public void send(String msg) { * Sends a message to the client handled bye this client handler
//todo(Jonas): pass message along to client. * 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);
} }
/**
* 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; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.LogManager;
import org.apache.logging.log4j.Logger; 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 Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER); public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
} }

View File

@ -1,10 +1,13 @@
package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
public class HumanNPC extends Human { public class HumanNPC extends Human {
public static final Logger LOGGER = LogManager.getLogger(); public static final Logger LOGGER = LogManager.getLogger();
public static final BudaLogConfig l = new BudaLogConfig(LOGGER); public static final BudaLogConfig l = new BudaLogConfig(LOGGER);
@ -26,4 +29,27 @@ public class HumanNPC extends Human {
this.name = name; 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; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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 ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import java.util.Arrays;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -12,8 +16,7 @@ public class HumanPlayer extends Human {
/** /**
* Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a * Creates a new GhostPlayer. Should be used at game start or if a HumanPlayer is turned into a
* ghost. * ghost.
* * @param position position on the train
* @param position position on the train
* @param name name. if null, then a default name is used. * @param name name. if null, then a default name is used.
*/ */
public HumanPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) { public HumanPlayer(int position, String name, ClientHandler clientHandler, boolean isOG) {
@ -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; package ch.unibas.dmi.dbis.cs108.gamelogic.klassenstruktur;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.gamelogic.ServerGameInfoHandler;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler; import ch.unibas.dmi.dbis.cs108.multiplayer.server.ClientHandler;
import org.apache.logging.log4j.LogManager; 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 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 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) 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 * sets the Position of this passenger
@ -82,6 +59,10 @@ public class Passenger {
hasVoted = voted; hasVoted = voted;
} }
public void setVote(int vote) {
this.vote = vote;
}
public void setIsOg() { public void setIsOg() {
isOG = true; isOG = true;
} }
@ -116,4 +97,24 @@ public class Passenger {
public ClientHandler getClientHandler() { public ClientHandler getClientHandler() {
return clientHandler; 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.helpers.Protocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.server.JServerProtocolParser;
import java.net.Socket; import java.net.Socket;
import java.io.*; import java.io.*;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Objects; import java.util.Objects;
import java.util.Scanner; import java.util.Scanner;
import java.util.Timer;
import java.util.TimerTask;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -23,6 +26,11 @@ public class Client {
private BufferedWriter out; private BufferedWriter out;
public ClientPinger clientPinger; 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) { public Client(Socket socket) {
try { try {
this.socket = socket; this.socket = socket;
@ -58,7 +66,7 @@ public class Client {
try { try {
if (bfr.ready()) { if (bfr.ready()) {
String msg = bfr.readLine(); String msg = bfr.readLine();
String formattedMSG = MessageFormatter.formatMsg(msg); String formattedMSG = MessageFormatter.formatMsg(msg, position);
sendMsgToServer(formattedMSG); sendMsgToServer(formattedMSG);
} }
Thread.sleep(5); 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 * Starts a thread which listens for incoming chat messages / other messages that the user
* has to see * has to see
@ -92,7 +124,7 @@ public class Client {
} else { System.out.println("chatMsg is null"); throw new IOException();} } else { System.out.println("chatMsg is null"); throw new IOException();}
} catch (IOException e) { } catch (IOException e) {
//e.printStackTrace(); //e.printStackTrace();
LOGGER.debug("Exception while trying to read message: " + e.getMessage()); LOGGER.warn("Exception while trying to read message: " + e.getMessage());
disconnectFromServer(); disconnectFromServer();
} }

View File

@ -43,11 +43,15 @@ public class JClientProtocolParser {
c.disconnectFromServer(); c.disconnectFromServer();
break; break;
case Protocol.serverRequestsGhostVote: 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 //TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input
break; break;
case Protocol.serverRequestsHumanVote: 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 //TODO(Seraina): How can be enforced, that clients won't vote otherwise? Trigger a methode here that listens to input
break; break;
default: default:

View File

@ -18,7 +18,7 @@ public class MessageFormatter {
* @return the reformatted message in the form HEADR$msg * @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 String header = ""; //header is first two characters
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
String s = ""; // just a friendly helper to save message in String s = ""; // just a friendly helper to save message in
@ -59,10 +59,20 @@ public class MessageFormatter {
stringBuilder.append(Protocol.listLobbies + "$"); stringBuilder.append(Protocol.listLobbies + "$");
s = ""; //Command has no parameters s = ""; //Command has no parameters
break; 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: default:
s = msg; s = msg;
} }
stringBuilder.append(s); stringBuilder.append(s);
LOGGER.debug(stringBuilder.toString());
return stringBuilder.toString(); return stringBuilder.toString();
} }

View File

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

View File

@ -92,6 +92,16 @@ public class Protocol {
*/ */
public static final String listLobbies = "LISTL"; 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 //SERVER TO CLIENT COMMANDS
@ -117,7 +127,8 @@ public class Protocol {
public static final String serverConfirmQuit = "QUITC"; 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"; public static final String serverRequestsGhostVote = "GVOTR";
@ -128,4 +139,6 @@ public class Protocol {
public static final String serverRequestsHumanVote = "HVOTR"; public static final String serverRequestsHumanVote = "HVOTR";
} }

View File

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

View File

@ -37,6 +37,7 @@ public class ServerPinger implements Runnable {
@Override @Override
public void run() { public void run() {
Thread.currentThread().setPriority(10);
try { try {
Thread.sleep(2000); Thread.sleep(2000);
while (socket.isConnected() && !socket.isClosed()) { while (socket.isConnected() && !socket.isClosed()) {
@ -55,9 +56,8 @@ public class ServerPinger implements Runnable {
System.out.println( System.out.println(
"Lost connection to user " + c.getClientUserName() + ". Waiting to reconnect..."); "Lost connection to user " + c.getClientUserName() + ". Waiting to reconnect...");
} else { } else {
c.disconnectClient(); //c.disconnectClient(); TODO: is that ever necessary?
LOGGER.debug( //LOGGER.debug("gotPingBack has not been set to true and isConnected has been set to false before");
"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; package ch.unibas.dmi.dbis.cs108.multiplayer.server;
import ch.unibas.dmi.dbis.cs108.BudaLogConfig; 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.Protocol;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger; import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.ServerPinger;
import java.io.*; import java.io.*;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.Socket; import java.net.Socket;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.Scanner; import java.util.Scanner;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -21,6 +25,7 @@ public class ClientHandler implements Runnable {
private Socket socket; private Socket socket;
private InetAddress ip; private InetAddress ip;
/** /**
* notes if the client has formally logged in yet. If connecting through the normal Client class, * 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 * the client is logged in automatically, if connecting though some external application, the
@ -70,6 +75,10 @@ public class ClientHandler implements Runnable {
return socket; 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() { public static HashSet<ClientHandler> getConnectedClients() {
return connectedClients; return connectedClients;
} }
@ -86,14 +95,14 @@ public class ClientHandler implements Runnable {
return loggedIn; return loggedIn;
} }
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
//Setters:
public String getClientUserName() { public String getClientUserName() {
return clientUserName; return clientUserName;
} }
//Setters:
public void setLoggedIn(boolean loggedIn) {
this.loggedIn = loggedIn;
}
@Override @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 * 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 * 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 * 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). * 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.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.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import ch.unibas.dmi.dbis.cs108.multiplayer.helpers.Protocol; 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 String header = ""; //"header" is the first 5 characters, i.e. the protocol part
try { try {
header = msg.substring(0, 5); 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) { } catch (IndexOutOfBoundsException e) {
System.out.println("Received unknown command"); System.out.println("Received unknown command");
} }
@ -65,7 +73,15 @@ public class JServerProtocolParser {
break; break;
case Protocol.listLobbies: case Protocol.listLobbies:
//TODO: add action //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; break;
default: default:
System.out.println("Received unknown command"); System.out.println("Received unknown command");