- the interface is now able to connect to arbitrary hosts (uses ^N to start the prompt) instead of the hardcoded localhost:10666

Sun, 14 Dec 2014 23:21:38 +0200

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sun, 14 Dec 2014 23:21:38 +0200
changeset 24
e651d02802c0
parent 23
f7221183a994
child 25
88b41eea08e0

- the interface is now able to connect to arbitrary hosts (uses ^N to start the prompt) instead of the hardcoded localhost:10666

sources/interface.cpp file | annotate | diff | comparison | revisions
sources/main.cpp file | annotate | diff | comparison | revisions
sources/network/ipaddress.cpp file | annotate | diff | comparison | revisions
sources/network/ipaddress.h 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/interface.cpp	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/interface.cpp	Sun Dec 14 23:21:38 2014 +0200
@@ -31,9 +31,18 @@
 #include <string.h>
 #include "interface.h"
 #include "network/rconsession.h"
+#include "network/ipaddress.h"
 
 enum { PAGE_SIZE = 10 };
 
+enum InputState
+{
+	INPUTSTATE_NORMAL,
+	INPUTSTATE_ADDRESS,
+	INPUTSTATE_PASSWORD,
+	INPUTSTATE_CONFIRM_DISCONNECTION,
+};
+
 static String g_input;
 static int g_cursor = 0;
 static int g_pan = 0;
@@ -46,6 +55,8 @@
 static Vector<String> g_output = {""};
 static int g_outputScroll = 0;
 static String g_title;
+static InputState g_inputState = INPUTSTATE_NORMAL;
+static IPAddress g_address;
 
 // -------------------------------------------------------------------------------------------------
 //
@@ -57,6 +68,24 @@
 
 // -------------------------------------------------------------------------------------------------
 //
+static FUNCTION
+interface_prompt_string() -> String
+{
+	String prompt;
+
+	switch (g_inputState)
+	{
+	case INPUTSTATE_NORMAL: prompt = ">"; break;
+	case INPUTSTATE_ADDRESS: prompt = "address:"; break;
+	case INPUTSTATE_PASSWORD: prompt = "password:"; break;
+	case INPUTSTATE_CONFIRM_DISCONNECTION: break;
+	}
+
+	return prompt;
+}
+
+// -------------------------------------------------------------------------------------------------
+//
 FUNCTION
 Interface::initialize() -> void
 {
@@ -153,8 +182,19 @@
 static FUNCTION
 interface_render_input() -> void
 {
-	static char prompt[] = ">";
-	int displaylength = COLS - strlen (prompt) - 2;
+	int pair = interface_color_pair (WHITE, BLUE);
+
+	if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
+	{
+		attron (pair);
+		mvhline (LINES - 2, 0, ' ', COLS);
+		mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n");
+		attroff (pair);
+		return;
+	}
+
+	String prompt = interface_prompt_string();
+	int displaylength = COLS - prompt.length() - 2;
 	int y = LINES - 2;
 
 	// Ensure the cursor is within bounds
@@ -170,27 +210,17 @@
 	int end = min<int> (g_input.length(), start + displaylength);
 	assert (g_cursor >= start and g_cursor <= end);
 
-	// Clear, but only as much as is necessary. I want to avoid clearing the entire line to save
-	// bandwidth over SSH connections. Perhaps needlessly? I'm paranoid.
-	int renderLength = strlen (prompt) + 1 + g_input.length();
-	static int lastRenderLength = 0;
-
-	if (lastRenderLength > renderLength)
-		mvhline (y, renderLength, ' ', lastRenderLength - renderLength);
-
-	lastRenderLength = renderLength;
-
 	// Render the input line, with the part of the input string that's before the cursor written
 	// AFTER the part that comes afterwards. This is to ensure that the cursor is placed at the
 	// position where our cursor is. It looks nice like that. :)
 
-	int pair = interface_color_pair (WHITE, BLUE);
+	mvhline (LINES - 2, 0, ' ', COLS);
+	mvprintw (y, prompt.length() + 1, "%s", g_input.chars());
 	attron (pair);
-	mvprintw (y, 0, "%s", prompt);
+	mvprintw (y, 0, "%s", prompt.chars());
 	attroff (pair);
-	mvprintw (y, strlen (prompt) + 1, "%s", g_input.chars());
 	g_cursorChar.ch = g_cursor != 0 ? g_input[g_cursor - 1] : '\0';
-	g_cursorChar.x = strlen (prompt) + (g_cursor - g_pan);
+	g_cursorChar.x = prompt.length() + (g_cursor - g_pan);
 	g_needRefresh = true;
 	g_needInputRender = false;
 }
@@ -237,7 +267,7 @@
 	if (g_cursorChar.ch != '\0')
 		mvprintw (y, g_cursorChar.x, "%c", g_cursorChar.ch);
 	else
-		mvprintw (y, 1, " ");
+		mvprintw (y, interface_prompt_string().length(), " ");
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -248,6 +278,15 @@
 	int ch = ::getch();
 	set_statusbar_text (String::from_number (ch));
 
+	if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
+	{
+		if (ch == 'y' or ch == 'Y')
+			g_inputState = INPUTSTATE_ADDRESS;
+		else if (ch == 'n' or ch == 'N')
+			g_inputState = INPUTSTATE_NORMAL;
+		return;
+	}
+
 	if (ch >= 0x20 and ch <= 0x7E)
 	{
 		g_input.insert (g_cursor++, char (ch));
@@ -320,9 +359,65 @@
 
 	case '\n':
 	case KEY_ENTER:
-		if (RCONSession::get_session()->send_command (g_input))
+		switch (g_inputState)
 		{
+		case INPUTSTATE_CONFIRM_DISCONNECTION:
+			break; // handled above
+
+		case INPUTSTATE_ADDRESS:
+			try
+			{
+				g_address = IPAddress::from_string (g_input);
+			}
+			catch (std::exception& e)
+			{
+				print (e.what());
+				return;
+			}
+
+			if (g_address.port == 0)
+				g_address.port = 10666;
+
 			g_input.clear();
+			g_inputState = INPUTSTATE_PASSWORD;
+			g_needInputRender = true;
+
+		case INPUTSTATE_PASSWORD:
+			if (g_inputState == INPUTSTATE_PASSWORD and not g_input.is_empty())
+			{
+				RCONSession* session = RCONSession::new_session();
+				session->set_password (g_input);
+				session->connect (g_address);
+				g_input.clear();
+				g_inputState = INPUTSTATE_NORMAL;
+				g_needInputRender = true;
+			}
+			break;
+
+		case INPUTSTATE_NORMAL:
+			if (RCONSession::get_session() != nullptr
+				and RCONSession::get_session()->send_command (g_input))
+			{
+				g_input.clear();
+				g_needInputRender = true;
+			}
+			break;
+		}
+		break;
+
+	case 'N' - 'A' + 1: // ^N
+		if (g_inputState == INPUTSTATE_NORMAL)
+		{
+			if (RCONSession::get_session() != nullptr
+				and RCONSession::get_session()->state() != RCON_DISCONNECTED)
+			{
+				g_inputState = INPUTSTATE_CONFIRM_DISCONNECTION;
+			}
+			else
+			{
+				g_inputState = INPUTSTATE_ADDRESS;
+			}
+
 			g_needInputRender = true;
 		}
 		break;
--- a/sources/main.cpp	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/main.cpp	Sun Dec 14 23:21:38 2014 +0200
@@ -43,10 +43,6 @@
 	HUFFMAN_Construct();
 	Interface::initialize();
 
-	new RCONSession;
-	RCONSession::get_session()->set_password ("testpassword");
-	RCONSession::get_session()->connect (IPAddress (localhost, 10666));
-
 	for (;;)
 	{
 		fd_set fdset;
--- a/sources/network/ipaddress.cpp	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/network/ipaddress.cpp	Sun Dec 14 23:21:38 2014 +0200
@@ -36,8 +36,6 @@
 #include <netdb.h>
 #include "ipaddress.h"
 
-bool IPAddress::sink;
-
 // -----------------------------------------------------------------------------
 //
 IPAddress::IPAddress() :
@@ -140,7 +138,7 @@
 // -----------------------------------------------------------------------------
 //
 STATIC METHOD
-IPAddress::from_string (String input, bool* ok) -> IPAddress
+IPAddress::from_string (String input) -> IPAddress
 {
 	unsigned int parts[4];
 	int colonpos = input.find (":");
@@ -156,10 +154,7 @@
 	else
 	{
 		// Possibly a hostname, try resolve it
-		value = IPAddress::resolve (addressString, ok);
-
-		if (*ok == false)
-			return value;
+		value = IPAddress::resolve (addressString);
 	}
 
 	if (colonpos != -1)
@@ -171,7 +166,7 @@
 // -----------------------------------------------------------------------------
 //
 STATIC METHOD
-IPAddress::resolve (String node, bool* ok) -> IPAddress
+IPAddress::resolve (String node) -> IPAddress
 {
 	addrinfo hints;
 	addrinfo* lookup;
@@ -179,10 +174,7 @@
 	hints.ai_family = AF_INET;
 
 	if (getaddrinfo (node, nullptr, &hints, &lookup) != 0)
-	{
-		*ok = false;
-		return IPAddress();
-	}
+		throw StringParseError ("unknown host " + node);
 
 	IPAddress result;
 	assert (lookup != nullptr);
--- a/sources/network/ipaddress.h	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/network/ipaddress.h	Sun Dec 14 23:21:38 2014 +0200
@@ -38,9 +38,23 @@
 
 struct IPAddress
 {
+	class StringParseError
+	{
+		String m_message;
+
+	public:
+		StringParseError (String message) :
+			m_message (message) {}
+
+		inline METHOD
+		what() const throw() -> const char*
+		{
+			return m_message.chars();
+		}
+	};
+
 	unsigned long host;
 	unsigned short port;
-	static bool sink;
 
 	IPAddress();
 	IPAddress (unsigned long host, unsigned short port);
@@ -56,8 +70,8 @@
 	inline METHOD operator!= (const IPAddress& other) const -> bool;
 	inline METHOD operator[] (int n) const -> unsigned char;
 
-	static METHOD from_string (String input, bool* ok = &sink) -> IPAddress;
-	static METHOD resolve (String node, bool* ok = &sink) -> IPAddress;
+	static METHOD from_string (String input) -> IPAddress;
+	static METHOD resolve (String node) -> IPAddress;
 };
 
 static const unsigned long localhost = 0x7F000001;
--- a/sources/network/rconsession.cpp	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/network/rconsession.cpp	Sun Dec 14 23:21:38 2014 +0200
@@ -16,13 +16,24 @@
 	}
 
 	g_rconSession = this;
+}
 
-	if (not m_socket.set_blocking (false))
+// -------------------------------------------------------------------------------------------------
+//
+STATIC METHOD
+RCONSession::new_session() -> RCONSession*
+{
+	RCONSession* session = new RCONSession;
+
+	if (not session->socket()->set_blocking (false))
 	{
-		// TODO: find a better way to deal with errors
 		print ("unable to set socket as non-blocking: %s\n",
-			m_socket.error_string().chars());
+			session->socket()->error_string().chars());
+		delete session;
+		return nullptr;
 	}
+
+	return session;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -275,3 +286,11 @@
 	bump_last_ping();
 	return true;
 }
+
+// -------------------------------------------------------------------------------------------------
+//
+METHOD
+RCONSession::state() const -> RCONSessionState
+{
+	return m_state;
+}
--- a/sources/network/rconsession.h	Sun Dec 14 20:47:44 2014 +0200
+++ b/sources/network/rconsession.h	Sun Dec 14 23:21:38 2014 +0200
@@ -88,7 +88,6 @@
 class RCONSession
 {
 public:
-	RCONSession();
 	~RCONSession();
 
 	METHOD connect (IPAddress address) -> void;
@@ -103,10 +102,14 @@
 	METHOD tick() -> void;
 	METHOD bump_last_ping() -> void;
 	METHOD send_command (const String& message) -> bool;
+	METHOD state() const -> RCONSessionState;
 
+	static METHOD new_session() -> RCONSession*;
 	static METHOD get_session() -> RCONSession*;
 
 private:
+	RCONSession();
+
 	RCONSessionState m_state;
 	IPAddress m_address;
 	UDPSocket m_socket;

mercurial