director.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. #include "director.h"
  2. #include <boost/format.hpp>
  3. #include "boxes.h"
  4. #include "galaxy.h"
  5. #include "logging.h"
  6. #include "utils.h"
  7. Director::Director() {
  8. BUGZ_LOG(warning) << "Director::Director()";
  9. // active = false;
  10. game = 0; // not in a game
  11. // do everything proxy_deactivate does ...
  12. // proxy_deactivate();
  13. active = false;
  14. // reset everything back to good state
  15. talk_direct = true;
  16. show_client = true;
  17. count = 0;
  18. /*
  19. Setup StringFunc for SL_parser:
  20. Construct these once, rather then every single time we need them.
  21. */
  22. SF_cimline = [this](const std::string &s) { this->SL_cimline(s); };
  23. SF_sectorline = [this](const std::string &s) { this->SL_sectorline(s); };
  24. SF_portline = [this](const std::string &s) { this->SL_portline(s); };
  25. SF_warpline = [this](const std::string &s) { this->SL_warpline(s); };
  26. }
  27. Director::~Director() { BUGZ_LOG(warning) << "Director::~Director()"; }
  28. void Director::client_input(const std::string &input) {
  29. // If we're already active, don't try to activate.
  30. if (chain) {
  31. chain->client_input(input);
  32. return;
  33. }
  34. if (active) {
  35. if (input == "Q" || input == "q") proxy_deactivate();
  36. return;
  37. } else if (input == "\x1b" || input == "~") {
  38. std::string &prompt = current_prompt;
  39. BUGZ_LOG(trace) << "CI: ACTIVATE prompt shows: [" << prompt << "]";
  40. if (prompt == "Selection (? for menu): ") {
  41. to_client(
  42. "\n\rThere's not much we can do here. Activate in-game at a "
  43. "Command prompt.\n\r");
  44. to_client(current_raw_prompt);
  45. return;
  46. }
  47. // easter-eggs:
  48. if (prompt == "Enter your choice: ") {
  49. to_client(
  50. "\n\r\x1b[1;36mI'd choose \x1b[1;37m`T`\x1b[1;36m, but "
  51. "that's how I was coded.\n\r");
  52. to_client(current_raw_prompt);
  53. return;
  54. }
  55. // easter-egg
  56. if (prompt == "[Pause]") {
  57. to_client(" \x1b[1;36mPAWS\x1b[0m\n\r");
  58. to_client(current_raw_prompt);
  59. return;
  60. }
  61. if (prompt == "Planet command (?=help) [D] ") {
  62. // future: Activate at planet menu ?
  63. return;
  64. }
  65. //
  66. // The command prompt that we're looking for:
  67. //
  68. // "Command [TL=00:00:00]:[242] (?=Help)? : "
  69. // the time, and the sector number vary...
  70. if (startswith(prompt, "Command [")) {
  71. // if (prompt.substr(0, 9) == "Command [") {
  72. // int len = prompt.length();
  73. if (endswith(prompt, "] (?=Help)? : ")) {
  74. // if (prompt.substr(len - 14) == "] (?=Help)? : ") {
  75. proxy_activate();
  76. return;
  77. }
  78. }
  79. }
  80. // Ok...
  81. if (talk_direct) to_server(input);
  82. /*
  83. if (emit_client_input)
  84. emit_client_input(line);
  85. */
  86. }
  87. void Director::server_line(const std::string &line,
  88. const std::string &raw_line) {
  89. // check for if we entered game/left game
  90. if (line.find("TradeWars Game Server ") != std::string::npos) {
  91. to_client("\rTradeWars Proxy v2++ READY (~ or ESC to activate)\n\r");
  92. if (game) {
  93. // TODO: Save galaxy data
  94. }
  95. game = 0;
  96. // reset "active game" -- we're at the TWGS main menu
  97. }
  98. if (line.find("Selection (? for menu): ") != std::string::npos) {
  99. char ch = line[line.length() - 1];
  100. if (ch >= 'A' && ch < 'Q') {
  101. game = ch;
  102. BUGZ_LOG(warning) << "GAME " << game << " activated!";
  103. // TODO: Load game data
  104. }
  105. // not needed (handled by above Game Server check).
  106. if (ch == 'Q') {
  107. if (game) {
  108. // TODO: Save galaxy data
  109. }
  110. game = 0;
  111. }
  112. }
  113. if (game) {
  114. // in-game parsing here.
  115. /*
  116. ____ _ _
  117. / ___| ___ _ ____ _____ _ __ | | (_)_ __ ___
  118. \___ \ / _ \ '__\ \ / / _ \ '__| | | | | '_ \ / _ \
  119. ___) | __/ | \ V / __/ | | |___| | | | | __/
  120. |____/ \___|_| \_/ \___|_| |_____|_|_| |_|\___|
  121. ____ _
  122. | _ \ __ _ _ __ ___(_)_ __ __ _
  123. | |_) / _` | '__/ __| | '_ \ / _` |
  124. | __/ (_| | | \__ \ | | | | (_| |
  125. |_| \__,_|_| |___/_|_| |_|\__, |
  126. |___/
  127. This is where all of the server lines are gleaned for all the
  128. information that we can get out of them.
  129. */
  130. if (startswith(line, " Items Status Trading % of max OnBoard"))
  131. SL_parser = SF_portline;
  132. if (startswith(line, "Sector : ")) SL_parser = SF_sectorline;
  133. if (line == ": ") SL_parser = SF_cimline;
  134. }
  135. if (SL_parser) {
  136. SL_parser(line);
  137. }
  138. /*
  139. if (emit_server_line) {
  140. emit_server_line(line);
  141. }
  142. */
  143. if (chain) {
  144. chain->server_line(line, raw_line);
  145. }
  146. }
  147. void Director::server_prompt(const std::string &prompt,
  148. const std::string &raw_prompt) {
  149. current_prompt = prompt;
  150. current_raw_prompt = raw_prompt;
  151. if (game) {
  152. // in-game parsing here.
  153. if (startswith(prompt, "Command [") && endswith(prompt, "] (?=Help)? : ")) {
  154. std::string sector_text;
  155. size_t before, after;
  156. before = prompt.find("]:[") + 3;
  157. after = prompt.find("] (?=Help)");
  158. sector_text = prompt.substr(before, after - before);
  159. current_sector = stoi(sector_text);
  160. BUGZ_LOG(fatal) << "Sector: " << sector_text;
  161. }
  162. }
  163. /*
  164. if (emit_server_prompt)
  165. emit_server_prompt(prompt);
  166. */
  167. if (chain) chain->server_prompt(prompt);
  168. }
  169. void Director::proxy_activate(void) {
  170. active = true; // yes, set keep-alive timer.
  171. to_server(" "); // start keep-alive timer.
  172. // set other values we need
  173. talk_direct = false;
  174. show_client = false;
  175. /*
  176. Wait a minute .. this might be confusing.
  177. Shouldn't I send them the current prompt?
  178. Just in case we abort in the middle of something?!?
  179. */
  180. old_prompt = current_prompt;
  181. old_raw_prompt = current_raw_prompt;
  182. to_client("\x1b[0m\n\r");
  183. /*
  184. ╔══════════════════════════════╗
  185. ║ TradeWars Proxy Active ║
  186. ╚══════════════════════════════╝
  187. -=>
  188. */
  189. Boxes box(30, 1, true);
  190. box.boxcolor = "\x1b[1;33;44m";
  191. box.textcolor = "\x1b[1;33;44m";
  192. to_client(box.top());
  193. std::string output = " TradeWars Proxy \x1b[5mActive\x1b[0;1;33;44m ";
  194. to_client(box.row(output));
  195. to_client(box.bottom());
  196. std::shared_ptr<Dispatch> menu = std::make_shared<MenuDispatch>(*this);
  197. chain = menu;
  198. MenuDispatch *md = static_cast<MenuDispatch *>(&(*menu));
  199. md->menu_box_color = "\x1b[1;33;44m";
  200. md->menu_text_color = "\x1b[1;37;44m";
  201. md->menu_title = "Proxy Menu";
  202. md->menu_options_color = "\x1b[1;36;40m";
  203. md->menu_prompt =
  204. "\x1b[0;31;40m\xdb\xb2\xb1\xb0 \x1b[31;40mRED "
  205. "\x1b[32;40mGREEN\x1b[30;42m\xdb\xb2\xb1\xb0 \x1b[0m : ";
  206. md->lazy = true;
  207. md->menu = {{"A", "Apple"}, {"B", "Blue"}, {"R", "Rabbit"}, {"Z", "ZOOO!"}};
  208. md->setNotify([this]() { this->menu_choice(); });
  209. menu->activate();
  210. /*
  211. // Using InputDispatch -- and see have_input
  212. std::shared_ptr<Dispatch> readline = std::make_shared<InputDispatch>(*this);
  213. chain = readline;
  214. InputDispatch *id = static_cast<InputDispatch *>(&(*readline));
  215. id->prompt = "\x1b[0m \x1b[1;33;44m-=>\x1b[0m \x1b[1;37;44m";
  216. id->max_length = 15;
  217. id->setNotify([this]() { this->have_input(); });
  218. readline->activate();
  219. */
  220. }
  221. void Director::menu_choice(void) {
  222. MenuDispatch *md = dynamic_cast<MenuDispatch *>(&(*chain));
  223. if (md) {
  224. if (md->input.empty()) {
  225. to_client("Menu aborted.\n\r");
  226. proxy_deactivate();
  227. return;
  228. } else {
  229. std::string text = str(
  230. boost::format("Back from Menu [%1%] was selected.\n\r") % md->input);
  231. to_client(text);
  232. md->activate();
  233. }
  234. }
  235. }
  236. void Director::have_input(void) {
  237. ++count;
  238. InputDispatch *id = dynamic_cast<InputDispatch *>(&(*chain));
  239. if (id) {
  240. std::string output =
  241. str(boost::format("Your Input (%2%): [%1%]\n\r") % id->input % count);
  242. to_client("\x1b[0m");
  243. to_client(output);
  244. } else {
  245. proxy_deactivate();
  246. return;
  247. }
  248. if (count > 3) {
  249. proxy_deactivate();
  250. } else {
  251. chain->activate();
  252. }
  253. }
  254. void Director::proxy_deactivate(void) {
  255. active = false;
  256. // reset everything back to good state
  257. talk_direct = true;
  258. show_client = true;
  259. chain.reset();
  260. to_client("\n\r");
  261. to_client(current_raw_prompt);
  262. }
  263. /*
  264. Server Line Parsing Routines
  265. */
  266. void Director::SL_cimline(const std::string &line) {
  267. if (line == ": ENDINTERROG") {
  268. SL_parser = nullptr;
  269. return;
  270. }
  271. if (line == ": ") {
  272. // do I need to do anything special here for this?
  273. // Maybe -- We would save special ports that don't show up (StarDock/Special) before.
  274. // We don't know (at this point) if this is warps or ports.
  275. return;
  276. }
  277. if (line.empty()) {
  278. SL_parser = nullptr;
  279. return;
  280. }
  281. // parse cimline
  282. // size_t pos = line.find('%');
  283. // std::string work = line;
  284. // if (pos == line.npos) {
  285. if (in(line, "%")) {
  286. // warpcim
  287. BUGZ_LOG(fatal) << "warpcim: [" << line << "]";
  288. auto warps = split(line);
  289. sector_warps sw;
  290. for (auto const &w : warps) {
  291. if (sw.sector == 0) {
  292. sw.sector = stoi(w);
  293. } else {
  294. sw.add(stoi(w));
  295. }
  296. }
  297. BUGZ_LOG(fatal) << "warpcim: " << sw;
  298. } else {
  299. // portcim
  300. struct port p = parse_portcim(line);
  301. if (p.sector == 0)
  302. BUGZ_LOG(fatal) << "portcim: FAIL [" << line << "]";
  303. else
  304. BUGZ_LOG(fatal) << "portcim: " << p;
  305. }
  306. }
  307. void Director::SL_thiefline(const std::string &line) {
  308. size_t pos = line.find("Suddenly you're Busted!");
  309. bool busted = pos != line.npos;
  310. if (busted) {
  311. BUGZ_LOG(fatal) << "set bust";
  312. SL_parser = nullptr;
  313. } else {
  314. pos = line.find("(You realize the guards saw you last time!)");
  315. if (pos != line.npos) SL_parser = nullptr;
  316. }
  317. // Are those the two ways to exit from this state?
  318. }
  319. void Director::SL_sectorline(const std::string &line) {
  320. BUGZ_LOG(fatal) << "sectorline: [" << line << "]";
  321. if (line.empty()) {
  322. SL_parser = nullptr;
  323. } else {
  324. /*
  325. sectorline: [Sector : 926 in The Federation.]
  326. sectorline: [Beacon : FedSpace, FedLaw Enforced]
  327. sectorline: [Ports : Stargate Alpha I, Class 9 (Special) (StarDock)]
  328. sectorline: [Traders : Civilian phil, w/ 30 ftrs,]
  329. sectorline: [ in Star Stomper (Sverdlov Merchant Cruiser)]
  330. sectorline: [Warps to Sector(s) : 70 - 441 - 575 - 600 - 629 - 711]
  331. sectorline: [Warps to Sector(s) : 70 - (475) - 569]
  332. */
  333. if (in(line, "Sector :")) {
  334. current_sector = stoi(line.substr(10));
  335. BUGZ_LOG(warning) << "SECTOR: " << current_sector;
  336. }
  337. if (in(line, "Ports :")) {
  338. std::string port_class;
  339. size_t pos = line.find(", Class ");
  340. if (pos != std::string::npos) {
  341. pos += 8;
  342. int class_ = stoi(line.substr(pos));
  343. BUGZ_LOG(fatal) << "PORT: " << class_;
  344. }
  345. }
  346. if (in(line, "Warps to Sector(s) :")) {
  347. std::string temp = line.substr( 20 );
  348. replace(temp, " - ", " ");
  349. // unexplored sectors ()
  350. // Should I track these?
  351. replace(temp, "(", "");
  352. replace(temp, ")", "");
  353. sector_warps sw;
  354. auto warps = split(temp);
  355. sw.sector = current_sector;
  356. for( auto const &w : warps) {
  357. sw.add(stoi(w));
  358. }
  359. BUGZ_LOG(fatal) << "WARPS: " << sw;
  360. }
  361. }
  362. }
  363. void Director::SL_portline(const std::string &line) {
  364. if (line.empty()) {
  365. SL_parser = nullptr;
  366. return;
  367. }
  368. BUGZ_LOG(info) << "portline : " << line;
  369. if (in(line, "%")) {
  370. // size_t pos = line.find('%');
  371. // if (pos != line.npos) {
  372. // Ok, this is a valid portline
  373. std::string work = line;
  374. replace(work, "Fuel Ore", "Fuel");
  375. auto parts = split(work);
  376. BUGZ_LOG(fatal) << "portline split:";
  377. for( auto const p : parts) {
  378. BUGZ_LOG(fatal) << p;
  379. }
  380. // BUGZ_LOG(fatal) << "portline split : [" << parts << "]";
  381. }
  382. }
  383. void Director::SL_warpline(const std::string &line) {
  384. if (line.empty()) {
  385. SL_parser = nullptr;
  386. return;
  387. }
  388. // process warp line
  389. BUGZ_LOG(fatal) << "warpline: [" << line << "]";
  390. }