diff --git a/net-client/pom.xml b/net-client/pom.xml index 8d113af2..971e5d23 100644 --- a/net-client/pom.xml +++ b/net-client/pom.xml @@ -7,11 +7,11 @@ com.beverly.hills.money.gang daikombat-server - 5.0.0 + 5.0.1 net-client - 5.0.0 + 5.0.1 14 @@ -65,12 +65,12 @@ com.beverly.hills.money.gang schema - 5.0.0 + 5.0.1 com.beverly.hills.money.gang security - 5.0.0 + 5.0.1 diff --git a/net-client/src/main/java/com/beverly/hills/money/gang/network/GameConnection.java b/net-client/src/main/java/com/beverly/hills/money/gang/network/GameConnection.java index 26ad7155..ee1607df 100644 --- a/net-client/src/main/java/com/beverly/hills/money/gang/network/GameConnection.java +++ b/net-client/src/main/java/com/beverly/hills/money/gang/network/GameConnection.java @@ -42,6 +42,7 @@ import io.netty.util.concurrent.EventExecutorGroup; import java.io.IOException; import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -78,14 +79,17 @@ public class GameConnection { private final ServerHMACService hmacService; - private final Channel channel; + private final AtomicReference channelRef = new AtomicReference<>(); - private final EventLoopGroup group; + private final CountDownLatch connectedLatch = new CountDownLatch(1); + private final EventLoopGroup group; private final AtomicReference state = new AtomicReference<>(); - public GameConnection(final GameServerCreds gameServerCreds) throws IOException { + public GameConnection( + final GameServerCreds gameServerCreds, + final Runnable onConnected) throws IOException { this.hmacService = new ServerHMACService(gameServerCreds.getPassword()); LOG.info("Start connecting"); state.set(GameConnectionState.CONNECTING); @@ -162,27 +166,24 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } }); long startTime = System.currentTimeMillis(); - this.channel = bootstrap.connect( + bootstrap.connect( gameServerCreds.getHostPort().getHost(), - gameServerCreds.getHostPort().getPort()).sync().channel(); - LOG.info("Connected to server in {} mls. Fast TCP enabled: {}", - System.currentTimeMillis() - startTime, ClientConfig.FAST_TCP); - pingScheduler.scheduleAtFixedRate(() -> { - try { - if (hasPendingPing.get()) { - LOG.warn("Old ping request is still pending"); - return; - } - hasPendingPing.set(true); - pingRequestedTimeMls.set(System.currentTimeMillis()); - writeToChannel(PING); - } catch (Exception e) { - LOG.error("Can't ping server", e); - } - }, ClientConfig.SERVER_MAX_INACTIVE_MLS / 3, ClientConfig.SERVER_MAX_INACTIVE_MLS / 3, - TimeUnit.MILLISECONDS); + gameServerCreds.getHostPort().getPort()).addListener((ChannelFutureListener) future -> { + channelRef.set(future.channel()); + if (future.isSuccess()) { + LOG.info("Connected to server in {} mls. Fast TCP enabled: {}", + System.currentTimeMillis() - startTime, ClientConfig.FAST_TCP); + schedulePing(); + state.set(GameConnectionState.CONNECTED); + connectedLatch.countDown(); + onConnected.run(); + } else { + LOG.error("Error occurred", future.cause()); + errorsQueueAPI.push(future.cause()); + disconnect(); + } + }); - state.set(GameConnectionState.CONNECTED); } catch (Exception e) { LOG.error("Error occurred", e); disconnect(); @@ -190,6 +191,38 @@ public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exc } } + public GameConnection( + final GameServerCreds gameServerCreds) throws IOException { + this(gameServerCreds, () -> { + }); + } + + public boolean waitUntilConnected(int timeoutMls) throws InterruptedException { + return connectedLatch.await(timeoutMls, TimeUnit.MILLISECONDS); + } + + public boolean waitUntilConnected() throws InterruptedException { + return waitUntilConnected(60_000); + } + + private void schedulePing() { + pingScheduler.scheduleAtFixedRate(() -> { + try { + if (hasPendingPing.get()) { + LOG.warn("Old ping request is still pending"); + return; + } + hasPendingPing.set(true); + pingRequestedTimeMls.set(System.currentTimeMillis()); + writeToChannel(PING); + } catch (Exception e) { + LOG.error("Can't ping server", e); + } + }, ClientConfig.SERVER_MAX_INACTIVE_MLS / 3, + ClientConfig.SERVER_MAX_INACTIVE_MLS / 3, + TimeUnit.MILLISECONDS); + } + public void shutdownPingScheduler() { try { pingScheduler.shutdown(); @@ -245,14 +278,20 @@ private void writeLocal(GeneratedMessageV3 command) { } private void writeToChannel(ServerCommand serverCommand) { + if (state.get() != GameConnectionState.CONNECTED) { + LOG.warn("Can't write to closed channel"); + return; + } LOG.debug("Write {}", serverCommand); - channel.writeAndFlush(serverCommand).addListener((ChannelFutureListener) future -> { - if (!future.isSuccess()) { - errorsQueueAPI.push(new IOException("Failed to write command " + serverCommand)); - } + Optional.ofNullable(channelRef.get()).ifPresent(channel -> { + channel.writeAndFlush(serverCommand).addListener((ChannelFutureListener) future -> { + if (!future.isSuccess()) { + errorsQueueAPI.push(new IOException("Failed to write command " + serverCommand)); + } + }); + networkStats.incSentMessages(); + networkStats.addOutboundPayloadBytes(serverCommand.getSerializedSize()); }); - networkStats.incSentMessages(); - networkStats.addOutboundPayloadBytes(serverCommand.getSerializedSize()); } public void disconnect() { @@ -268,7 +307,7 @@ public void disconnect() { LOG.error("Can't shutdown bootstrap group", e); } try { - Optional.ofNullable(channel).ifPresent(ChannelOutboundInvoker::close); + Optional.ofNullable(channelRef.get()).ifPresent(ChannelOutboundInvoker::close); } catch (Exception e) { LOG.error("Can't close channel", e); } @@ -302,7 +341,6 @@ public NetworkStatsReader getNetworkStats() { return networkStats; } - private enum GameConnectionState { CONNECTING, CONNECTED, DISCONNECTING, DISCONNECTED } diff --git a/pom.xml b/pom.xml index c7af24b3..0057d895 100644 --- a/pom.xml +++ b/pom.xml @@ -12,7 +12,7 @@ com.beverly.hills.money.gang daikombat-server - 5.0.0 + 5.0.1 pom diff --git a/schema/pom.xml b/schema/pom.xml index 6ef6e5ae..7bf6d8b1 100644 --- a/schema/pom.xml +++ b/schema/pom.xml @@ -7,11 +7,11 @@ com.beverly.hills.money.gang daikombat-server - 5.0.0 + 5.0.1 schema - 5.0.0 + 5.0.1 14 diff --git a/security/pom.xml b/security/pom.xml index 1c565160..adef3c42 100644 --- a/security/pom.xml +++ b/security/pom.xml @@ -7,11 +7,11 @@ com.beverly.hills.money.gang daikombat-server - 5.0.0 + 5.0.1 security - 5.0.0 + 5.0.1 14 diff --git a/server/pom.xml b/server/pom.xml index a148233e..78581f26 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -7,11 +7,11 @@ com.beverly.hills.money.gang daikombat-server - 5.0.0 + 5.0.1 server - 5.0.0 + 5.0.1 14 @@ -78,12 +78,12 @@ com.beverly.hills.money.gang schema - 5.0.0 + 5.0.1 com.beverly.hills.money.gang security - 5.0.0 + 5.0.1 eu.rekawek.toxiproxy @@ -122,7 +122,7 @@ com.beverly.hills.money.gang net-client - 5.0.0 + 5.0.1 test diff --git a/server/src/test/java/com/beverly/hills/money/gang/it/AbstractGameServerTest.java b/server/src/test/java/com/beverly/hills/money/gang/it/AbstractGameServerTest.java index a28e0648..bf5e4210 100644 --- a/server/src/test/java/com/beverly/hills/money/gang/it/AbstractGameServerTest.java +++ b/server/src/test/java/com/beverly/hills/money/gang/it/AbstractGameServerTest.java @@ -117,6 +117,11 @@ protected GameConnection createGameConnection(String password, String host, int .hostPort(HostPort.builder().host(host).port(port).build()) .build()); gameConnections.add(gameConnection); + try { + gameConnection.waitUntilConnected(5_000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } return gameConnection; } } diff --git a/server/src/test/java/com/beverly/hills/money/gang/it/GameConnectionTest.java b/server/src/test/java/com/beverly/hills/money/gang/it/GameConnectionTest.java index 566e842d..82960b48 100644 --- a/server/src/test/java/com/beverly/hills/money/gang/it/GameConnectionTest.java +++ b/server/src/test/java/com/beverly/hills/money/gang/it/GameConnectionTest.java @@ -2,7 +2,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertTrue; import com.beverly.hills.money.gang.config.ServerConfig; @@ -11,6 +11,8 @@ import com.beverly.hills.money.gang.proto.JoinGameCommand; import com.beverly.hills.money.gang.proto.ServerResponse; import java.io.IOException; +import java.net.ConnectException; +import java.net.UnknownHostException; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.SetEnvironmentVariable; @@ -104,9 +106,9 @@ public void testDisconnectTwice() throws IOException, InterruptedException { * @then an exception is thrown */ @Test - public void testGetServerInfoNotExistingServer() { - assertThrows(IOException.class, - () -> createGameConnection(ServerConfig.PIN_CODE, "666.666.666.666", port)); + public void testGetServerInfoNotExistingServer() throws IOException { + var connection = createGameConnection(ServerConfig.PIN_CODE, "666.666.666.666", port); + assertInstanceOf(UnknownHostException.class, connection.getErrors().poll().get()); } /** @@ -115,8 +117,8 @@ public void testGetServerInfoNotExistingServer() { * @then an exception is thrown */ @Test - public void testGetServerInfoWrongPort() { - assertThrows(IOException.class, - () -> createGameConnection(ServerConfig.PIN_CODE, "localhost", 666)); + public void testGetServerInfoWrongPort() throws IOException { + var connection = createGameConnection(ServerConfig.PIN_CODE, "localhost", 666); + assertInstanceOf(ConnectException.class, connection.getErrors().poll().get()); } } diff --git a/server/src/test/java/com/beverly/hills/money/gang/it/JoinGameTest.java b/server/src/test/java/com/beverly/hills/money/gang/it/JoinGameTest.java index 1f247a61..531f35c4 100644 --- a/server/src/test/java/com/beverly/hills/money/gang/it/JoinGameTest.java +++ b/server/src/test/java/com/beverly/hills/money/gang/it/JoinGameTest.java @@ -112,7 +112,7 @@ public void testJoinGameAfterManyPlayersJoined() throws Exception { * @when a player connects to a server using wrong game id * @then the player is not connected */ - @Test + @RepeatedTest(16) public void testJoinGameNotExistingGame() throws IOException { int gameIdToConnectTo = 666; GameConnection gameConnection = createGameConnection(ServerConfig.PIN_CODE, "localhost", port);