Coverage Summary for Class: Controller (it.polimi.ingsw.controller)

Class Method, % Line, %
Controller 100% (11/ 11) 100% (110/ 110)
Controller$1 100% (1/ 1) 100% (2/ 2)
total 100% (12/ 12) 100% (112/ 112)


1 package it.polimi.ingsw.controller; 2  3 import com.google.gson.Gson; 4 import it.polimi.ingsw.model.*; 5 import it.polimi.ingsw.utils.Observable; 6 import it.polimi.ingsw.utils.Observer; 7 import it.polimi.ingsw.utils.model.Command; 8 import it.polimi.ingsw.utils.model.Notification; 9  10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.stream.Collectors; 13  14 /** 15  * Controller for Game 16  */ 17 public class Controller extends Observable<String> implements Observer<Notification> { 18  /** 19  * Reference to Game for this Controller 20  */ 21  private final Game game; 22  23  /** 24  * Current Client State, this is used to calculate the diff information to send 25  */ 26  private ArrayList<String> prevReport = new ArrayList<>(); 27  28  /** 29  * Create an instance of Controller that manage the Game 30  * 31  * @param game the reference to game 32  */ 33  public Controller(Game game) { 34  if (game == null) 35  throw new NullPointerException(); 36  this.game = game; 37  } 38  39  /** 40  * On Notification parse the message and verify if it is a valid data 41  * 42  */ 43  @Override 44  public void update(Notification notification) { 45  try { 46  Command command = new Gson().fromJson(notification.getMessage(), Command.class); 47  if (command == null) 48  return; 49  filter(notification.getUsername(), command.getFuncName(), command.getFuncData()); 50  } catch (Exception e) { 51  // Just fail to parse 52  } 53  } 54  55  /** 56  * Function to notify all client about current Game State and to start Game 57  */ 58  public void startGame() { 59  createReport(new ArrayList<>()); 60  } 61  62  /** 63  * This function is used to filter requests, if the Game State is changed all 64  * client will be notified with the new State through a createReport() function 65  * 66  * @param username player's username 67  * @param functionName function's name to use 68  * @param data used for the function 69  */ 70  private synchronized void filter(String username, String functionName, String data) { 71  72  ArrayList<Command> report = new ArrayList<>(); 73  FuncCommand targetFunction = FuncCommand.getFromValue(functionName); 74  // If is a Quit Command 75  if (targetFunction == FuncCommand.QUIT_PLAYER && game.getPlayerList().stream() 76  .anyMatch(e -> e.getUsername().equals(username) && e.getStatusPlayer() != StatusPlayer.LOSE)) { 77  78  game.quitPlayer(); 79  // Update Client Game End, a non lose player quit the game 80  createReport(report); 81  return; 82  } 83  // Filter 84  if (!game.getCurrentPlayer().equals(username)) 85  return; 86  // Parse and Run Command 87  splitter(targetFunction, data); 88  89  // Add Option to End Turn 90  if (game.getPhase() == GamePhase.CHOOSE_ACTION && game.canEndTurn()) 91  report.add(new Command(TypeCommand.ACTION.getValue(), FuncCommand.CHOOSE_ACTION.getValue(), null, null)); 92  93  // Update Client with new game state 94  createReport(report); 95  } 96  97  /** 98  * This function is used to parse and check input data from user, and then run 99  * the command 100  * 101  * @param command Function to Launch 102  * @param data Data used in the Function 103  */ 104  private void splitter(FuncCommand command, String data) { 105  106  GamePhase phase = game.getPhase(); 107  switch (command) { 108  case CHOOSE_ACTION: { 109  int[] position = null; 110  if (data != null) 111  position = new Gson().fromJson(data, int[].class); 112  113  if ((phase == GamePhase.CHOOSE_WORKER || phase == GamePhase.PENDING 114  || phase == GamePhase.CHOOSE_ACTION)) { 115  game.chooseAction(position); 116  } 117  } 118  break; 119  case SET_GOD: 120  if (phase == GamePhase.CHOOSE_GOD && game.getGodList().contains(God.strConverter(data))) 121  game.setGod(God.strConverter(data)); 122  break; 123  case SET_WORKERS: { 124  int position = Integer.parseInt(data); 125  if (phase == GamePhase.SET_WORKERS && position < 25 && position >= 0) 126  game.setWorkers(position); 127  128  } 129  break; 130  case CHOOSE_WORKER: { 131  int position = Integer.parseInt(data); 132  if ((phase == GamePhase.CHOOSE_WORKER || phase == GamePhase.PENDING) && position >= 0 && position < 25) 133  game.chooseWorker(Integer.parseInt(data)); 134  135  } 136  break; 137  case SET_COLOR: 138  if (phase == GamePhase.SET_COLOR) 139  game.setColor(Color.strConverter(data)); 140  break; 141  case SET_GOD_LIST: { 142  God god = God.strConverter(data); 143  if (god != null && phase == GamePhase.SET_GOD_LIST 144  && game.getGodList().size() < game.mode.getPlayersNum() && !game.getGodList().contains(god)) { 145  game.setGodList(god); 146  } 147  } 148  break; 149  case SET_START_PLAYER: 150  if (phase == GamePhase.START_PLAYER 151  && game.getPlayerList().stream().anyMatch(e -> e.getUsername().equals(data))) 152  game.choosePlayer(data); 153  break; 154  default: 155  break; 156  } 157  } 158  159  /** 160  * Create and Send to Client the current Game State 161  * 162  * @param report initial report state 163  * @return Game State as ArrayList<Command> converted into a Json via Gson 164  */ 165  private void createReport(ArrayList<Command> report) { 166  GamePhase phase = game.getPhase(); 167  CommandConverter cc = new CommandConverter(); 168  // Prepare report to send 169  report.add(new Command(TypeCommand.CURRENT_PLAYER.getValue(), game.getCurrentPlayer())); 170  report.add(new Command(TypeCommand.GAME_PHASE.getValue(), phase.toString())); 171  report.add(new Command(TypeCommand.GAME_MODE.getValue(), game.mode.toString())); 172  report.addAll(infoOnPhase(phase)); 173  report.addAll(cc.reportBoard(phase, game.getBoard(), game.getCurrentPlayer())); 174  report.addAll(cc.reportAction(phase, game.getActions())); 175  report.addAll(cc.reportPlayer(phase, game.getPlayerList())); 176  177  // Convert to array of string for diff state 178  ArrayList<String> newReport = (ArrayList<String>) report.stream().map(e -> new Gson().toJson(e)) 179  .collect(Collectors.toList()); 180  181  // Diff from current client state with prev state 182  ArrayList<Command> toRes = new ArrayList<>(); 183  toRes.addAll(prevReport.stream().filter(e -> !newReport.contains(e)) 184  .map(e -> new Gson().fromJson(e, Command.class)).map(e -> { 185  e.setStatus(false); 186  return e; 187  }).collect(Collectors.toList())); 188  toRes.addAll(newReport.stream().filter(e -> !prevReport.contains(e)) 189  .map(e -> new Gson().fromJson(e, Command.class)).map(e -> { 190  e.setStatus(true); 191  return e; 192  }).collect(Collectors.toList())); 193  194  // Update prev state 195  prevReport = (ArrayList<String>) newReport.stream() 196  .filter(e -> !(new Gson().fromJson(e, Command.class).getType().equals("action"))) 197  .collect(Collectors.toList()); 198  199  // Prepare data to send 200  String toSendAll = new Gson() 201  .toJson(toRes.stream().filter(e -> !e.getType().equals("action")).collect(Collectors.toList())); 202  String toSendCurrentPlayer = new Gson().toJson(toRes); 203  204  // Update all client not current player state 205  notify((ArrayList<String>) game.getPlayerList().stream().map(e -> e.getUsername()) 206  .filter(e -> !e.equals(game.getCurrentPlayer())).collect(Collectors.toList()), toSendAll); 207  // Update current player state 208  notify(new ArrayList<>(Arrays.asList(game.getCurrentPlayer())), toSendCurrentPlayer); 209  } 210  211  /** 212  * Create an ArrayList of setup Command from current GamePhase 213  * 214  * @param phase Current Game Phase 215  * @return if there is any data to be added based on Current Phase will be 216  * returned, otherwise an empty arraylist will be returned 217  */ 218  private ArrayList<Command> infoOnPhase(GamePhase phase) { 219  ArrayList<Command> report = new ArrayList<>(); 220  switch (phase) { 221  case SET_COLOR: { 222  report.addAll(game 223  .getColors().stream().map(e -> new Command(TypeCommand.COLOR.getValue(), 224  FuncCommand.SET_COLOR.getValue(), e.toString(), e.toString())) 225  .collect(Collectors.toList())); 226  227  } 228  break; 229  case SET_GOD_LIST: { 230  ArrayList<God> godList = game.getGodList(); 231  report.addAll(Arrays.stream(God.values()).filter(e -> e != God.STANDARD && !godList.contains(e)) 232  .map(e -> new Command(TypeCommand.GOD.getValue(), FuncCommand.SET_GOD_LIST.getValue(), 233  e.toString(), e.toString())) 234  .collect(Collectors.toList())); 235  report.addAll(godList.stream().map(e -> new Command(TypeCommand.GOD_LIST.getValue(), e.toString())) 236  .collect(Collectors.toList())); 237  238  } 239  break; 240  case CHOOSE_GOD: { 241  ArrayList<God> godList = game.getGodList(); 242  report.addAll(godList.stream().map(e -> new Command(TypeCommand.GOD_LIST.getValue(), 243  FuncCommand.SET_GOD.getValue(), e.toString(), e.toString())).collect(Collectors.toList())); 244  245  } 246  break; 247  default: 248  break; 249  } 250  return report; 251  } 252 }