PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/hexx-network/src/main/java/de/bitrain/hexx/network/logic/GameClient.java

https://gitlab.com/hexx/hexx
Java | 345 lines | 174 code | 58 blank | 113 comment | 9 complexity | 6745ee51c879b58f786fcd94d7a9fe56 MD5 | raw file
  1. package de.bitrain.hexx.network.logic;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import java.util.List;
  5. import java.util.Observable;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import com.esotericsoftware.kryonet.Client;
  9. import com.esotericsoftware.kryonet.Connection;
  10. import de.bitrain.hexx.core.logic.Player;
  11. import de.bitrain.hexx.core.logic.SessionManager;
  12. import de.bitrain.hexx.core.logic.WeakObserverReference;
  13. import de.bitrain.hexx.core.logic.board.Position;
  14. import de.bitrain.hexx.core.logic.game.Game;
  15. import de.bitrain.hexx.core.logic.game.GameState;
  16. import de.bitrain.hexx.core.logic.game.interfaces.GameI;
  17. import de.bitrain.hexx.network.logic.match_making.MatchMakingStatus;
  18. import de.bitrain.hexx.network.logic.message.Message;
  19. import de.bitrain.hexx.network.logic.message.MessageListener;
  20. import de.bitrain.hexx.network.logic.message.types.GameStartMessage;
  21. import de.bitrain.hexx.network.logic.message.types.JoinRequest;
  22. import de.bitrain.hexx.network.logic.message.types.MatchMakingRequest;
  23. import de.bitrain.hexx.network.logic.message.types.MatchMakingResponse;
  24. import de.bitrain.hexx.network.logic.message.types.MoveMessage;
  25. import de.bitrain.hexx.network.logic.message.types.RoomCreateRequest;
  26. import de.bitrain.hexx.network.logic.message.types.RoomCreateResponse;
  27. import de.bitrain.hexx.network.logic.message.types.RoomListRequest;
  28. import de.bitrain.hexx.network.logic.message.types.RoomListResponse;
  29. import de.bitrain.hexx.network.logic.room.RoomInfo;
  30. /**
  31. * Represents a game client which can connect to a remote game server.
  32. */
  33. public class GameClient extends ObservableGameManager {
  34. private final static Logger logger = LoggerFactory.getLogger(GameClient.class);
  35. /**
  36. * Underlying kryo client responsible for communicating with the server.
  37. */
  38. private final Client kryoClient;
  39. /**
  40. * RemoteClient representing the server.
  41. */
  42. private RemoteClient server;
  43. /**
  44. * {@link RoomInfo} of the room the client is currently connected or
  45. * <code>null</code> if the client is not connected toany room at the moment.
  46. */
  47. private RoomInfo roomInfo;
  48. /**
  49. * Initializes the client.
  50. */
  51. public GameClient() {
  52. // Create client and add listener
  53. this.kryoClient = new Client();
  54. this.kryoClient.addListener(new InnerListener().setMessageProcessor(this));
  55. Message.registerClasses(this.kryoClient.getKryo());
  56. }
  57. /**
  58. * Tries to connect to the server.
  59. *
  60. * @return Returns true upon establishment of the connection or false on error or after the given
  61. * timeout.
  62. */
  63. public boolean connectToServer(final InetSocketAddress socketAddress,
  64. final int timeout) {
  65. try {
  66. // Start the client and connect to the server
  67. this.kryoClient.start();
  68. this.kryoClient.connect(timeout, socketAddress.getAddress(),
  69. socketAddress.getPort());
  70. }
  71. // If an error occurred, log it and shut the client down again
  72. catch (final IOException e) {
  73. logger.error("Error connecting to the server {}, exception: {}",
  74. socketAddress.toString(), e.getMessage());
  75. this.disconnect();
  76. return false;
  77. }
  78. // Create the remote client representing our connection to the server
  79. this.server = new RemoteClient(this.kryoClient, new Player(-1));
  80. logger.debug("Successfully connected to server {}",
  81. socketAddress.toString());
  82. return true;
  83. }
  84. /**
  85. * Tries to create a room on the connected server.
  86. */
  87. public boolean createRoom(final RoomInfo roomInfo, final GameI game) {
  88. // Abort if we are not connected
  89. // TODO throw exception?
  90. if (!this.isConnected()) {
  91. return false;
  92. }
  93. // Create the room request message and append the game state and room info
  94. final Message roomCreateRequest = new RoomCreateRequest();
  95. roomCreateRequest.append(roomInfo);
  96. roomCreateRequest.append(game.getGameState());
  97. // Send the message to the server
  98. this.server.sendMessage(roomCreateRequest);
  99. return true;
  100. }
  101. /**
  102. * Sends a match-making request to the connected server.
  103. */
  104. public boolean requestMatch() {
  105. // TODO throw exception?
  106. if (!this.isConnected()) {
  107. return false;
  108. }
  109. final Message matchMakingRequest = new MatchMakingRequest();
  110. matchMakingRequest.append(this.getLocalPlayer().getName());
  111. this.server.sendMessage(matchMakingRequest);
  112. return true;
  113. }
  114. /**
  115. * Disconnects the client from the server.
  116. */
  117. public void disconnect() {
  118. this.kryoClient.stop();
  119. }
  120. @Override
  121. public NetworkGame getGame() {
  122. return (NetworkGame) super.getGame();
  123. }
  124. /**
  125. * Returns the kryo client for this game client.
  126. */
  127. public Client getKryoClient() {
  128. return this.kryoClient;
  129. }
  130. /**
  131. * Returns the local player associated with this client.
  132. */
  133. public Player getLocalPlayer() {
  134. return this.server.getPlayer();
  135. }
  136. /**
  137. * Returns the {@link RoomInfo} of the room the client is currently connected
  138. * to, or <code>null</code> if the client is not connected to any room.
  139. */
  140. public RoomInfo getRoomInfo() {
  141. return this.roomInfo;
  142. }
  143. /**
  144. * Returns the server or null, if the client is not connected.
  145. */
  146. public RemoteClient getServer() {
  147. return this.server;
  148. }
  149. /**
  150. * Proxy to {@link Client#isConnected()}
  151. */
  152. public boolean isConnected() {
  153. return this.kryoClient.isConnected();
  154. }
  155. /**
  156. * Tries to join a room, announcing the given player name to the server.
  157. *
  158. * @param roomId id of the room to be joined
  159. */
  160. private void joinRoom(final Integer roomId) {
  161. // Abort if there's no connection to a server
  162. if (!this.isConnected()) {
  163. return;
  164. }
  165. // Send our local player to the server
  166. this.server.sendMessage(new JoinRequest()
  167. .append(this.server.getPlayer()).append(roomId));
  168. }
  169. /**
  170. * Tries to join the room with the given {@link RoomInfo}.
  171. *
  172. * @see #joinRoom(Integer)
  173. */
  174. public void joinRoom(final RoomInfo roomInfo) {
  175. this.roomInfo = roomInfo;
  176. this.joinRoom(roomInfo.getId());
  177. }
  178. /**
  179. * Processes GameStartMessages.
  180. */
  181. public void processMessage(final GameStartMessage message)
  182. throws Exception {
  183. final List<Object> contents = message.getContents();
  184. // Restore the game from the given game state
  185. final Game game = ((GameState) contents.get(0)).restoreAll();
  186. // Now take the player names from the second part of the contents
  187. final List<?> playersNamed = (List<?>) contents.get(1);
  188. final List<Player> playersUnnamed = game.getActivePlayers();
  189. Player playerNamed, playerUnnamed;
  190. // Walk though all players and carry the names over
  191. for (int i = 0; i < playersNamed.size(); i++) {
  192. logger.debug("Player {}: {}", i, playersNamed.get(i).toString());
  193. playerNamed = (Player) playersNamed.get(i);
  194. playerUnnamed = playersUnnamed.get(i);
  195. playerUnnamed.setName(playerNamed.getName());
  196. // If the current player is our local player, save it
  197. if (playerUnnamed.getName().equals(this.server.getPlayer().getName())) {
  198. logger.debug("Local player {}: {}", this.toString(),
  199. playerUnnamed.toString());
  200. this.server.setPlayer(playerUnnamed);
  201. }
  202. }
  203. // Store the game
  204. this.setGame(new NetworkGame(game, this.getLocalPlayer()));
  205. // Also store it in the session manager
  206. //SessionManager.getInstance().setCurrentGame(this.getGame());
  207. SessionManager.getInstance().setCurrentGame(game);
  208. // Register as listener with the game
  209. this.getGame().addObserver(new WeakObserverReference(this));
  210. // Notify observers that something changed
  211. super.processMessage(message);
  212. }
  213. /**
  214. * Processes MoveMessages from the server.
  215. */
  216. public void processMessage(final MoveMessage message) throws Exception {
  217. // TODO we need to catch IllegalMoveExceptions
  218. this.getGame().getGame()
  219. .tryMoveOnPosition((Position) message.getContent(0));
  220. // TODO move somewhere else?
  221. if (this.getGame().getGame().isOver()) {
  222. this.disconnect();
  223. }
  224. super.processMessage(message);
  225. }
  226. /**
  227. * Processes room creation responses and joins the room.
  228. *
  229. * TODO room creation failure?
  230. */
  231. public void processMessage(final RoomCreateResponse message) throws Exception {
  232. this.joinRoom((RoomInfo) message.getContent(0));
  233. }
  234. /**
  235. * Processes match-making responses.
  236. *
  237. * <p>Joins the provided room if a match was found.</p>
  238. */
  239. public void processMessage(final MatchMakingResponse message) throws Exception {
  240. if (message.getContent(0).equals(MatchMakingStatus.MATCH_FOUND)) {
  241. this.joinRoom((RoomInfo) message.getContent(1));
  242. }
  243. }
  244. /**
  245. * Processes incoming room lists and notifies observers.
  246. */
  247. public void processMessage(final RoomListResponse message) throws Exception {
  248. this.setChanged();
  249. this.notifyObservers(message);
  250. }
  251. /**
  252. * Sends a RoomListRequest to the server.
  253. */
  254. public void refreshRoomList() {
  255. this.server.sendMessage(new RoomListRequest());
  256. }
  257. /**
  258. * Sets the name of the local player.
  259. */
  260. public boolean setLocalPlayerName(final String name) {
  261. // Abort if not connected
  262. if (!this.isConnected()) {
  263. return false;
  264. }
  265. this.server.getPlayer().setName(name);
  266. return true;
  267. }
  268. /**
  269. * Sends local moves to the server.
  270. */
  271. @Override
  272. public void update(final Observable o, final Object position) {
  273. if (position instanceof Position) {
  274. this.server.sendMessage(new MoveMessage().append(position));
  275. }
  276. }
  277. /**
  278. * This listener monitors the kryo-server for incoming messages and processes
  279. * them.
  280. */
  281. private class InnerListener extends MessageListener {
  282. @Override
  283. public void disconnected(final Connection connection) {
  284. // TODO perform cleanup if needed?
  285. }
  286. }
  287. }