Browse Source

This is my work on debugging output.

This shows what dispatcher is getting SL.
This shows what dispatcher sent 2S.
Steve Thielemann 3 years ago
parent
commit
e11fd21d58
10 changed files with 1538 additions and 76 deletions
  1. 19 15
      director.cpp
  2. 1363 0
      director.cpp.orig
  3. 2 1
      director.h
  4. 48 16
      dispatchers.cpp
  5. 11 8
      dispatchers.h
  6. 62 19
      scripts.cpp
  7. 4 4
      scripts.h
  8. 24 9
      session.cpp
  9. 1 0
      session.h
  10. 4 4
      test-director.cpp

+ 19 - 15
director.cpp

@@ -104,7 +104,7 @@ void Director::client_input(const std::string &input) {
     }
   }
   // Ok...
-  if (talk_direct) to_server(input);
+  if (talk_direct) to_server(input, "Director::client_input");
 }
 
 void Director::server_line(const std::string &line,
@@ -238,6 +238,7 @@ void Director::server_line(const std::string &line,
   }
 
   if (chain) {
+    BUGZ_LOG(info) << chain->name << " SL: [" << line << "]";
     chain->server_line(line, raw_line);
   }
 }
@@ -272,11 +273,14 @@ void Director::server_prompt(const std::string &prompt,
   if (emit_server_prompt)
     emit_server_prompt(prompt);
    */
-  if (chain) chain->server_prompt(prompt);
+  if (chain) {
+    BUGZ_LOG(info) << chain->name << " SP: [" << prompt << "]";
+    chain->server_prompt(prompt);
+  }
 }
 
 void Director::build_menu(void) {
-  main_menu = std::make_shared<MenuDispatch>(*this);
+  main_menu = std::make_shared<MenuDispatch>(*this, "MenuMain");
   MenuDispatch *md = static_cast<MenuDispatch *>(&(*main_menu));
   ANSIColor bcolor(COLOR::YELLOW, COLOR::BLUE, ATTR::BOLD);
   ANSIColor tcolor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD);
@@ -308,13 +312,13 @@ void Director::build_menu(void) {
               {"X", "eXit"}};
   md->setNotify([this]() { this->menu_choice(); });
 
-  cim = std::make_shared<CIMDispatch>(*this);
+  cim = std::make_shared<CIMDispatch>(*this, "CIMDirector");
   cim->setNotify([this]() { this->cim_done(); });
 }
 
 void Director::proxy_activate(void) {
   active = true;   // yes, set keep-alive timer.
-  to_server(" ");  // start keep-alive timer.
+  to_server(" ", "Director::proxy_active()");  // start keep-alive timer.
 
   // set other values we need
   talk_direct = false;
@@ -459,7 +463,7 @@ void Director::menu_choice(void) {
           // type 0 ports.  Type 9 stays at 9.
           galaxy.meta["port_CIM"] = (int)time_t_now();
           chain = cim;
-          to_server("^RQ");
+          to_server("^RQ", "Director::Port CIM");
           {
             std::string text = str(boost::format("Port CIM Report (%1%)\n\r") %
                                    galaxy.ports.size());
@@ -471,7 +475,7 @@ void Director::menu_choice(void) {
           break;
         case 'W':  // Warp CIM
           chain = cim;
-          to_server("^IQ");
+          to_server("^IQ", "Director::Warp CIM");
           {
             std::string text = str(boost::format("Warp CIM Report (%1%)\n\r") %
                                    galaxy.warps.size());
@@ -512,7 +516,7 @@ MenuDispatch *Director::init_scripts_menu(void) {
     md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
     return md;
   } else {
-    scripts_menu = std::make_shared<MenuDispatch>(*this);
+    scripts_menu = std::make_shared<MenuDispatch>(*this, "MenuScripts");
     md = static_cast<MenuDispatch *>(&(*scripts_menu));
     md->menu_box_color = "\x1b[0;32;40m";
     md->menu_text_color = "\x1b[1;32;40m";
@@ -555,7 +559,7 @@ void Director::scripts_done(void) {
       switch (md->input[0]) {
         case 'T':  // Trade
         {
-          script = std::make_shared<TraderDispatch>(*this);
+          script = std::make_shared<TraderDispatch>(*this, "TraderDirector");
           TraderDispatch *ts = static_cast<TraderDispatch *>(&((*script)));
           ts->setNotify([this]() { this->proxy_deactivate(); });
 
@@ -585,7 +589,7 @@ void Director::scripts_done(void) {
           return;
         } break;
         case 'S': {
-          script = std::make_shared<MoveDispatch>(*this);
+          script = std::make_shared<MoveDispatch>(*this, "MoveDirector");
           MoveDispatch *md = static_cast<MoveDispatch *>(&((*script)));
           md->setNotify([this]() { this->proxy_deactivate(); });
           md->move_to = 1;
@@ -594,7 +598,7 @@ void Director::scripts_done(void) {
           return;
         } break;
         case '!': {
-          script = std::make_shared<ScriptTerror>(*this);
+          script = std::make_shared<ScriptTerror>(*this, "TerrorDirector");
           ScriptTerror *st = static_cast<ScriptTerror *>(&(*script));
           st->setNotify([this]() {
             script.reset();
@@ -626,7 +630,7 @@ void Director::scripts_done(void) {
           }
         } break;
         case 'V': {
-          script = std::make_shared<ScriptVoyager>(*this);
+          script = std::make_shared<ScriptVoyager>(*this, "VoyagerDirector");
           ScriptVoyager *sv = static_cast<ScriptVoyager *>(&(*script));
           sv->setNotify([this]() {
             script.reset();
@@ -637,7 +641,7 @@ void Director::scripts_done(void) {
           return;
         } break;
         case 'E': {
-          script = std::make_shared<ScriptExplore>(*this);
+          script = std::make_shared<ScriptExplore>(*this, "ExploreDirector");
           ScriptExplore *se = static_cast<ScriptExplore *>(&(*script));
           se->setNotify([this]() {
             script.reset();
@@ -648,7 +652,7 @@ void Director::scripts_done(void) {
           return;
         } break;
         case 'U': {
-          script = std::make_shared<ScriptPlanet>(*this);
+          script = std::make_shared<ScriptPlanet>(*this, "PlanetDirector");
           ScriptPlanet *sp = static_cast<ScriptPlanet *>(&(*script));
           sp->setNotify([this]() {
             script.reset();
@@ -699,7 +703,7 @@ InputDispatch *Director::init_config_input(void) {
     return id;
   } else {
     // set it up
-    config_input = std::make_shared<InputDispatch>(*this);
+    config_input = std::make_shared<InputDispatch>(*this, "InputDirector");
     id = static_cast<InputDispatch *>(&(*config_input));
     ANSIColor by{1, 33};
     ANSIColor cyan{36};

+ 1363 - 0
director.cpp.orig

@@ -0,0 +1,1363 @@
+#include "director.h"
+
+#include <boost/format.hpp>
+#include <cctype>
+
+#include "ansicolor.h"
+#include "boxes.h"
+#include "galaxy.h"
+#include "logging.h"
+#include "scripts.h"
+#include "utils.h"
+
+Director::Director() {
+  BUGZ_LOG(warning) << "Director::Director()";
+
+  // active = false;
+  game = 0;  // not in a game
+  galaxy.reset();
+
+  // do everything proxy_deactivate does ...
+  // proxy_deactivate();
+  active = false;
+  // reset everything back to good state
+  talk_direct = true;
+  show_client = true;
+  count = 0;
+
+  /*
+  Setup StringFunc for SL_parser:
+  Construct these once, rather then every single time we need them.
+  */
+  SF_cimline = [this](const std::string &s) { this->SL_cimline(s); };
+  SF_sectorline = [this](const std::string &s) { this->SL_sectorline(s); };
+  SF_portline = [this](const std::string &s) { this->SL_portline(s); };
+  SF_warpline = [this](const std::string &s) { this->SL_warpline(s); };
+  SF_infoline = [this](const std::string &s) { this->SL_infoline(s); };
+  SF_densityline = [this](const std::string &s) { this->SL_densityline(s); };
+  SF_computer_portline = [this](const std::string &s) {
+    this->SL_computer_portline(s);
+  };
+  SF_planetline = [this](const std::string &s) { this->SL_planetline(s); };
+  build_menu();
+}
+
+Director::~Director() { BUGZ_LOG(warning) << "Director::~Director()"; }
+
+void Director::client_input(const std::string &input) {
+  // If we're already active, don't try to activate.
+
+  if (chain) {
+    chain->client_input(input);
+    return;
+  }
+
+  if (active) {
+    if (input == "Q" || input == "q") proxy_deactivate();
+    return;
+  } else if (input == "\x1b" || input == "~") {
+    std::string &prompt = current_prompt;
+    BUGZ_LOG(trace) << "ACTIVATE prompt shows: [" << prompt << "]";
+
+    if (prompt == "Selection (? for menu): ") {
+      to_client(
+          "\n\rThere's not much we can do here.  Activate in-game at a "
+          "Command prompt.\n\r");
+      to_client(current_raw_prompt);
+      return;
+    }
+
+    // easter-eggs:
+
+    if (prompt == "Enter your choice: ") {
+      ANSIColor c1(COLOR::CYAN, ATTR::BOLD);
+      ANSIColor c2(COLOR::WHITE, ATTR::BOLD);
+
+      to_client(std::string("\n\r") + c1() + "I'd choose " + c2() + "`T`" +
+                c1() + ", but that's how I was coded.\n\r");
+      //          "\n\r\x1b[1;36mI'd choose \x1b[1;37m`T`\x1b[1;36m, but "
+      //          "that's how I was coded.\n\r");
+
+      to_client(current_raw_prompt);
+      return;
+    }
+
+    // easter-egg
+    if (prompt == "[Pause]") {
+      ANSIColor c1(COLOR::CYAN, ATTR::BOLD);
+      to_client(std::string(" ") + c1() + "PAWS" + reset() + "\n\r");
+      to_client(current_raw_prompt);
+      return;
+    }
+
+    if (at_planet_prompt(prompt)) {
+      // future:  If activated at planet menu, activate the planet upgrade
+      // script!  (Maybe).  If I can figure out what planet it is/and where.
+      to_client("\n\r\x1b[0mFUTURE:  Activate the planet upgrade script.\n\r");
+      to_client(current_raw_prompt);
+      return;
+    }
+
+    if (at_command_prompt(prompt)) {
+      proxy_activate();
+      return;
+    }
+  }
+  // Ok...
+  if (talk_direct) to_server(input);
+}
+
+void Director::server_line(const std::string &line,
+                           const std::string &raw_line) {
+  // check for if we entered game/left game
+
+  if (line.find("TradeWars Game Server   ") != std::string::npos) {
+    // Inject our proxy activation message
+    to_client("\rTradeWars Proxy v2++ READY (~ or ESC to activate)\n\r");
+    /*
+    There's a delay here when I save the game data.
+    I've moved it futher down.  Hide it at a prompt, so it isn't so
+    noticeable.
+     */
+    // reset "active game" -- we're at the TWGS main menu
+  }
+
+  if (line.find("Selection (? for menu): ") != std::string::npos) {
+    char ch = line[line.length() - 1];
+    if (ch >= 'A' && ch < 'Q') {
+      if ((game) && (game != ch)) {
+        galaxy.save();
+      }
+      game = ch;
+      BUGZ_LOG(warning) << "GAME " << game << " activated!";
+      // TODO:  Load game data
+      galaxy.game = game;
+      galaxy.username = username;
+      galaxy.load();
+
+      // YAML loaded, set sensible default config values (if missing).
+      if (!galaxy.config.contains("display_lines")) {
+        galaxy.config["display_lines"] = 20;
+      }
+
+      galaxy.meta["help"]["display_lines"] =
+          "Number of report lines to display";
+
+      if (!galaxy.config.contains("burnt_percent")) {
+        galaxy.config["burnt_percent"] = 40;
+      }
+
+      galaxy.meta["help"]["burnt_percent"] =
+          "Don't display ports in report below this percent";
+    }
+    // not needed (handled by above Game Server check).
+    if (ch == 'Q') {
+      if (game) {
+        // TODO:  Save galaxy data
+        galaxy.save();
+      }
+      game = 0;
+      galaxy.reset();
+    }
+  }
+
+  if (game) {
+    // in-game parsing here.
+
+    /*
+      ____                             _     _
+     / ___|  ___ _ ____   _____ _ __  | |   (_)_ __   ___
+     \___ \ / _ \ '__\ \ / / _ \ '__| | |   | | '_ \ / _ \
+      ___) |  __/ |   \ V /  __/ |    | |___| | | | |  __/
+     |____/ \___|_|    \_/ \___|_|    |_____|_|_| |_|\___|
+
+      ____                _
+     |  _ \ __ _ _ __ ___(_)_ __   __ _
+     | |_) / _` | '__/ __| | '_ \ / _` |
+     |  __/ (_| | |  \__ \ | | | | (_| |
+     |_|   \__,_|_|  |___/_|_| |_|\__, |
+                                  |___/
+
+   This is where all of the server lines are gleaned for all the
+   information that we can get out of them.
+
+    // When activating the computer
+    SP: [Command [TL=00:00:00]:[926] (?=Help)? : ]
+    Sector: 926
+    CI: [c]
+    SL: [Command [TL=00:00:00]:[926] (?=Help)? : C]
+    SL: [<Computer>]
+    SL: []
+    SL: [<Computer activated>]
+    SL: []
+    SP: [Computer command [TL=00:00:00]:[926] (?=Help)? ]
+
+    // deactivating the computer
+    SL: [Computer command [TL=00:00:00]:[926] (?=Help)? Q]
+    SL: []
+    SL: [<Computer deactivated>]
+    SL: []
+    */
+
+    if (line == "<Port>") {
+      SL_parser = SF_portline;
+    }
+
+    if (line ==
+            "                           Corporate Planet Scan                  "
+            "            " ||
+        line ==
+            "                           Personal Planet Scan                   "
+            "           ") {
+      SL_parser = SF_planetline;
+    }
+
+    /*
+    if (startswith(line, " Items     Status  Trading % of max OnBoard"))
+      SL_parser = SF_portline;
+    */
+    if (endswith(line, "Relative Density Scan")) {
+      galaxy.dscan.reset(current_sector);
+      SL_parser = SF_densityline;
+    }
+    if (startswith(line, "Sector  : ")) SL_parser = SF_sectorline;
+    if (line == ": ") SL_parser = SF_cimline;
+    if (line == "<Info>") SL_parser = SF_infoline;
+    if (startswith(line, "What sector is the port in? [")) {
+      // Computer Port Report
+      // SL: [What sector is the port in? [611] 4]
+      size_t pos = line.rfind(' ');
+
+      computer_port_sector = stoi(line.substr(pos));
+      SL_parser = SF_computer_portline;
+    }
+  }
+
+  if (SL_parser) {
+    SL_parser(line);
+  }
+
+  if (chain) {
+    chain->server_line(line, raw_line);
+  }
+}
+
+void Director::server_prompt(const std::string &prompt,
+                             const std::string &raw_prompt) {
+  current_prompt = prompt;
+  current_raw_prompt = raw_prompt;
+
+  if (game) {
+    if (prompt == "Selection (? for menu): ") {
+      galaxy.save();
+      game = 0;
+      galaxy.reset();
+    }
+
+    // in-game parsing here.
+    // if (startswith(prompt, "Command [") && endswith(prompt, "] (?=Help)? :
+    // ")) {
+    if (at_command_prompt(prompt)) {
+      std::string sector_text;
+      size_t before, after;
+      before = prompt.find("]:[") + 3;
+      after = prompt.find("] (?=Help)");
+      sector_text = prompt.substr(before, after - before);
+      current_sector = stoi(sector_text);
+      BUGZ_LOG(info) << "current_sector = " << current_sector;
+    }
+  }
+
+  /*
+  if (emit_server_prompt)
+    emit_server_prompt(prompt);
+   */
+  if (chain) chain->server_prompt(prompt);
+}
+
+void Director::build_menu(void) {
+  main_menu = std::make_shared<MenuDispatch>(*this);
+  MenuDispatch *md = static_cast<MenuDispatch *>(&(*main_menu));
+  ANSIColor bcolor(COLOR::YELLOW, COLOR::BLUE, ATTR::BOLD);
+  ANSIColor tcolor(COLOR::WHITE, COLOR::BLUE, ATTR::BOLD);
+  ANSIColor mocolor(COLOR::CYAN, ATTR::BOLD);
+  md->menu_box_color = bcolor();   // "\x1b[1;33;44m";
+  md->menu_text_color = tcolor();  // "\x1b[1;37;44m";
+  md->menu_title = "Proxy Menu";
+  md->menu_options_color = mocolor();  // "\x1b[1;36;40m";
+
+  ANSIColor by{1, 33};
+  ANSIColor cyan{36};
+  ANSIColor bg{1, 32};
+  std::string prompt = by() + "M" + cyan() + "ain " + by() + "P" + cyan() +
+                       "roxy " + bg() + "=>" + reset() + " ";
+
+  md->menu_prompt = prompt;  // "Main Proxy => ";
+
+  // "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "
+  // "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";
+  md->lazy = true;
+  md->menu = {{"C", "Configure"},
+              {"D", "Display Report"},
+              {"E", "Export Data/Save"},
+              {"I", "Information"},
+              {"P", "Port CIM"},
+              {"W", "Warp CIM"},
+              {"T", "Trading Report (same as D)"},
+              {"S", "Scripts"},
+              {"X", "eXit"}};
+  md->setNotify([this]() { this->menu_choice(); });
+
+  cim = std::make_shared<CIMDispatch>(*this);
+  cim->setNotify([this]() { this->cim_done(); });
+}
+
+void Director::proxy_activate(void) {
+  active = true;   // yes, set keep-alive timer.
+  to_server(" ");  // start keep-alive timer.
+
+  // set other values we need
+  talk_direct = false;
+  show_client = false;
+  /*
+  Wait a minute .. this might be confusing.
+  Shouldn't I send them the current prompt?
+  Just in case we abort in the middle of something?!?
+  */
+  old_prompt = current_prompt;
+  old_raw_prompt = current_raw_prompt;
+  to_client("\x1b[0m\n\r");
+
+  /*
+  ╔══════════════════════════════╗
+  ║    TradeWars Proxy Active    ║
+  ╚══════════════════════════════╝
+     -=>
+  */
+
+  Boxes box(30, 1, true);
+  box.boxcolor = "\x1b[1;33;44m";
+  box.textcolor = "\x1b[1;33;44m";
+  to_client(box.top());
+  std::string output = "    TradeWars Proxy \x1b[5mActive\x1b[0;1;33;44m    ";
+  to_client(box.row(output));
+  to_client(box.bottom());
+
+  if (galaxy.meta.contains("port_CIM")) {
+    time_t last_port_cim_report = galaxy.meta["port_CIM"].get<int>();
+    int seconds_ago = time_t_now() - last_port_cim_report;
+    int minutes_ago = seconds_ago / 60;
+    BUGZ_LOG(fatal) << "port_CIM was " << minutes_ago << " minutes ago.";
+
+    if (minutes_ago >= 60) {
+      float hours_ago = minutes_ago / 60.0;
+      std::string message =
+          str(boost::format(
+                  "Warning: Last Port CIM Refresh was %1$0.2f hours ago.\n\r") %
+              hours_ago);
+      to_client(message);
+    }
+  } else {
+    BUGZ_LOG(fatal) << "no meta port_CIM value seen.";
+  }
+
+  chain = main_menu;
+  main_menu->activate();
+  /*
+    // Using InputDispatch  -- and see have_input
+    std::shared_ptr<Dispatch> readline = std::make_shared<InputDispatch>(*this);
+    chain = readline;
+    InputDispatch *id = static_cast<InputDispatch *>(&(*readline));
+    id->prompt = "\x1b[0m\x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
+    id->max_length = 15;
+    id->setNotify([this]() { this->have_input(); });
+    readline->activate();
+  */
+}
+
+void Director::menu_choice(void) {
+  MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
+  if (md) {
+    if (md->input.empty()) {
+      to_client("Menu aborted.\n\r");
+      proxy_deactivate();
+      return;
+    } else {
+      switch (md->input[0]) {
+        case 'C':  // configure
+          config_edit();
+          return;
+          break;
+        case 'D':
+        case 'T':  // display trading report
+        {
+          auto pptv = galaxy.find_best_trades();
+          std::string output;
+          galaxy.sort_port_pair_type(pptv);
+
+          int max_display = 20;
+          if (galaxy.config.contains("display_lines"))
+            max_display = galaxy.config["display_lines"].get<int>();
+          else
+            galaxy.config["display_lines"] = max_display;
+
+          if ((max_display <= 0) || (max_display > 255)) {
+            max_display = 255;
+            galaxy.config["display_lines"] = 255;
+          }
+          const int per_line = 5;
+          int count = 0;
+          int line = 0;
+          std::string display_line;
+
+          ANSIColor by{1, 33};  // yellow
+          ANSIColor cyan{36};   // cyan
+          ANSIColor bg{1, 32};  // bright green
+          ANSIColor bb{1, 34};  // bright blue
+
+          for (auto const &ppt : pptv) {
+            output =
+                str(boost::format("%1%%2$5d%3%:%4%%5$-5d%3%(%6%%7$d%3%) ") %
+                    by() % ppt.s1 % cyan() % bg() % ppt.s2 % bb() % ppt.type);
+            display_line.append(output);
+
+            ++count;
+            if (count == per_line) {
+              count = 0;
+              display_line.append("\n\r");
+              to_client(display_line);
+              display_line.clear();
+              ++line;
+            }
+
+            if (line == max_display) break;
+          }
+
+          if (count != 0) {
+            display_line.append("\n\r");
+            to_client(display_line);
+            display_line.clear();
+          }
+
+          // We got < 5 lines, and max_display is > 5.  Offer suggestion:
+          if ((line < 5) && (max_display > 5)) {
+            // suggestion:
+            to_client(
+                "HINT: For more lines, try reducing the burnt_percent?\n\r");
+          }
+        } break;
+        case 'E':  // Export Data/Save
+          to_client("Saving...");
+          galaxy.save();
+          to_client("\rSaved....\n\r");
+          break;
+        case 'I':  // Information
+          information();
+          break;
+        case 'P':  // Port CIM
+          // Since we're adding/updating, we don't lose our
+          // type 0 ports.  Type 9 stays at 9.
+          galaxy.meta["port_CIM"] = (int)time_t_now();
+          chain = cim;
+          to_server("^RQ");
+          {
+            std::string text = str(boost::format("Port CIM Report (%1%)\n\r") %
+                                   galaxy.ports.size());
+            // to_client("Port CIM Report\n\r");
+            to_client(text);
+          }
+          chain->activate();
+          return;
+          break;
+        case 'W':  // Warp CIM
+          chain = cim;
+          to_server("^IQ");
+          {
+            std::string text = str(boost::format("Warp CIM Report (%1%)\n\r") %
+                                   galaxy.warps.size());
+            // to_client("Warp CIM Report\n\r");
+            to_client(text);
+          }
+          chain->activate();
+          return;
+          break;
+        // case 'T':  // Trading Report
+        //  break;
+        case 'S':  // Scripts
+        {
+          init_scripts_menu();
+          chain = scripts_menu;
+          chain->activate();
+          return;
+        } break;
+        case 'X':  // Exit
+          proxy_deactivate();
+          return;
+      }
+      /*
+      std::string text = str(
+          boost::format("Back from Menu [%1%] was selected.\n\r") %
+      md->input);
+      to_client(text);
+      */
+
+      md->activate();
+    }
+  }
+}
+
+MenuDispatch *Director::init_scripts_menu(void) {
+  MenuDispatch *md;
+  if (scripts_menu) {
+    md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
+    return md;
+  } else {
+    scripts_menu = std::make_shared<MenuDispatch>(*this);
+    md = static_cast<MenuDispatch *>(&(*scripts_menu));
+    md->menu_box_color = "\x1b[0;32;40m";
+    md->menu_text_color = "\x1b[1;32;40m";
+    md->menu_title = "Scripts Menu";
+    md->menu_options_color = "\x1b[1;32;40m";
+    md->lazy = false;
+
+    ANSIColor by{1, 33};
+    ANSIColor cyan{36};
+    ANSIColor bg{1, 32};
+    std::string prompt = by() + "S" + cyan() + "cript " + by() + "M" + cyan() +
+                         "enu " + bg() + "=>" + reset() + " ";
+
+    md->menu_prompt = prompt;  // " SCRIPT : ";
+    md->menu = {{"!", "Terror"},
+                {"T", "Trade"},
+                {"S", "Safe Move"},
+                {"C", "Closest Trade"},
+                {"N", "Nearest Unexplored"},
+                {"V", "Voyager Explorer"},
+                {"E", "Explorer"},
+                {"U", "Upgrade Planet Pants"},
+                {"X", "Exit Scripts"}};
+    md->setNotify([this]() { this->scripts_done(); });
+    return md;
+  }
+}
+
+void Director::scripts_done(void) {
+  // Was script selected?  If so, run it!
+  // otherwise, back to the menu we go...
+  MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*scripts_menu));
+  if (md) {
+    if (md->input.empty()) {
+      to_client("Scripts aborted.\n\r");
+      scripts_menu.reset();
+      proxy_deactivate();
+      return;
+    } else {
+      switch (md->input[0]) {
+        case 'T':  // Trade
+        {
+          script = std::make_shared<TraderDispatch>(*this);
+          TraderDispatch *ts = static_cast<TraderDispatch *>(&((*script)));
+          ts->setNotify([this]() { this->proxy_deactivate(); });
+
+          // Locate best trades
+          auto found = galaxy.find_trades(current_sector, false);
+          if (found.empty()) {
+            to_client(
+                "No Trades found.  Port burnt (CONFIG: lower burnt_percent?) "
+                "or no ports around.\n\r");
+            proxy_deactivate();
+            return;
+          }
+          // sort first?
+          galaxy.sort_port_pair_type(found);
+
+          BUGZ_LOG(fatal) << "Found " << found.size() << " possible trade(s).";
+          BUGZ_LOG(fatal) << "Best: " << found[0].s1 << "," << found[0].s2
+                          << " : " << found[0].type;
+
+          // Set parameters
+          ts->port[0] = found[0].s1;
+          ts->port[1] = found[0].s2;
+          ts->type = found[0].type;
+          ts->trades = found[0].trades;
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        case 'S': {
+          script = std::make_shared<MoveDispatch>(*this);
+          MoveDispatch *md = static_cast<MoveDispatch *>(&((*script)));
+          md->setNotify([this]() { this->proxy_deactivate(); });
+          md->move_to = 1;
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        case '!': {
+          script = std::make_shared<ScriptTerror>(*this);
+          ScriptTerror *st = static_cast<ScriptTerror *>(&(*script));
+          st->setNotify([this]() {
+            script.reset();
+            this->proxy_deactivate();
+          });
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        // }
+        case 'C': {
+          auto best = galaxy.find_closest(current_sector);
+          if (best.type != 0) {
+            std::string text =
+                str(boost::format("Best/Closest: %1% with %2% & %3%\n\r") %
+                    best.type % best.s1 % best.s2);
+            to_client(text);
+          } else {
+            to_client("I don't see any best trades.\n\r");
+          }
+        } break;
+        case 'N': {
+          sector_type s = galaxy.find_nearest_unexplored(current_sector);
+          if (s != 0) {
+            std::string text = str(boost::format("Sector: %1%\n\r") % s);
+            to_client(text);
+          } else {
+            to_client("I don't see any unexplored.\n\r");
+          }
+        } break;
+        case 'V': {
+          script = std::make_shared<ScriptVoyager>(*this);
+          ScriptVoyager *sv = static_cast<ScriptVoyager *>(&(*script));
+          sv->setNotify([this]() {
+            script.reset();
+            this->proxy_deactivate();
+          });
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        case 'E': {
+          script = std::make_shared<ScriptExplore>(*this);
+          ScriptExplore *se = static_cast<ScriptExplore *>(&(*script));
+          se->setNotify([this]() {
+            script.reset();
+            this->proxy_deactivate();
+          });
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        case 'U': {
+          script = std::make_shared<ScriptPlanet>(*this);
+          ScriptPlanet *sp = static_cast<ScriptPlanet *>(&(*script));
+          sp->setNotify([this]() {
+            script.reset();
+            this->proxy_deactivate();
+          });
+          chain = script;
+          chain->activate();
+          return;
+        } break;
+        case 'Q':
+          chain = main_menu;
+          main_menu->activate();
+          return;
+          break;
+      }
+    }
+  }
+
+  proxy_activate();
+
+  // And to end scripts, we do .. what exactly?
+  // DEBUG:  Ok, why does everything work OK if I reset the scripts_menu
+  // here?? probably do want to destroy scripts here.  ;)
+  // scripts_menu.reset();
+  // proxy_deactivate();
+}
+
+/**
+ * @brief Setup Config Input
+ *
+ * @return DispatchInput*
+ */
+InputDispatch *Director::init_config_input(void) {
+  InputDispatch *id;
+  if (config_input) {
+    // Yes, it has been setup before.
+    id = dynamic_cast<InputDispatch *>(&(*config_input));
+
+    ANSIColor by{1, 33};
+    ANSIColor cyan{36};
+    ANSIColor bg{1, 32};
+    std::string prompt =
+        by() + "C" + cyan() + "onfig " + bg() + "=>" + reset() + " ";
+    id->prompt = prompt;  // "Config => ";
+    id->numeric = true;
+    id->max_length = 3;
+    config_item.clear();
+    return id;
+  } else {
+    // set it up
+    config_input = std::make_shared<InputDispatch>(*this);
+    id = static_cast<InputDispatch *>(&(*config_input));
+    ANSIColor by{1, 33};
+    ANSIColor cyan{36};
+    ANSIColor bg{1, 32};
+    std::string prompt =
+        by() + "C" + cyan() + "onfig " + bg() + "=>" + reset() + " ";
+    id->prompt = prompt;  // "Config => ";
+    id->numeric = true;
+    id->max_length = 3;
+    id->setNotify([this]() { this->config_have_input(); });
+    config_item.clear();
+    return id;
+  }
+}
+
+void Director::config_edit(void) {
+  // display current config
+  std::string menu_box_color = "\x1b[1;33;44m";
+  std::string menu_text_color = "\x1b[1;37;44m";
+  auto abox = Boxes::alert("   Configuration:   ", menu_box_color,
+                           menu_text_color, 20, 1, true);
+  for (auto line : abox) {
+    to_client(line);
+  }
+  // to_client("Configuration:\n\r");
+  int item = 1;
+  ANSIColor number(COLOR::CYAN);
+  ANSIColor key(COLOR::GREEN, ATTR::BOLD);
+  ANSIColor value(COLOR::BLUE, ATTR::BOLD);
+
+  for (auto const &cfg : galaxy.config.items()) {
+    // for (auto const &cfg : galaxy.config) {
+    std::string output =
+        str(boost::format("%1%%2$2d %3%%4$20s: %5%%6$s%7%\n\r") % number() %
+            item % key() % cfg.key() % value() % cfg.value() % reset());
+    to_client(output);
+    ++item;
+  }
+  std::string message =
+      number() + "Enter number to edit, " + key() + "blank to exit.\n\r";
+  // to_client("Enter number to edit, blank to exit.\n\r");
+  to_client(message);
+
+  // setup call to config_input:
+  InputDispatch *id = init_config_input();
+  chain = config_input;
+  id->activate();
+
+  // to return to the menu:
+  // MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
+  // md->activate();
+}
+
+void Director::config_have_input(void) {
+  InputDispatch *id = dynamic_cast<InputDispatch *>(&(*config_input));
+
+  if (config_item.empty()) {
+    // This is a config menu selection
+    if (id->input.empty()) {
+      // We're done here.  Return to menu.
+      chain = main_menu;
+      MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
+      md->activate();
+      // destroy the input?  yes.
+      config_input.reset();
+      return;
+    } else {
+      int item = sstoi(id->input);
+
+      if ((item < 1) || (item > (int)galaxy.config.size())) {
+        // selection out of range - redisplay config menu
+        to_client("What?  I didn't see that item.\n\r");
+        config_edit();
+        return;
+      } else {
+        int pos = 1;
+
+        for (auto const &c : galaxy.config.items()) {
+          if (pos == item) {
+            // got it!
+            ANSIColor key(COLOR::GREEN, ATTR::BOLD);
+            ANSIColor value(COLOR::BLUE, ATTR::BOLD);
+
+            config_item = c.key();
+            std::string output =
+                str(boost::format("%1%%2% : %3%%4%\n\r") % key() % config_item %
+                    value() % galaxy.meta["help"][config_item]);
+            to_client(output);
+            id->max_length = 30;
+            id->numeric = false;
+
+            ANSIColor by{1, 33};
+            ANSIColor cyan{36};
+            ANSIColor bg{1, 32};
+            std::string prompt =
+                by() + "C" + cyan() + "hange to " + bg() + "=>" + reset() + " ";
+            id->prompt = prompt;
+            id->activate();
+            return;
+          };
+          ++pos;
+        }
+        to_client("What?  I didn't find that item?\n\r");
+        config_edit();
+        return;
+      }
+    }
+  } else {
+    // This is a config item edit
+    if (id->input.empty()) {
+      to_client("No change.\n\r");
+      config_edit();
+      return;
+    } else {
+      BUGZ_LOG(fatal) << "Config EDIT: " << config_item << " = " << id->input;
+      galaxy.config[config_item] = id->input;
+      config_edit();
+      return;
+    }
+  }
+}
+
+void Director::have_input(void) {
+  ++count;
+  InputDispatch *id = dynamic_cast<InputDispatch *>(&(*chain));
+  if (id) {
+    std::string output =
+        str(boost::format("Your Input (%2%): [%1%]\n\r") % id->input % count);
+    to_client("\x1b[0m");
+    to_client(output);
+  } else {
+    proxy_deactivate();
+    return;
+  }
+  if (count > 3) {
+    proxy_deactivate();
+  } else {
+    chain->activate();
+  }
+}
+
+void Director::cim_done(void) {
+  BUGZ_LOG(info) << "CIM done";
+  chain = main_menu;
+  main_menu->activate();
+}
+
+void Director::information(void) {
+  std::string output;
+  to_client("I currently know the following:\n\r");
+  output = str(
+      boost::format("Ports: %1%, Sectors: %2%, Config: %3%, Meta: %4%\n\r") %
+      galaxy.ports.size() % galaxy.warps.size() % galaxy.config.size() %
+      galaxy.meta.size());
+  to_client(output);
+}
+
+void Director::proxy_deactivate(void) {
+  active = false;
+  // reset everything back to good state
+  talk_direct = true;
+  show_client = true;
+  chain.reset();
+  to_client("\n\r");
+  to_client(current_raw_prompt);
+}
+
+/*
+
+Server Line Parsing Routines
+
+ */
+
+void Director::SL_cimline(const std::string &line) {
+  if (line == ": ENDINTERROG") {
+    SL_parser = nullptr;
+    return;
+  }
+  if (line == ": ") {
+    // do I need to do anything special here for this?
+    // Maybe -- We would save special ports that don't show up
+    // (StarDock/Special) before. We don't know (at this point) if this is
+    // warps or ports.
+    return;
+  }
+  if (line.empty()) {
+    SL_parser = nullptr;
+    return;
+  }
+
+  // parse cimline
+  // size_t pos = line.find('%');
+  // std::string work = line;
+
+  // if (pos == line.npos) {
+  if (in(line, "%")) {
+    // portcim
+    struct port p = parse_portcim(line);
+    if (p.sector == 0)
+      BUGZ_LOG(fatal) << "portcim:  FAIL [" << line << "]";
+    else
+      BUGZ_LOG(trace) << "portcim: " << p;
+    galaxy.add_port(p);
+  } else {
+    // warpcim
+    // BUGZ_LOG(fatal) << "warpcim: [" << line << "]";
+    auto warps = split(line);
+    sector_warps sw;
+    for (auto const &w : warps) {
+      if (sw.sector == 0) {
+        sw.sector = stoi(w);
+      } else {
+        sw.add(stoi(w));
+      }
+    }
+    BUGZ_LOG(trace) << "warpcim: " << sw;
+    galaxy.add_warp(sw);
+  }
+}
+
+void Director::SL_thiefline(const std::string &line) {
+  size_t pos = line.find("Suddenly you're Busted!");
+  bool busted = pos != line.npos;
+  if (busted) {
+    BUGZ_LOG(fatal) << "set bust";
+    SL_parser = nullptr;
+  } else {
+    pos = line.find("(You realize the guards saw you last time!)");
+    if (pos != line.npos) SL_parser = nullptr;
+  }
+
+  // Are those the two ways to exit from this state?
+}
+void Director::SL_sectorline(const std::string &line) {
+  BUGZ_LOG(fatal) << "sectorline: [" << line << "]";
+  if (line.empty()) {
+    SL_parser = nullptr;
+  } else {
+    /*
+    sectorline: [Sector  : 926 in The Federation.]
+    sectorline: [Beacon  : FedSpace, FedLaw Enforced]
+    sectorline: [Ports   : Stargate Alpha I, Class 9 (Special) (StarDock)]
+    sectorline: [Traders : Civilian phil, w/ 30 ftrs,]
+    sectorline: [           in Star Stomper (Sverdlov Merchant Cruiser)]
+    sectorline: [Warps to Sector(s) :  70 - 441 - 575 - 600 - 629 - 711]
+    sectorline: [Warps to Sector(s) :  70 - (475) - 569]
+
+    What can we get from Traders : line?  Can we get if they are hostile
+    to us?  We need to respond to "is powering up weapons" ... We can
+    react faster then a person can!
+    "phil is powering up weapons systems!"
+
+    Also the auto-attack ones Ferrengi -- we need to auto-respond Retreat.
+
+    */
+    if (in(line, "Sector  :")) {
+      current_sector = stoi(line.substr(10));
+      BUGZ_LOG(warning) << "SECTOR: " << current_sector;
+    }
+    if (in(line, "Ports   :")) {
+      std::string port_class;
+      size_t pos = line.find(", Class ");
+      if (pos != std::string::npos) {
+        pos += 8;
+        int class_ = stoi(line.substr(pos));
+        BUGZ_LOG(fatal) << "PORT: " << class_;
+        galaxy.add_port(current_sector, class_);
+      }
+    }
+    if (in(line, "Warps to Sector(s) :")) {
+      std::string temp = line.substr(20);
+      replace(temp, " - ", " ");
+      // unexplored sectors ()
+      // Should I track these?
+      replace(temp, "(", "");
+      replace(temp, ")", "");
+      sector_warps sw;
+      auto warps = split(temp);
+      sw.sector = current_sector;
+      // what if there is only one warp?
+      for (auto const &w : warps) {
+        sw.add(stoi(w));
+      }
+      BUGZ_LOG(fatal) << "WARPS: " << sw;
+      galaxy.add_warp(sw);
+    }
+  }
+}
+
+void Director::SL_densityline(const std::string &line) {
+  BUGZ_LOG(fatal) << "densityline: [" << line << "]";
+  if (line.empty()) {
+    SL_parser = nullptr;
+    return;
+  }
+
+  /*
+  // Ensure this really is a density scan and not something else
+  if (!in(line, "Sector") || !in(line, "Warps") || !in(line, "NavHaz") ||
+      !in(line, "Anom")) {
+    BUGZ_LOG(fatal) << "densityline: Invalid line.";
+    SL_parser = nullptr;
+    return;
+  }
+  if (not galaxy.meta["density"]) {
+    galaxy.meta["density"] = YAML::Node();
+  }
+  */
+
+  /*
+   0         1   2                3  4     5 6    7      8     9     10   11 12
+  "Sector    55  ==>              0  Warps : 4    NavHaz :     0%    Anom : No"
+  "Sector ( 223) ==>              0  Warps : 3    NavHaz :     0%    Anom : No"
+  */
+  if (in(line, "==>")) {
+    std::string work = line;
+    replace(work, ":", "");
+    bool known = !in(work, "(");
+    replace(work, "(", "");
+    replace(work, ")", "");
+    replace(work, "%", "");
+    auto dense = split(work);
+    // Parse our data
+
+    sector_type sector = std::stoi(dense.at(1));
+    uint16_t density = std::stoi(dense.at(3));
+    uint16_t warps = std::stoi(dense.at(5));
+    uint16_t navhaz = std::stoi(dense.at(7));
+    bool anom = in(dense.at(9), "Yes");
+
+    struct density d = {sector, density, warps, navhaz, anom, known};
+    galaxy.dscan.add_scan(d);
+
+    // Commit data
+    BUGZ_LOG(warning) << "densityline: {sector=" << sector
+                      << " density=" << density << " warps=" << warps
+                      << " navhaz=" << navhaz << " anom=" << anom
+                      << " known=" << known << "}";
+    /*
+    if (galaxy.meta["density"][sector]) {
+      galaxy.meta["density"][sector] = YAML::Node();
+    }
+    */
+<<<<<<< HEAD
+    // what():  [json.exception.type_error.305] cannot use operator[] with a
+    // numeric argument with object
+
+    std::string sector_text = std::to_string(sector);
+    galaxy.meta["density"][sector_text]["density"] = density;
+    galaxy.meta["density"][sector_text]["warps"] = warps;
+    galaxy.meta["density"][sector_text]["navhaz"] = navhaz;
+    galaxy.meta["density"][sector_text]["anom"] = anom;
+    galaxy.meta["density"][sector_text]["known"] = known;
+=======
+   // what():  [json.exception.type_error.305] cannot use operator[] with a numeric argument with object
+   
+   std::string sector_text = std::to_string(sector);
+    galaxy.meta["_density"][sector_text]["density"] = density;
+    galaxy.meta["_density"][sector_text]["warps"] = warps;
+    galaxy.meta["_density"][sector_text]["navhaz"] = navhaz;
+    galaxy.meta["_density"][sector_text]["anom"] = anom;
+    galaxy.meta["_density"][sector_text]["known"] = known;
+>>>>>>> master
+    // Add a check to see if density is greater than 500
+    // Add datetime stamp
+  }
+}
+
+void Director::SL_portline(const std::string &line) {
+  /*
+  We take blank lines because we start at <Port>.
+  Otherwise, we trigger on computer port requests.
+
+  if (line.empty()) {
+    SL_parser = nullptr;
+    return;
+  }
+  */
+  /*
+  SL: [ Items     Status  Trading % of max OnBoard]
+  SL: [ -----     ------  ------- -------- -------]
+  SL: [Fuel Ore   Buying    3000    100%       0]
+  SL: [Organics   Buying    3000    100%       0]
+  SL: [Equipment  Buying    3000    100%       0]
+  SL: []
+  SL: [Commerce report for: 03:51:56 PM Mon Oct 24, 2033     You can buy:]
+  SL: [A  Cargo holds     :    650 credits / next hold                0]
+  SL: [B  Fighters        :    233 credits per fighter               75]
+  SL: [C  Shield Points   :    116 credits per point                100]
+  SL: []
+   */
+  // BUGZ_LOG(info) << "portline : " << line;
+  if (in(line, "%")) {
+    // size_t pos = line.find('%');
+    // if (pos != line.npos) {
+    // Ok, this is a valid portline
+    std::string work = line;
+    replace(work, "Fuel Ore", "Fuel");
+    auto parts = split(work);
+
+    if (parts[0] == "Items") return;
+
+    char c = tolower(parts[0][0]);
+    int pos;
+    char foe[4] = "foe";
+
+    for (pos = 0; pos < 3; ++pos) {
+      if (c == foe[pos]) break;
+    }
+    int amount = stoi(parts[2]);
+    int percent = stoi(parts[3]);
+
+    // update port
+    auto port = galaxy.ports.find(current_sector);
+    if (port != galaxy.ports.end()) {
+      port->second.amount[pos] = amount;
+      port->second.percent[pos] = percent;
+    }
+
+    /*
+    BUGZ_LOG(fatal) << "portline split:";
+    for (auto const p : parts) {
+      BUGZ_LOG(fatal) << p;
+    }
+    */
+
+    // Here's the end:
+    if (parts[0] == "Equipment") SL_parser = nullptr;
+
+    // BUGZ_LOG(fatal) << "portline split : [" << parts << "]";
+  }
+}
+
+void Director::SL_warpline(const std::string &line) {
+  if (line.empty()) {
+    SL_parser = nullptr;
+    return;
+  }
+
+  // process warp line
+  BUGZ_LOG(fatal) << "warpline: [" << line << "]";
+}
+
+void Director::SL_infoline(const std::string &line) {
+  static int state;
+
+  if (line == "<Info>") {
+    state = 0;
+    galaxy.meta["info"] = json();
+  }
+  if (line.empty()) {
+    ++state;
+    if (state == 2) {
+      SL_parser = nullptr;
+
+      // clear out the existing ship data
+      galaxy.meta["ship"] = json();
+      // process the parsed information in meta["info"]
+      if (galaxy.meta["info"].contains("Total Holds")) {
+        std::string work = galaxy.meta["info"]["Total Holds"];
+        replace(work, "Fuel Ore", "Fuel");
+        auto parts = split(work, " - ");
+        int total_holds = stoi(parts[0]);
+        BUGZ_LOG(fatal) << "total holds: " << total_holds;
+        auto contents = split(parts[1]);
+
+        for (auto const &hold : contents) {
+          auto hold_amount = split(hold, "=");
+          BUGZ_LOG(fatal) << hold_amount[0] << " with " << hold_amount[1];
+          std::string key = hold_amount[0];
+          str_tolower(key);
+          // equipment = e
+          // organics = o
+          // fuel = f
+          // colonists = c
+          // empty = empty
+          if (key != "empty") {
+            key = key[0];
+          }
+          galaxy.meta["ship"]["holds"][key] = stoi(hold_amount[1]);
+        }
+        galaxy.meta["ship"]["holds"]["total"] = total_holds;
+      }
+
+      if (galaxy.meta["info"].contains("Turns to Warp")) {
+        // Why is this saying that this is a string?
+        int warp_turns = sstoi(json_str(galaxy.meta["info"]["Turns to Warp"]));
+        BUGZ_LOG(fatal) << "Turns to Warp: " << warp_turns;
+        galaxy.meta["ship"]["warp_turns"] = warp_turns;
+      }
+
+      if (galaxy.meta["info"].contains("LongRange Scan")) {
+        std::string scanner_text =
+            galaxy.meta["info"]["LongRange Scan"].get<std::string>();
+        char scanner = scanner_text[0];
+        BUGZ_LOG(fatal) << "Scanner: " << scanner;
+        galaxy.meta["ship"]["scanner"] = scanner;
+      }
+
+      // turns isn't ship specific
+      if (galaxy.meta["info"].contains("Turns left")) {
+        // OR this could be "Unlimited" !!!
+        std::string text = galaxy.meta["info"]["Turns left"];
+        if (text == "Unlimited") {
+          galaxy.meta["turns"] = -1;
+        } else {
+          int turns =
+              stoi(text);  // galaxy.meta["info"]["Turns left"].as<int>();
+          BUGZ_LOG(fatal) << "Turns left: " << turns;
+          galaxy.meta["turns"] = turns;
+        }
+      }
+
+      if (galaxy.meta["info"].contains("Current Sector")) {
+        int sector = sstoi(json_str(galaxy.meta["info"]["Current Sector"]));
+        BUGZ_LOG(fatal) << "Sector: " << sector;
+        // it should already be sector ...
+        current_sector = sector;
+      }
+
+      if (galaxy.meta["info"].contains("Credits")) {
+        std::string credit_text = galaxy.meta["info"]["Credits"];
+        replace(credit_text, ",", "");
+        int credits = stoi(credit_text);
+        galaxy.meta["credits"] = credits;
+        BUGZ_LOG(fatal) << "Credits: " << credits;
+      }
+    }
+    return;
+  }
+
+  // info to parse:
+  size_t pos = line.find(" : ");
+  if ((!endswith(line, " : ")) && (pos != line.npos)) {
+    std::string key = line.substr(0, pos);
+    // Ferrengi ships don't have date built
+    std::string value = line.substr(pos + 3);
+    trim(key);
+    trim(value);
+    galaxy.meta["info"][key] = value;
+    BUGZ_LOG(fatal) << "Info: " << key << " : " << value;
+  }
+  // [Corp           # 1, Galaxy Stomping, Inc.]
+  if (startswith(line, "Corp  ")) {
+    pos = line.find(", ");
+    if (pos != line.npos) {
+      galaxy.meta["info"]["Corp"] = line.substr(pos + 2);
+    }
+  }
+}
+
+void Director::SL_computer_portline(const std::string &line) {
+  if (startswith(line, "What sector is the port in?"))
+    computer_port_done = false;
+
+  if (line == "I have no information about a port in that sector.") {
+    computer_port_sector = 0;
+    SL_parser = nullptr;
+    return;
+  }
+
+  if (!computer_port_done) {
+    if (in(line, "Fuel Ore")) computer_port_done = true;
+    if (in(line, "Cargo holds")) {
+      // If I want to scan the class type 0 ports:
+      // computer_port_done = true;
+      // otherwise:
+      SL_parser = nullptr;
+      return;
+    }
+  }
+
+  if (computer_port_done) {
+    if (line.empty()) {
+      SL_parser = nullptr;
+      return;
+    }
+
+    // scan for items of interest
+    // SL: [Fuel Ore   Buying     810    100%       0]
+    // SL: [Organics   Buying     856     57%       0]
+    // SL: [Equipment  Selling    922     44%       0]
+    std::string work = line;
+    replace(work, "Fuel Ore", "Fuel");
+    replace(work, "%", "");
+    auto parts = split(work);
+
+    char c = tolower(parts[0][0]);
+    int pos;
+    char foe[4] = "foe";
+
+    for (pos = 0; pos < 3; ++pos) {
+      if (c == foe[pos]) break;
+    }
+
+    int amount = stoi(parts[2]);
+    int percent = stoi(parts[3]);
+
+    // update port
+    auto port = galaxy.ports.find(computer_port_sector);
+    if (port != galaxy.ports.end()) {
+      BUGZ_LOG(info) << "COM PORT " << computer_port_sector << " " << c << " "
+                     << amount << " " << percent;
+      port->second.amount[pos] = amount;
+      port->second.percent[pos] = percent;
+    }
+  }
+}
+
+void Director::SL_planetline(const std::string &line) {
+  if (line == "Corporate command [TL=00:00:00]:[344] (?=Help)? Q" ||
+      line == "Computer command [TL=00:00:00]:[344] (?=Help)? Q") {
+    SL_parser = nullptr;
+    return;
+  }
+
+  if (in(line, "Class ") &&
+      (in(line, "Level ") || (endswith(line, "No Citadel")))) {
+    int level;
+    char c;
+    sector_type sector;
+    int number;
+    std::string name;
+    std::string work = line;
+    size_t pos = work.find('#');
+    std::string temp = work.substr(0, pos);
+    trim(temp);
+    sector = sstoi(temp);
+    work = work.substr(pos + 1);
+    number = sstoi(work);
+    pos = work.find(' ');
+    work = work.substr(pos + 1);
+    trim(work);
+    if (endswith(work, "No Citadel")) {
+      level = 0;
+    } else {
+      pos = work.rfind("Level ");
+      temp = work.substr(pos + 6);
+      level = sstoi(temp);
+    }
+    pos = work.rfind("Class ");
+    temp = work.substr(pos + 6);
+    c = temp[0];
+    work = work.substr(0, pos);
+    trim(work);
+    name = work;
+    BUGZ_LOG(warning) << (int)sector << " # " << number << " Class " << c
+                      << " Level " << level << " name: [" << name << "]";
+    planet p{sector, number, level, name, c};
+    galaxy.planets[number] = p;
+  }
+
+  /*
+  SL: [   344   #4    Enjoy the Silence        Class M, Earth Type Level 3] SL:
+  [   ---   (4M)            1T   64   25   14T   693   277         2T 10M] SL: [
+  344   #5    High There!              Class L, Mountainous          Level 2]
+  SL: [   ---   (5M)            2T    0    6   25T   100   112         2T 6M]
+  ...
+  SL: [   ---   (9M)            3T   64   31   39T   793   389         4T ---]
+  */
+}

+ 2 - 1
director.h

@@ -21,7 +21,8 @@ class Director {
   std::shared_ptr<Dispatch> chain;
 
   StringFunc to_client;
-  StringFunc to_server;
+  // StringFunc to_server;
+  std::function<void (const std::string &, const std::string &)> to_server;
   // void Session::post(notifyFunc nf)
   std::function<void(notifyFunc)> post;
 

+ 48 - 16
dispatchers.cpp

@@ -7,15 +7,30 @@
 #include "logging.h"
 #include "utils.h"
 
-Dispatch::Dispatch(Director &d) : director{d} { aborted = false; };
+Dispatch::Dispatch(Director &d, const char *called) : director{d} {
+  aborted = false;
+  if (called == nullptr)
+    name = "Dispatch";
+  else
+    name = called;
+};
 Dispatch::~Dispatch(){};
 
-void Dispatch::to_server(const std::string &send) { director.to_server(send); }
+void Dispatch::deactivate(void) {
+  notify();
+}
+
+void Dispatch::to_server(const std::string &send) { director.to_server(send, name); }
 void Dispatch::to_client(const std::string &send) { director.to_client(send); }
 const std::string &Dispatch::get_prompt(void) {
   return director.current_prompt;
 }
 
+void Dispatch::activate_chain(std::shared_ptr<Dispatch> new_chain) {
+  director.chain = new_chain;
+  director.chain->activate();
+}
+
 void Dispatch::setNotify(notifyFunc nf) { notify_ = nf; }
 
 void Dispatch::notify(void) {
@@ -57,9 +72,11 @@ void Dispatch::client_input(const std::string &input) {
   deactivate();
 }
 
-InputDispatch::InputDispatch(Director &d) : Dispatch(d) {
+InputDispatch::InputDispatch(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "InputDispatch()";
   numeric = false;
+  if (called == nullptr) name = "Input";
 }
 
 InputDispatch::~InputDispatch() { BUGZ_LOG(warning) << "~InputDispatch()"; }
@@ -156,8 +173,10 @@ void InputDispatch::client_input(const std::string &cinput) {
  *
  */
 
-MenuDispatch::MenuDispatch(Director &d) : Dispatch(d) {
+MenuDispatch::MenuDispatch(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "MenuDispatch()";
+  if (called == nullptr) name = "Menu";
 }
 
 MenuDispatch::~MenuDispatch() { BUGZ_LOG(warning) << "~MenuDispatch()"; }
@@ -388,7 +407,10 @@ void MenuDispatch::client_input(const std::string &cinput) {
   }
 }
 
-CIMDispatch::CIMDispatch(Director &d) : Dispatch(d) {}
+CIMDispatch::CIMDispatch(Director &d, const char *called)
+    : Dispatch(d, called) {
+  if (called == nullptr) name = "CIM";
+}
 
 void CIMDispatch::activate(void) { count = 0; }
 
@@ -411,9 +433,11 @@ void CIMDispatch::server_line(const std::string &line,
   }
 }
 
-MoveDispatch::MoveDispatch(Director &d) : Dispatch(d) {
+MoveDispatch::MoveDispatch(Director &d, const char * called) : Dispatch(d, called) {
   BUGZ_LOG(warning) << "MoveDispatch()";
   use_express = false;
+  if (called == nullptr)
+    name = "Move";
 }
 
 MoveDispatch::~MoveDispatch() { BUGZ_LOG(warning) << "~MoveDispatch()"; }
@@ -456,7 +480,10 @@ void MoveDispatch::activate(void) {
   at_destination.append(std::to_string(move_to));
 }
 
-void MoveDispatch::deactivate(void) { notify(); }
+void MoveDispatch::deactivate(void) { 
+  BUGZ_LOG(fatal) << "MoveDispatch::deactivate() " << success;
+  notify(); 
+}
 
 // optional here
 void MoveDispatch::server_line(const std::string &line,
@@ -616,8 +643,8 @@ void MoveDispatch::server_prompt(const std::string &prompt) {
     }
   } else if (state == 3) {
     if (at_command_prompt(prompt)) {
-      // this happens when the sector is adject to current_sector.
-      BUGZ_LOG(fatal) << "Are we there yet?";
+      // this happens when the sector is adjcent to current_sector.
+      BUGZ_LOG(fatal) << "Are we there yet? / Adjacent sector.";
       success = true;
       deactivate();
       return;
@@ -689,10 +716,12 @@ void MoveDispatch::client_input(const std::string &input) {
   deactivate();
 }
 
-TraderDispatch::TraderDispatch(Director &d) : Dispatch(d) {
+TraderDispatch::TraderDispatch(Director &d, const char * called) : Dispatch(d, called) {
   BUGZ_LOG(fatal) << "TraderDispatch()";
   state = 0;
   success = false;
+  if (called == nullptr)
+    name = "Trader";
   director.galaxy.meta["help"]["stop_percent"] =
       "ScriptTrader stop trading if below this percent.";
   director.galaxy.meta["help"]["trade_end_empty"] =
@@ -720,7 +749,7 @@ void TraderDispatch::activate(void) {
 
   /*
   1 = Info
-  
+
   */
 
   // Ok, what do we do first here?
@@ -1190,14 +1219,15 @@ void TraderDispatch::server_prompt(const std::string &prompt) {
       }
 
       if (active_port == port[0]) {
-        if (port[0] == 0) {
-          deactivate();
-          return;
-        }
         active_port = port[1];
       } else
         active_port = port[0];
 
+      if (active_port == 0) {
+        deactivate();
+        return;
+      }
+
       // Is target port burnt?
       auto pos = director.galaxy.ports.find(active_port);
       bool burnt = false;
@@ -1246,8 +1276,10 @@ void TraderDispatch::client_input(const std::string &cinput) {
  * CoreDispatch:  This is an example class that does dispatch.
  * Copy this and make changes from there...
  */
-CoreDispatch::CoreDispatch(Director &d) : Dispatch(d) {
+CoreDispatch::CoreDispatch(Director &d, const char * called) : Dispatch(d, called) {
   BUGZ_LOG(warning) << "CoreDispatch()";
+  if (called == nullptr)
+    name = "Core";
 }
 
 void CoreDispatch::activate(void) {

+ 11 - 8
dispatchers.h

@@ -30,7 +30,8 @@ class Dispatch {
   notifyFunc notify_;
 
  public:
-  Dispatch(Director &);
+ std::string name;
+  Dispatch(Director &, const char * called=nullptr);
   virtual ~Dispatch();
   bool aborted;
   
@@ -38,12 +39,14 @@ class Dispatch {
   void notify(void);
 
   virtual void activate(void) = 0;
-  virtual void deactivate(void) = 0;
+  virtual void deactivate(void);
 
   const std::string &get_prompt(void);
   void to_server(const std::string &send);
   void to_client(const std::string &send);
 
+  void activate_chain(std::shared_ptr<Dispatch> new_chain);
+
   // default to chain calls
   void chain_client_input(const std::string &input);
   void chain_server_line(const std::string &line, const std::string &raw_line);
@@ -62,7 +65,7 @@ class Dispatch {
 class InputDispatch : public Dispatch {
  private:
  public:
-  InputDispatch(Director &);
+  InputDispatch(Director &, const char * called=nullptr);
   ~InputDispatch();
 
   std::string prompt;
@@ -91,7 +94,7 @@ class MenuDispatch : public Dispatch {
   std::string centered(int length, const std::string &);
 
  public:
-  MenuDispatch(Director &);
+  MenuDispatch(Director &, const char * called=nullptr);
   ~MenuDispatch();
 
   std::string menu_box_color;
@@ -116,7 +119,7 @@ class MenuDispatch : public Dispatch {
 
 class CIMDispatch : public Dispatch {
  public:
-  CIMDispatch(Director &);
+  CIMDispatch(Director &, const char * called=nullptr);
   int count;
   
   void activate(void) override;
@@ -126,7 +129,7 @@ class CIMDispatch : public Dispatch {
 
 class MoveDispatch : public Dispatch {
  public:
-  MoveDispatch(Director &);
+  MoveDispatch(Director &, const char * called=nullptr);
   ~MoveDispatch();
   
   sector_type move_to;
@@ -153,7 +156,7 @@ class MoveDispatch : public Dispatch {
 class TraderDispatch : public Dispatch {
  private:
  public:
-  TraderDispatch(Director &);
+  TraderDispatch(Director &, const char * called=nullptr);
   ~TraderDispatch();
 
   char foe[4] = "foe";
@@ -209,7 +212,7 @@ class TraderDispatch : public Dispatch {
 
 class CoreDispatch : public Dispatch {
  public:
-  CoreDispatch(Director &);
+  CoreDispatch(Director &, const char * called=nullptr);
 
   void activate(void) override;
   void deactivate(void) override;

+ 62 - 19
scripts.cpp

@@ -4,8 +4,10 @@
 
 #include "logging.h"
 
-ScriptTerror::ScriptTerror(Director &d) : Dispatch(d) {
+ScriptTerror::ScriptTerror(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "ScriptTerror()";
+  if (called == nullptr) name = "Terror";
   init();
 }
 
@@ -14,19 +16,19 @@ ScriptTerror::~ScriptTerror() { BUGZ_LOG(warning) << "~ScriptTerror()"; }
 void ScriptTerror::init(void) {
   BUGZ_LOG(fatal) << "ScriptTerror::init()";
 
-  move = std::make_shared<MoveDispatch>(director);
+  move = std::make_shared<MoveDispatch>(director, "TerrorMove");
   md = static_cast<MoveDispatch *>(&(*move));
   // setup notify functions for results/completion.
   md->setNotify([this]() { this->move_notify(); });
 
-  input = std::make_shared<InputDispatch>(director);
+  input = std::make_shared<InputDispatch>(director, "TerrorInput");
   id = static_cast<InputDispatch *>(&(*input));
   id->prompt = "Number of loops: ";
   id->max_length = 4;
   id->numeric = true;
   id->setNotify([this]() { this->input_notify(); });
 
-  trader = std::make_shared<TraderDispatch>(director);
+  trader = std::make_shared<TraderDispatch>(director, "TerrorTrader");
   td = static_cast<TraderDispatch *>(&(*trader));
   td->setNotify([this]() { this->trade_notify(); });
   BUGZ_LOG(fatal) << "ScriptTerror::init() completed.";
@@ -222,19 +224,21 @@ void ScriptTerror::trade_notify(void) {
   deactivate();
 }
 
-ScriptVoyager::ScriptVoyager(Director &d) : Dispatch(d) {
+ScriptVoyager::ScriptVoyager(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "ScriptVoyager()";
+  if (called == nullptr) name = "Voyager";
   init();
 }
 
 ScriptVoyager::~ScriptVoyager() { BUGZ_LOG(warning) << "~ScriptVoyager()"; }
 
 void ScriptVoyager::init(void) {
-  move = std::make_shared<MoveDispatch>(director);
+  move = std::make_shared<MoveDispatch>(director, "VoyagerMove");
   md = static_cast<MoveDispatch *>(&(*move));
   md->setNotify([this]() { this->move_notify(); });
 
-  input = std::make_shared<InputDispatch>(director);
+  input = std::make_shared<InputDispatch>(director, "VoyagerInput");
   id = static_cast<InputDispatch *>(&(*input));
   id->prompt = "Number of loops/tries: ";
   id->max_length = 5;
@@ -316,19 +320,21 @@ void ScriptVoyager::next(void) {
 
 void ScriptVoyager::server_prompt(const std::string &prompt) {}
 
-ScriptExplore::ScriptExplore(Director &d) : Dispatch(d) {
+ScriptExplore::ScriptExplore(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "ScriptExplore()";
+  if (called == nullptr) name = "Explore";
   init();
 }
 
 ScriptExplore::~ScriptExplore() { BUGZ_LOG(warning) << "~ScriptExplore()"; }
 
 void ScriptExplore::init() {
-  move = std::make_shared<MoveDispatch>(director);
+  move = std::make_shared<MoveDispatch>(director, "ExploreMove");
   md = static_cast<MoveDispatch *>(&(*move));
   md->setNotify([this]() { this->move_notify(); });
 
-  input = std::make_shared<InputDispatch>(director);
+  input = std::make_shared<InputDispatch>(director, "ExploreInput");
   id = static_cast<InputDispatch *>(&(*input));
   id->prompt = "Number of sectors to explore: ";
   id->max_length = 5;
@@ -580,22 +586,24 @@ void ScriptExplore::server_prompt(const std::string &prompt) {
   }
 }
 
-ScriptPlanet::ScriptPlanet(Director &d) : Dispatch(d) {
+ScriptPlanet::ScriptPlanet(Director &d, const char *called)
+    : Dispatch(d, called) {
   BUGZ_LOG(warning) << "ScriptPlanet()";
+  if (called == nullptr) name = "Planet";
   init();
 }
 
 ScriptPlanet::~ScriptPlanet() { BUGZ_LOG(warning) << "~ScriptPlanet()"; }
 
 void ScriptPlanet::init() {
-  move = std::make_shared<MoveDispatch>(director);
+  move = std::make_shared<MoveDispatch>(director, "PlanetMove");
   md = static_cast<MoveDispatch *>(&(*move));
   md->setNotify([this]() { this->move_notify(); });
   md->use_express = true;
-  trader = std::make_shared<TraderDispatch>(director);
+  trader = std::make_shared<TraderDispatch>(director, "PlanetTrader");
   td = static_cast<TraderDispatch *>(&(*trader));
   td->setNotify([this]() { this->trade_notify(); });
-  input = std::make_shared<InputDispatch>(director);
+  input = std::make_shared<InputDispatch>(director, "PlanetInput");
   id = static_cast<InputDispatch *>(&(*input));
   id->prompt = "Which planet would you like to upgrade => ";
   id->max_length = 3;
@@ -782,7 +790,7 @@ void ScriptPlanet::move_notify() {
 
 void ScriptPlanet::trade_notify() {
   director.chain = us;
-  
+
   if (td->success) {
     if (state == 13) {
       // Ok, we have what we came from, return to the planet.
@@ -962,10 +970,45 @@ void ScriptPlanet::server_prompt(const std::string &prompt) {
       // Ok, we're ready for the next step:
       // checking the resources.
 
+      // Need resources
+      current_product = -1;
+      for (int x = 0; x < 3; ++x) {
+        if (needs[x] > amount[x]) {
+          current_product = x;
+          break;
+        }
+      }
+
+      if (current_product == -1) {
+        // I think we have everything.
+        BUGZ_LOG(fatal) << "I think we've got it...";
+        to_server("CUY");
+        deactivate();
+        return;
+      } else {
+        // Ok, let's find where we need to go
+        current_buyfrom = director.galaxy.find_nearest_selling(
+            director.current_sector, current_product);
+        if (current_buyfrom == 0) {
+          to_client("We weren't able to locate a port selling.\n\r");
+          deactivate();
+          return;
+        }
+
+        to_server("Q");
+        state = 13;
+        md->move_to = current_buyfrom;
+        director.chain = move;
+        md->activate();
+        return;
+      }
+
+
       // ending here for now. (not in citadel)
-      to_server("Q");
-      deactivate();
-      return;
+      // to_server("Q");
+      // deactivate();
+      // return;
+      
     }
   } else if (state == 14) {
     if (prompt == "Land on which planet <Q to abort> ? ") {
@@ -1154,4 +1197,4 @@ SL: [ -------  ---------  ---------  ---------  ---------  ---------
   }
 }
 
-void ScriptPlanet::client_input(const std::string &input) { deactivate(); }
+void ScriptPlanet::client_input(const std::string &input) { deactivate(); }

+ 4 - 4
scripts.h

@@ -18,7 +18,7 @@ class ScriptTerror : public Dispatch {
   std::string old_trade_end_empty;
   
  public:
-  ScriptTerror(Director &);
+  ScriptTerror(Director &, const char *called = nullptr);
   ~ScriptTerror();
   int loops;
   int max_loops;
@@ -45,7 +45,7 @@ class ScriptVoyager : public Dispatch {
     void next(void);
     
   public:
-    ScriptVoyager(Director &);
+    ScriptVoyager(Director &, const char *called = nullptr);
     ~ScriptVoyager();
     int loops;
 
@@ -70,7 +70,7 @@ class ScriptExplore : public Dispatch {
     std::shared_ptr<Dispatch> us;
 
   public:
-    ScriptExplore(Director &);
+    ScriptExplore(Director &, const char *called = nullptr);
     ~ScriptExplore();
     int loops;
     bool infinite;
@@ -102,7 +102,7 @@ class ScriptPlanet : public Dispatch {
     std::shared_ptr<Dispatch> us;
     
   public:
-    ScriptPlanet(Director &);
+    ScriptPlanet(Director &, const char *called = nullptr);
     ~ScriptPlanet();
     int planet;
     int sector;

+ 24 - 9
session.cpp

@@ -37,7 +37,10 @@ Session::Session(boost::asio::ip::tcp::socket socket,
   if (CONFIG.contains("keepalive")) keepalive_secs = CONFIG["keepalive"];
 
   // Initialize the director
-  director.to_server = boost::bind(&Session::to_server, this, _1);
+  director.to_server = [this](const std::string &message, const std::string &source) {
+    this->to_server(message, source);
+    // boost::bind(&Session::to_server, this, _1, _2);
+  };
   director.to_client = boost::bind(&Session::to_client, this, _1, true);
   director.post = boost::bind(&Session::post, this, _1);
 
@@ -328,7 +331,7 @@ void Session::reset_prompt_timer(void) { prompt_timer_.cancel(); }
 void Session::on_server_prompt(const std::string &prompt,
                                const std::string &raw_prompt) {
   std::string temp = repr(prompt);
-  BUGZ_LOG(warning) << "SP: [" << temp << "]";
+  BUGZ_LOG(trace) << "SP: [" << temp << "]";
   director.server_prompt(prompt, raw_prompt);
   if (server_telnet) {
     std::string ayt = std::string((const char *)"\x00\xff\xfd\xf6", 4);
@@ -350,12 +353,12 @@ void Session::on_server_prompt(const std::string &prompt,
 
     if (prompt == ayt) {
       to_server(ayt_resp);
-      BUGZ_LOG(fatal) << "AYT?";
+      BUGZ_LOG(warning) << "AYT?";
       server_prompt.clear();
     }
     if (prompt == ayt2) {
       to_server(ayt2_resp);
-      BUGZ_LOG(fatal) << "AYT2??";
+      BUGZ_LOG(warning) << "AYT2??";
       server_prompt.clear();
       // let the user see what is happening...  We're done negotiating (I think)
       director.show_client = true;
@@ -411,9 +414,12 @@ void Session::server_read(void) {
           server_read();
         } else {
           if (!server_prompt.empty()) {
-            BUGZ_LOG(info) << "2C: [" << server_prompt << "]";
-            to_client(server_prompt);
-            server_prompt.clear();
+            // This re-displays the very last message received (before closing)
+            // Unfortunately, it repeats the last prompt.
+            // (I'm looking to display the message from the server // like is full.)
+            // BUGZ_LOG(trace) << "2C: [" << server_prompt << "]";
+            // to_client(server_prompt);
+            // server_prompt.clear();
           }
           BUGZ_LOG(warning) << "S: read_failed: socket.shutdown()";
           connected = false;
@@ -450,7 +456,7 @@ void Session::on_resolve(
 
 void Session::client_input(const std::string &input) {
   std::string temp = repr(input);
-  BUGZ_LOG(info) << "CI: [" << temp << "]";
+  BUGZ_LOG(trace) << "CI: [" << temp << "]";
   director.client_input(input);
 }
 
@@ -535,8 +541,17 @@ void Session::to_client(const std::string &message, bool log) {
 }
 
 void Session::to_server(const std::string &message) {
+  std::string empty;
+  to_server(message, empty);
+}
+
+void Session::to_server(const std::string &message, const std::string &source) {
   auto self(shared_from_this());
-  BUGZ_LOG(trace) << "2S: " << message;
+  if (source.empty())
+    BUGZ_LOG(trace) << "2S: " << message;
+  else
+    BUGZ_LOG(trace) << source << " 2S: " << message;
+
   boost::asio::async_write(
       server_, boost::asio::buffer(message),
       [this, self](boost::system::error_code ec, std::size_t /*length*/) {

+ 1 - 0
session.h

@@ -33,6 +33,7 @@ class Session : public std::enable_shared_from_this<Session> {
   const std::string &get_prompt(void);
   void set_prompt(const std::string &prompt);
   void to_client(const std::string &message, bool log = true);
+  void to_server(const std::string &message, const std::string &source);
   void to_server(const std::string &message);
 
   /*

+ 4 - 4
test-director.cpp

@@ -23,9 +23,9 @@ TEST(director, director_debugging) {
   Director dir;
   std::vector<std::string> client;
   std::vector<std::string> server;
-  dir.to_client = [&client](const std::string line) { client.push_back(line); };
-  dir.to_server = [&server](const std::string line) { server.push_back(line); };
-
+  dir.to_client = [&client](const std::string line) -> void { client.push_back(line); };
+  dir.to_server = [&server](const std::string &line, const std::string &source) -> void { server.push_back(line); };
+  // dir.to_server = [](const std::string &line, const std::string &source) -> void { server.push_back(line); };
   // activating the game gets this over to dir.galaxy.username.
   dir.username = "test";
 
@@ -78,7 +78,7 @@ TEST(director, director_parsing_density) {
   std::vector<std::string> client;
   std::vector<std::string> server;
   dir.to_client = [&client](const std::string line) { client.push_back(line); };
-  dir.to_server = [&server](const std::string line) { server.push_back(line); };
+  dir.to_server = [&server](const std::string &line, const std::string &source) { server.push_back(line); };
 
   dir.username = "test";
   std::string lines[] = {