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 }