- now exits cleanly with ^Q!

Mon, 15 Dec 2014 01:41:06 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Mon, 15 Dec 2014 01:41:06 +0200
changeset 27
089e37c0887e
parent 26
24c7b804c99f
child 28
3cc042af3090

- now exits cleanly with ^Q!
- the statusbar now actually features meaningful info (IP, num admins, current level) and is colored to match titlebar

sources/basics.h file | annotate | diff | comparison | revisions
sources/interface.cpp file | annotate | diff | comparison | revisions
sources/interface.h file | annotate | diff | comparison | revisions
sources/main.cpp file | annotate | diff | comparison | revisions
sources/network/rconsession.cpp file | annotate | diff | comparison | revisions
sources/network/rconsession.h file | annotate | diff | comparison | revisions
--- a/sources/basics.h	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/basics.h	Mon Dec 15 01:41:06 2014 +0200
@@ -63,9 +63,12 @@
 using Function = std::function<Signature>;
 
 FUNCTION print_to_console (const String& a) -> void;
+FUNCTION request_exit() -> void;
 
 template<typename T> inline FUNCTION
 clamp (T a, T b, T c) -> T
 {
 	return (a < b) ? b : (a > c) ? c : a;
 }
+
+struct Exitception {};
--- a/sources/interface.cpp	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/interface.cpp	Mon Dec 15 01:41:06 2014 +0200
@@ -50,13 +50,14 @@
 static bool g_needStatusBarRender = false;
 static bool g_needInputRender = false;
 static bool g_needOutputRender = false;
-static String g_statusBarText;
 static struct { char ch; int x; } g_cursorChar;
 static Vector<String> g_output = {""};
 static int g_outputScroll = 0;
 static String g_title;
 static InputState g_inputState = INPUTSTATE_NORMAL;
+static Function<void (void)> g_disconnectConfirmFunction = nullptr;
 static IPAddress g_address;
+static String g_statusBarText;
 
 // -------------------------------------------------------------------------------------------------
 //
@@ -86,6 +87,33 @@
 
 // -------------------------------------------------------------------------------------------------
 //
+static FUNCTION
+set_input_state (InputState newstate) -> void
+{
+	// Clear the input row (unless going to or from confirm state)
+	if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
+		and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
+	{
+		g_input.clear();
+	}
+
+	switch (newstate)
+	{
+	case INPUTSTATE_ADDRESS:
+		if (g_address.host != 0)
+			g_input = g_address.to_string (IP_WITH_PORT);
+		break;
+
+	default:
+		break;
+	}
+
+	g_inputState = newstate;
+	g_needInputRender = true;
+}
+
+// -------------------------------------------------------------------------------------------------
+//
 FUNCTION
 Interface::initialize() -> void
 {
@@ -111,13 +139,6 @@
 	g_needRefresh = false;
 	print ("Interface initialized.\n");
 }
-// -------------------------------------------------------------------------------------------------
-//
-static FUNCTION
-interface_sessions_width() -> int
-{
-	return COLS / 3;
-}
 
 // -------------------------------------------------------------------------------------------------
 //
@@ -151,6 +172,21 @@
 // -------------------------------------------------------------------------------------------------
 //
 static FUNCTION
+safe_disconnect (Function<void()> afterwards) -> void
+{
+	if (RCONSession::get_session() != nullptr
+		and RCONSession::get_session()->state() != RCON_DISCONNECTED)
+	{
+		g_disconnectConfirmFunction = afterwards;
+		set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION);
+	}
+	else
+		afterwards();
+}
+
+// -------------------------------------------------------------------------------------------------
+//
+static FUNCTION
 interface_render_output() -> void
 {
 	int height = LINES - 3;
@@ -233,20 +269,62 @@
 static FUNCTION
 interface_render_statusbar() -> void
 {
+	int color = interface_color_pair (WHITE, BLUE);
 	int y = LINES - 1;
+	attron (color);
 	mvhline (y, 0, ' ', COLS);
 	mvprintw (y, 0, "%s", g_statusBarText.chars());
+	attroff (color);
 	g_needRefresh = true;
 	g_needStatusBarRender = false;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-static FUNCTION
-set_statusbar_text (const String& a) -> void
+FUNCTION
+Interface::update_statusbar() -> void
 {
-	g_statusBarText = a;
-	g_needStatusBarRender = true;
+	String text;
+	RCONSession* session = RCONSession::get_session();
+
+	if (session == nullptr)
+	{
+		text = "[DISCONNECTED]";
+	}
+	else
+	{
+		switch (session->state())
+		{
+		case RCON_DISCONNECTED:
+			text = "[DISCONNECTED]";
+			break;
+
+		case RCON_CONNECTING:
+		case RCON_AUTHENTICATING:
+			text = "Connecting to " + session->address().to_string (IP_WITH_PORT) + "...";
+			break;
+
+		case RCON_CONNECTED:
+			{
+				String adminText;
+
+				if (session->num_admins() == 0)
+					adminText = "No other admins";
+				else
+					adminText = format ("%1 other admin%s1", session->num_admins());
+
+				text = format ("%1 | %2 | %3", session->address().to_string (IP_WITH_PORT),
+					session->level(), adminText);
+			}
+			break;
+		}
+	}
+
+	if (text != g_statusBarText)
+	{
+		g_statusBarText = text;
+		g_needStatusBarRender = true;
+	}
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -254,6 +332,7 @@
 FUNCTION
 Interface::render_full() -> void
 {
+	update_statusbar();
 	interface_render_titlebar();
 	interface_render_output();
 	interface_render_statusbar();
@@ -279,43 +358,15 @@
 
 // -------------------------------------------------------------------------------------------------
 //
-static FUNCTION
-set_input_state (InputState newstate) -> void
-{
-	// Clear the input row (unless going to or from confirm state)
-	if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
-		and g_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
-	{
-		g_input.clear();
-	}
-
-	switch (newstate)
-	{
-	case INPUTSTATE_ADDRESS:
-		if (g_address.host != 0)
-			g_input = g_address.to_string (IP_WITH_PORT);
-		break;
-
-	default:
-		break;
-	}
-
-	g_inputState = newstate;
-	g_needInputRender = true;
-}
-
-// -------------------------------------------------------------------------------------------------
-//
 FUNCTION
 Interface::handle_input() -> void
 {
 	int ch = ::getch();
-	set_statusbar_text (String::from_number (ch));
 
 	if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
 	{
 		if (ch == 'y' or ch == 'Y')
-			set_input_state (INPUTSTATE_ADDRESS);
+			g_disconnectConfirmFunction();
 		else if (ch == 'n' or ch == 'N')
 			set_input_state (INPUTSTATE_NORMAL);
 		return;
@@ -328,9 +379,34 @@
 	}
 	else switch (ch)
 	{
-	case KEY_F(1):
-		endwin();
-		exit (EXIT_SUCCESS);
+	case 'Q' - 'A' + 1: // ^Q
+		switch (g_inputState)
+		{
+		case INPUTSTATE_CONFIRM_DISCONNECTION:
+			break;
+
+		case INPUTSTATE_NORMAL:
+			safe_disconnect ([]()
+			{
+				endwin();
+				throw Exitception();
+			});
+			break;
+
+		case INPUTSTATE_PASSWORD:
+			set_input_state (INPUTSTATE_ADDRESS);
+			break;
+
+		case INPUTSTATE_ADDRESS:
+			set_input_state (INPUTSTATE_NORMAL);
+		}
+		break;
+
+	case '\e':
+		if (g_inputState == INPUTSTATE_PASSWORD)
+			set_input_state (INPUTSTATE_ADDRESS);
+		else if (g_inputState == INPUTSTATE_ADDRESS)
+			set_input_state (INPUTSTATE_NORMAL);
 		break;
 
 	case KEY_LEFT:
@@ -438,17 +514,7 @@
 
 	case 'N' - 'A' + 1: // ^N
 		if (g_inputState == INPUTSTATE_NORMAL)
-		{
-			if (RCONSession::get_session() != nullptr
-				and RCONSession::get_session()->state() != RCON_DISCONNECTED)
-			{
-				set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION);
-			}
-			else
-			{
-				set_input_state (INPUTSTATE_ADDRESS);
-			}
-		}
+			safe_disconnect ([]() {set_input_state (INPUTSTATE_ADDRESS);});
 		break;
 	}
 }
--- a/sources/interface.h	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/interface.h	Mon Dec 15 01:41:06 2014 +0200
@@ -38,4 +38,5 @@
 	FUNCTION render() -> void;
 	FUNCTION render_full() -> void;
 	FUNCTION set_title (const String& message) -> void;
+	FUNCTION update_statusbar() -> void;
 };
--- a/sources/main.cpp	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/main.cpp	Mon Dec 15 01:41:06 2014 +0200
@@ -35,6 +35,8 @@
 #include "huffman/huffman.h"
 #include "interface.h"
 
+static bool g_shouldExit = false;
+
 // -------------------------------------------------------------------------------------------------
 //
 FUNCTION
@@ -43,35 +45,53 @@
 	HUFFMAN_Construct();
 	Interface::initialize();
 
-	for (;;)
+	try
 	{
-		fd_set fdset;
-		int highest = 0;
-		timeval timeout;
-		timeout.tv_sec = 0;
-		timeout.tv_usec = 250000; // 0.25 seconds
-		FD_ZERO (&fdset);
-		FD_SET (0, &fdset);
-		RCONSession* session = RCONSession::get_session();
-
-		if (session)
+		for (;;)
 		{
-			int fd = session->socket()->file_descriptor();
-			highest = max (highest, fd);
-			FD_SET (fd, &fdset);
-		}
+			if (g_shouldExit)
+				break;
 
-		select (highest + 1, &fdset, nullptr, nullptr, &timeout);
+			fd_set fdset;
+			int highest = 0;
+			timeval timeout;
+			timeout.tv_sec = 0;
+			timeout.tv_usec = 250000; // 0.25 seconds
+			FD_ZERO (&fdset);
+			FD_SET (0, &fdset);
+			RCONSession* session = RCONSession::get_session();
 
-		if (FD_ISSET (0, &fdset))
-			// stdin is ready, what's incoming?
-			Interface::handle_input();
+			if (session)
+			{
+				int fd = session->socket()->file_descriptor();
+				highest = max (highest, fd);
+				FD_SET (fd, &fdset);
+			}
+
+			select (highest + 1, &fdset, nullptr, nullptr, &timeout);
 
-		if (session)
-			session->tick();
+			if (FD_ISSET (0, &fdset))
+				// stdin is ready, what's incoming?
+				Interface::handle_input();
+
+			if (session)
+				session->tick();
 
-		Interface::render();
+			Interface::render();
+		}
 	}
+	catch (const Exitception&) {}
+
+	if (RCONSession::get_session())
+		RCONSession::get_session()->disconnect();
 
 	return EXIT_SUCCESS;
 }
+
+// -------------------------------------------------------------------------------------------------
+//
+FUNCTION
+request_exit() -> void
+{
+	g_shouldExit = true;
+}
--- a/sources/network/rconsession.cpp	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/network/rconsession.cpp	Mon Dec 15 01:41:06 2014 +0200
@@ -7,7 +7,8 @@
 //
 RCONSession::RCONSession() :
 	m_state (RCON_DISCONNECTED),
-	m_lastPing (0)
+	m_lastPing (0),
+	m_numAdmins (0)
 {
 	if (g_rconSession != NULL)
 	{
@@ -40,6 +41,8 @@
 //
 RCONSession::~RCONSession()
 {
+	disconnect();
+
 	if (g_rconSession == this)
 		g_rconSession = nullptr;
 }
@@ -51,6 +54,7 @@
 {
 	m_address = address;
 	m_state = RCON_CONNECTING;
+	Interface::update_statusbar();
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -58,13 +62,14 @@
 METHOD
 RCONSession::disconnect() -> void
 {
-	if (m_state == RCON_CONNECTED)
+	if (m_state > RCON_CONNECTING)
 	{
 		// Say goodbye to remote
 		Bytestream packet;
 		packet.write_byte (CLRC_DISCONNECT);
 		this->send (packet);
 		print ("Disconnected from %1\n", m_address.to_string (IP_WITH_PORT));
+		Interface::update_statusbar();
 	}
 
 	m_state = RCON_DISCONNECTED;
@@ -202,11 +207,13 @@
 		break;
 
 	case SVRCU_ADMINCOUNT:
-		print ("Admin count: %d1\n", packet.read_byte());
+		m_numAdmins = packet.read_byte();
+		Interface::update_statusbar();
 		break;
 
 	case SVRCU_MAP:
-		print ("New level: %1\n", packet.read_string());
+		m_level = packet.read_string();
+		Interface::update_statusbar();
 		break;
 	}
 }
@@ -295,3 +302,27 @@
 {
 	return m_state;
 }
+
+// -------------------------------------------------------------------------------------------------
+//
+METHOD
+RCONSession::address() const -> const IPAddress&
+{
+	return m_address;
+}
+
+// -------------------------------------------------------------------------------------------------
+//
+METHOD
+RCONSession::num_admins() const -> int
+{
+	return m_numAdmins;
+}
+
+// -------------------------------------------------------------------------------------------------
+//
+METHOD
+RCONSession::level() const -> const String&
+{
+	return m_level;
+}
--- a/sources/network/rconsession.h	Sun Dec 14 23:41:00 2014 +0200
+++ b/sources/network/rconsession.h	Mon Dec 15 01:41:06 2014 +0200
@@ -90,10 +90,12 @@
 public:
 	~RCONSession();
 
+	METHOD address() const -> const IPAddress&;
 	METHOD connect (IPAddress address) -> void;
 	METHOD disconnect() -> void;
 	METHOD handle_packet (Bytestream& packet, const IPAddress& from) -> void;
 	METHOD process_server_updates (Bytestream& packet) -> void;
+	METHOD num_admins() const -> int;
 	METHOD send (const Bytestream& packet) -> void;
 	METHOD send_hello() -> void;
 	METHOD send_password() -> void;
@@ -103,6 +105,7 @@
 	METHOD bump_last_ping() -> void;
 	METHOD send_command (const String& message) -> bool;
 	METHOD state() const -> RCONSessionState;
+	METHOD level() const -> const String&;
 
 	static METHOD new_session() -> RCONSession*;
 	static METHOD get_session() -> RCONSession*;
@@ -118,4 +121,6 @@
 	String m_salt;
 	int m_serverProtocol;
 	String m_hostname;
+	int m_numAdmins;
+	String m_level;
 };

mercurial