Explorar o código

Fixes for server shutdown logic, implemented connection aborting for
local server

Ivan Savenko hai 1 ano
pai
achega
0fc0ad238b
Modificáronse 4 ficheiros con 71 adicións e 41 borrados
  1. 40 7
      client/CServerHandler.cpp
  2. 1 0
      client/CServerHandler.h
  3. 18 26
      client/ServerRunner.cpp
  4. 12 8
      client/ServerRunner.h

+ 40 - 7
client/CServerHandler.cpp

@@ -177,6 +177,7 @@ INetworkHandler & CServerHandler::getNetworkHandler()
 
 void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
 {
+	logNetwork->trace("\tLocal server startup has been requested");
 #ifdef VCMI_MOBILE
 	// mobile apps can't spawn separate processes - only thread mode is available
 	serverRunner.reset(new ServerThreadRunner());
@@ -187,10 +188,11 @@ void CServerHandler::startLocalServerAndConnect(bool connectToLobby)
 		serverRunner.reset(new ServerThreadRunner());
 #endif
 
+	logNetwork->trace("\tStarting local server");
 	serverRunner->start(getLocalPort(), connectToLobby);
+	logNetwork->trace("\tConnecting to local server");
 	connectToServer(getLocalHostname(), getLocalPort());
-
-	logNetwork->trace("\tConnecting to the server: %d ms", th->getDiff());
+	logNetwork->trace("\tWaiting for connection");
 }
 
 void CServerHandler::connectToServer(const std::string & addr, const ui16 port)
@@ -238,6 +240,10 @@ void CServerHandler::onTimer()
 	if(getState() == EClientState::CONNECTION_CANCELLED)
 	{
 		logNetwork->info("Connection aborted by player!");
+		serverRunner->wait();
+		serverRunner.reset();
+		if (GH.windows().topWindow<CSimpleJoinScreen>() != nullptr)
+			GH.windows().popWindows(1);
 		return;
 	}
 
@@ -301,6 +307,9 @@ EClientState CServerHandler::getState() const
 
 void CServerHandler::setState(EClientState newState)
 {
+	if (newState == EClientState::CONNECTION_CANCELLED && serverRunner != nullptr)
+		serverRunner->shutdown();
+
 	state = newState;
 }
 
@@ -830,11 +839,7 @@ void CServerHandler::onPacketReceived(const std::shared_ptr<INetworkConnection>
 
 void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> & connection, const std::string & errorMessage)
 {
-	if (serverRunner)
-	{
-		serverRunner->wait();
-		serverRunner.reset();
-	}
+	waitForServerShutdown();
 
 	if(getState() == EClientState::DISCONNECTING)
 	{
@@ -865,6 +870,34 @@ void CServerHandler::onDisconnected(const std::shared_ptr<INetworkConnection> &
 	networkConnection.reset();
 }
 
+void CServerHandler::waitForServerShutdown()
+{
+	if (!serverRunner)
+		return; // may not exist for guest in MP
+
+	serverRunner->wait();
+	int exitCode = serverRunner->exitCode();
+	serverRunner.reset();
+
+	if (exitCode == 0)
+	{
+		logNetwork->info("Server closed correctly");
+	}
+	else
+	{
+		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
+		if (getState() == EClientState::CONNECTING)
+		{
+			showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
+			setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect
+		}
+		logNetwork->error("Error: server failed to close correctly or crashed!");
+		logNetwork->error("Check log file for more info");
+	}
+
+	serverRunner.reset();
+}
+
 void CServerHandler::visitForLobby(CPackForLobby & lobbyPack)
 {
 	if(applier->getApplier(CTypeList::getInstance().getTypeID(&lobbyPack))->applyOnLobbyHandler(this, lobbyPack))

+ 1 - 0
client/CServerHandler.h

@@ -111,6 +111,7 @@ class CServerHandler final : public IServerAPI, public LobbyInfo, public INetwor
 	std::atomic<EClientState> state;
 
 	void threadRunNetwork();
+	void waitForServerShutdown();
 
 	void sendLobbyPack(const CPackForLobby & pack) const override;
 

+ 18 - 26
client/ServerRunner.cpp

@@ -22,58 +22,50 @@
 
 ServerThreadRunner::ServerThreadRunner() = default;
 ServerThreadRunner::~ServerThreadRunner() = default;
-ServerProcessRunner::ServerProcessRunner() = default;
-ServerProcessRunner::~ServerProcessRunner() = default;
 
 void ServerThreadRunner::start(uint16_t port, bool connectToLobby)
 {
-	setThreadName("runServer");
-
 	server = std::make_unique<CVCMIServer>(port, connectToLobby, true);
 
 	threadRunLocalServer = boost::thread([this]{
+		setThreadName("runServer");
 		server->run();
 	});
 }
 
-void ServerThreadRunner::stop()
+void ServerThreadRunner::shutdown()
 {
 	server->setState(EServerState::SHUTDOWN);
 }
 
-int ServerThreadRunner::wait()
+void ServerThreadRunner::wait()
 {
 	threadRunLocalServer.join();
+}
+
+int ServerThreadRunner::exitCode()
+{
 	return 0;
 }
 
-void ServerProcessRunner::stop()
+#ifndef VCMI_MOBILE
+
+ServerProcessRunner::ServerProcessRunner() = default;
+ServerProcessRunner::~ServerProcessRunner() = default;
+
+void ServerProcessRunner::shutdown()
 {
 	child->terminate();
 }
 
-int ServerProcessRunner::wait()
+void ServerProcessRunner::wait()
 {
 	child->wait();
+}
 
+int ServerProcessRunner::exitCode()
+{
 	return child->exit_code();
-
-//	if (child->exit_code() == 0)
-//	{
-//		logNetwork->info("Server closed correctly");
-//	}
-//	else
-//	{
-//		boost::mutex::scoped_lock interfaceLock(GH.interfaceMutex);
-//
-//		if (getState() == EClientState::CONNECTING)
-//		{
-//			showServerError(CGI->generaltexth->translate("vcmi.server.errors.existingProcess"));
-//			setState(EClientState::CONNECTION_CANCELLED); // stop attempts to reconnect
-//		}
-//		logNetwork->error("Error: server failed to close correctly or crashed!");
-//		logNetwork->error("Check %s for more info", logName);
-//	}
 }
 
 void ServerProcessRunner::start(uint16_t port, bool connectToLobby)
@@ -93,4 +85,4 @@ void ServerProcessRunner::start(uint16_t port, bool connectToLobby)
 		throw std::runtime_error("Failed to start server! Reason: " + ec.message());
 }
 
-
+#endif

+ 12 - 8
client/ServerRunner.h

@@ -15,21 +15,23 @@ class IServerRunner
 {
 public:
 	virtual void start(uint16_t port, bool connectToLobby) = 0;
-	virtual void stop() = 0;
-	virtual int wait() = 0;
+	virtual void shutdown() = 0;
+	virtual void wait() = 0;
+	virtual int exitCode() = 0;
 
 	virtual ~IServerRunner() = default;
 };
 
-/// Server instance will run as a thread of client process
+/// Class that runs server instance as a thread of client process
 class ServerThreadRunner : public IServerRunner, boost::noncopyable
 {
 	std::unique_ptr<CVCMIServer> server;
 	boost::thread threadRunLocalServer;
 public:
 	void start(uint16_t port, bool connectToLobby) override;
-	void stop() override;
-	int wait() override;
+	void shutdown() override;
+	void wait() override;
+	int exitCode() override;
 
 	ServerThreadRunner();
 	~ServerThreadRunner();
@@ -41,15 +43,17 @@ namespace boost::process {
 class child;
 }
 
-/// Server instance will run as a separate process
+/// Class that runs server instance as a child process
+/// Available only on desktop systems where process management is allowed
 class ServerProcessRunner : public IServerRunner, boost::noncopyable
 {
 	std::unique_ptr<boost::process::child> child;
 
 public:
 	void start(uint16_t port, bool connectToLobby) override;
-	void stop() override;
-	int wait() override;
+	void shutdown() override;
+	void wait() override;
+	int exitCode() override;
 
 	ServerProcessRunner();
 	~ServerProcessRunner();