Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/control-socket.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ Error codes: `bad_request`, `unknown_command`, `wrong_scene`, `busy`,
| `hover-ui` (`x`, `y`) | move the pointer to a window pixel with no button, as physical motion: RmlUi hover and the game's own pointer (native hover, tooltips) both follow it |
| `drag-ui` (`x`, `y`, `to_x`, `to_y`, `button`) | press at one window pixel, move to the other over eight frames with the button held, release there |
| `type` (`text`, `enter`) | deliver committed UTF-8 to the focused field; optional boolean `enter` submits on a later frame |
| `console` (`text`) | run one `$` developer command (`$fps 60`, …) as if typed into the chat box, without opening it, so nothing typed shows in the frame; `bad_request` when the text is not a `$` command the client knows, or its arguments are missing or malformed |
| `ui` | the open windows by name — `inventory`, `inventory_extension`, `character`, `trade`, `storage`, `storage_extension`, `mix`, `npc_shop`, `lucky_item`, `chat_input`, `party`, `command`, `my_shop`, `purchase_shop`, `npc_quest` (the quest dialog of Sebina, Marlon and Devin), and `message_box` while a dialog waits for Enter or Esc — and the window pixels of the named elements that are shown: `trade.confirm`, `trade.zen`, `inventory.repair`, `inventory.my_shop`, `npc_shop.repair`, `npc_shop.repair_all`, `my_shop.title`, `my_shop.open`, `my_shop.close`, `command.trade`, `command.purchase`, `command.party`, `npc_quest.answer.0` and on (the rows of the quest dialog's answers), `npc_quest.complete`, `npc_quest.close`, `character.stat.strength` (`agility`, `vitality`, `energy`, `command`: the character window's "+" buttons, while there are level-up points) |
| `slot-pixel` (`grid`, `slot`) | the window pixel of a slot's square: `inventory` and `equipment` (the slot numbers `state` reports), `trade`, `trade_partner`, `storage`, `mix`, `npc_shop`, and the personal shops `my_shop` and `purchase_shop` (slots 204 and up, as `state` and the server number them); `not_open` while that window is closed, `bad_request` for a slot the grid does not have |
| `login` (`account`, `password`, `server`) | server selection, credentials, character list |
Expand Down
3 changes: 3 additions & 0 deletions src/source/App/Control/ControlCommands.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ std::string ClickUi(const Request& request, std::unique_ptr<Act>& act);
std::string HoverUi(const Request& request, std::unique_ptr<Act>& act);
std::string DragUi(const Request& request, std::unique_ptr<Act>& act);
std::string Type(const Request& request, std::unique_ptr<Act>& act);
// Runs one `$` developer command as if typed into the chat box, without
// opening it, so nothing typed shows up in the frame.
std::string Console(const Request& request, std::unique_ptr<Act>& act);

// Ui family: where the open item windows are.
std::string Ui(const Request& request, std::unique_ptr<Act>& act);
Expand Down
31 changes: 31 additions & 0 deletions src/source/App/Control/ControlCommandsUi.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "stdafx.h"
#include "App/Control/ControlCommands.h"

#include "Core/Text/Utf8.h"
#include "Core/Utilities/Log/muConsoleDebug.h"
#include "UI/Core/WindowAccess.h"
#include "UI/Core/WindowSystem.h"
#include "UI/Dialogs/GenericConfirmDialog.h"
Expand All @@ -13,6 +15,7 @@

#include "json.hpp"

#include <exception>
#include <optional>
#include <string>
#include <string_view>
Expand Down Expand Up @@ -290,4 +293,32 @@ std::string SlotPixel(const Request& request, std::unique_ptr<Act>&)
}
return SquarePixel(request, **square, slot);
}

std::string Console(const Request& request, std::unique_ptr<Act>& /*act*/)
{
std::string text;
if (!request.GetString("text", text) || text.empty() || text.front() != '$')
{
return EncodeError(request.EncodedId(), ErrorCode::BadRequest, "`text` is a `$` command, e.g. \"$fps 60\"");
}
bool known = false;
try
{
known = g_ConsoleDebug->CheckCommand(Core::Text::FromUtf8(text));
}
catch (const std::exception&)
{
// A command that parses its arguments with std::stof/substr throws on a missing or
// malformed one ("$fps" without a number); refuse it instead of ending the client.
return EncodeError(request.EncodedId(), ErrorCode::BadRequest,
"`" + text + "` is missing an argument or has a malformed one");
}
if (!known)
{
return EncodeError(request.EncodedId(), ErrorCode::BadRequest, "the client has no `$` command `" + text + "`");
}
nlohmann::json result;
result["text"] = text;
return EncodeResult(request.EncodedId(), result.dump());
}
} // namespace App::Control::Commands
1 change: 1 addition & 0 deletions src/source/App/Control/ControlDispatcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const std::vector<CommandEntry>& CommandTable()
{"screenshot", SceneRequirement::Any, &Commands::Screenshot},
{"hotkey", SceneRequirement::Any, &Commands::Hotkey},
{"type", SceneRequirement::Any, &Commands::Type},
{"console", SceneRequirement::Any, &Commands::Console},
{"click-ui", SceneRequirement::Any, &Commands::ClickUi},
{"hover-ui", SceneRequirement::Any, &Commands::HoverUi},
{"drag-ui", SceneRequirement::Any, &Commands::DragUi},
Expand Down
8 changes: 4 additions & 4 deletions src/source/App/Control/ControlProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,10 @@ const std::vector<std::string>& CommandNames()
// the whole client into that test. The dispatcher checks itself against
// this list instead (ControlDispatcher.cpp, CommandTable).
static const std::vector<std::string> names = {
"ping", "scene", "state", "nearby", "events", "wait-for", "screenshot", "login", "select-char", "logout",
"quit", "move", "warp", "teleport", "attack", "skill", "pickup", "use", "equip", "say",
"whisper", "party", "halt", "hotkey", "click-ui", "drag-ui", "hover-ui", "type", "ui", "slot-pixel",
"trade",
"ping", "scene", "state", "nearby", "events", "wait-for", "screenshot", "login",
"select-char", "logout", "quit", "move", "warp", "teleport", "attack", "skill",
"pickup", "use", "equip", "say", "whisper", "party", "halt", "hotkey",
"click-ui", "drag-ui", "hover-ui", "type", "ui", "slot-pixel", "trade", "console",
};
return names;
}
Expand Down
8 changes: 4 additions & 4 deletions tests/network/test_control_protocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,10 @@ TEST_CASE("Control protocol treats a blank line as no request [network][control-
TEST_CASE("Control protocol serves the documented command vocabulary [network][control-protocol]")
{
const std::vector<std::string> expected = {
"ping", "scene", "state", "nearby", "events", "wait-for", "screenshot", "login", "select-char", "logout",
"quit", "move", "warp", "teleport", "attack", "skill", "pickup", "use", "equip", "say",
"whisper", "party", "halt", "hotkey", "click-ui", "drag-ui", "hover-ui", "type", "ui", "slot-pixel",
"trade",
"ping", "scene", "state", "nearby", "events", "wait-for", "screenshot", "login",
"select-char", "logout", "quit", "move", "warp", "teleport", "attack", "skill",
"pickup", "use", "equip", "say", "whisper", "party", "halt", "hotkey",
"click-ui", "drag-ui", "hover-ui", "type", "ui", "slot-pixel", "trade", "console",
};

for (const std::string& command : expected)
Expand Down
Loading