UCIEndpoint.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. /*
  2. * Copyright (c) 2020, the SerenityOS developers.
  3. * Copyright (c) 2023, Tim Ledbetter <timledbetter@gmail.com>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include "UCIEndpoint.h"
  8. #include <AK/ByteBuffer.h>
  9. #include <AK/Debug.h>
  10. #include <LibCore/EventLoop.h>
  11. namespace Chess::UCI {
  12. Endpoint::Endpoint(NonnullOwnPtr<Core::File> in, NonnullOwnPtr<Core::File> out)
  13. : m_in_fd(in->fd())
  14. , m_in(Core::BufferedFile::create(move(in)).release_value_but_fixme_should_propagate_errors())
  15. , m_out(move(out))
  16. {
  17. set_in_notifier();
  18. }
  19. void Endpoint::send_command(Command const& command)
  20. {
  21. auto command_string = command.to_string().release_value_but_fixme_should_propagate_errors();
  22. dbgln_if(UCI_DEBUG, "{} Sent UCI Command: {}", class_name(), command_string);
  23. m_out->write_until_depleted(command_string.bytes()).release_value_but_fixme_should_propagate_errors();
  24. }
  25. void Endpoint::event(Core::Event& event)
  26. {
  27. switch (static_cast<Command::Type>(event.type())) {
  28. case Command::Type::UCI:
  29. return handle_uci();
  30. case Command::Type::Debug:
  31. return handle_debug(static_cast<DebugCommand const&>(event));
  32. case Command::Type::IsReady:
  33. return handle_uci();
  34. case Command::Type::SetOption:
  35. return handle_setoption(static_cast<SetOptionCommand const&>(event));
  36. case Command::Type::Position:
  37. return handle_position(static_cast<PositionCommand const&>(event));
  38. case Command::Type::Go:
  39. return handle_go(static_cast<GoCommand const&>(event));
  40. case Command::Type::Stop:
  41. return handle_stop();
  42. case Command::Type::Id:
  43. return handle_id(static_cast<IdCommand const&>(event));
  44. case Command::Type::UCIOk:
  45. return handle_uciok();
  46. case Command::Type::ReadyOk:
  47. return handle_readyok();
  48. case Command::Type::BestMove:
  49. return handle_bestmove(static_cast<BestMoveCommand const&>(event));
  50. case Command::Type::Info:
  51. return handle_info(static_cast<InfoCommand const&>(event));
  52. case Command::Type::Quit:
  53. return handle_quit();
  54. case Command::Type::UCINewGame:
  55. return handle_ucinewgame();
  56. default:
  57. Object::event(event);
  58. break;
  59. }
  60. }
  61. void Endpoint::custom_event(Core::CustomEvent& custom_event)
  62. {
  63. if (custom_event.custom_type() == EndpointEventType::UnexpectedEof)
  64. handle_unexpected_eof();
  65. }
  66. void Endpoint::set_in_notifier()
  67. {
  68. m_in_notifier = Core::Notifier::construct(m_in_fd.value(), Core::Notifier::Type::Read);
  69. m_in_notifier->on_activation = [this] {
  70. if (!m_in->can_read_line().release_value_but_fixme_should_propagate_errors()) {
  71. Core::EventLoop::current().post_event(*this, make<Core::CustomEvent>(EndpointEventType::UnexpectedEof));
  72. m_in_notifier->set_enabled(false);
  73. return;
  74. }
  75. auto buffer = ByteBuffer::create_zeroed(4096).release_value_but_fixme_should_propagate_errors();
  76. while (m_in->can_read_line().release_value_but_fixme_should_propagate_errors()) {
  77. auto line = m_in->read_line(buffer).release_value_but_fixme_should_propagate_errors().trim_whitespace();
  78. if (line.is_empty())
  79. continue;
  80. auto maybe_command = read_command(line);
  81. if (maybe_command.is_error()) {
  82. dbgln_if(UCI_DEBUG, "{} Error while parsing UCI command: {}, error: {}", class_name(), maybe_command.error(), line);
  83. if (on_command_read_error)
  84. on_command_read_error(move(line), maybe_command.release_error());
  85. continue;
  86. }
  87. Core::EventLoop::current().post_event(*this, maybe_command.release_value());
  88. }
  89. };
  90. }
  91. ErrorOr<NonnullOwnPtr<Command>> Endpoint::read_command(StringView line) const
  92. {
  93. dbgln_if(UCI_DEBUG, "{} Received UCI Command: {}", class_name(), line);
  94. if (line == "uci") {
  95. return UCICommand::from_string(line);
  96. } else if (line.starts_with("debug"sv)) {
  97. return DebugCommand::from_string(line);
  98. } else if (line.starts_with("isready"sv)) {
  99. return IsReadyCommand::from_string(line);
  100. } else if (line.starts_with("setoption"sv)) {
  101. return SetOptionCommand::from_string(line);
  102. } else if (line.starts_with("position"sv)) {
  103. return PositionCommand::from_string(line);
  104. } else if (line.starts_with("go"sv)) {
  105. return GoCommand::from_string(line);
  106. } else if (line.starts_with("stop"sv)) {
  107. return StopCommand::from_string(line);
  108. } else if (line.starts_with("id"sv)) {
  109. return IdCommand::from_string(line);
  110. } else if (line.starts_with("uciok"sv)) {
  111. return UCIOkCommand::from_string(line);
  112. } else if (line.starts_with("readyok"sv)) {
  113. return ReadyOkCommand::from_string(line);
  114. } else if (line.starts_with("bestmove"sv)) {
  115. return BestMoveCommand::from_string(line);
  116. } else if (line.starts_with("info"sv)) {
  117. return InfoCommand::from_string(line);
  118. } else if (line.starts_with("quit"sv)) {
  119. return QuitCommand::from_string(line);
  120. } else if (line.starts_with("ucinewgame"sv)) {
  121. return UCINewGameCommand::from_string(line);
  122. }
  123. return Error::from_string_literal("Unknown command");
  124. }
  125. };