|
From: <gi...@cr...> - 2025-12-04 07:05:15
|
via 9e2179861ec4accfd3ced83639dcd004ba15abcb (commit)
from d7418c7e04b6be3e21a3df9889984769620c3177 (commit)
-----------------------------------------------------------------------
commit 9e2179861ec4accfd3ced83639dcd004ba15abcb
Author: Isaac Clancy <ik...@ya...>
Date: Thu Dec 4 17:17:56 2025 +1300
Fix "input" messages from client being reordered
All the messages that the webtiles client sends back to the server
should arrive in the order they were send. However, due to the way in
which we were converting messages of type "input" into console input, it
could be reordered with respect to other messages. Commit fc0747a made
it common for "input" messages to be reordered before other messages
that give keycodes instead of being extremely rare making this
reordering problem very noticeable. To fix this, add a new "text_input"
message type which doesn't suffer from reordering and use it instead
(the old "input" messages type needs to remain unchanged so that
servers can still run old versions of the game).
-----------------------------------------------------------------------
Summary of changes:
crawl-ref/source/libunix.cc | 50 +++++++++++++---------
crawl-ref/source/tileweb.cc | 35 +++++++++++++--
crawl-ref/source/tileweb.h | 2 +
.../source/webserver/game_data/static/game.js | 2 +-
.../source/webserver/game_data/static/menu.js | 6 +--
.../source/webserver/game_data/static/textinput.js | 2 +-
.../webserver/game_data/static/ui-layouts.js | 2 +-
7 files changed, 69 insertions(+), 30 deletions(-)
diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc
index 2f841a3e26..fb8d0f15bc 100644
--- a/crawl-ref/source/libunix.cc
+++ b/crawl-ref/source/libunix.cc
@@ -502,11 +502,14 @@ static int proc_mouse_event(int c, const MEVENT *me)
}
#endif
-static int pending = 0;
+static int curses_pending_key = 0;
+#ifdef USE_TILE_WEB
+static int tiles_pending_key = 0;
+#endif
static bool _curses_fetch_pending_key(bool blocking)
{
- if (pending)
+ if (curses_pending_key)
return true;
wint_t c;
@@ -532,15 +535,15 @@ static bool _curses_fetch_pending_key(bool blocking)
// a timeout from an error...
if (!blocking)
return false;
- pending = ESCAPE;
+ curses_pending_key = ESCAPE;
return true;
case OK:
// a normal (printable) key
- pending = c;
+ curses_pending_key = c;
return true;
case KEY_CODE_YES:
default:
- pending = -c;
+ curses_pending_key = -c;
return true;
}
}
@@ -562,14 +565,21 @@ static int _get_key_from_curses()
refresh();
tiles.redraw();
+ if (tiles_pending_key)
+ {
+ wint_t c = tiles_pending_key;
+ tiles_pending_key = 0;
+ return c;
+ }
+
wint_t c = tiles.await_input(&_curses_has_key);
if (c != 0)
return c;
#endif
_curses_fetch_pending_key(true);
- int result = pending;
- pending = 0;
+ int result = curses_pending_key;
+ curses_pending_key = 0;
return result;
}
@@ -592,16 +602,16 @@ static int _headless_getchk()
watchdog();
#endif
- if (pending)
+#ifdef USE_TILE_WEB
+ tiles.redraw();
+
+ if (tiles_pending_key)
{
- int c = pending;
- pending = 0;
+ wint_t c = tiles_pending_key;
+ tiles_pending_key = 0;
return c;
}
-
-#ifdef USE_TILE_WEB
- tiles.redraw();
wint_t c = tiles.await_input([]() { return false; });
if (c != 0)
return c;
@@ -1841,14 +1851,13 @@ void delay(unsigned int time)
static bool _headless_kbhit()
{
- if (pending)
- return true;
-
#ifdef USE_TILE_WEB
+ if (tiles_pending_key)
+ return true;
wint_t c = tiles.try_await_input();
if (c != 0)
{
- pending = c;
+ tiles_pending_key = c;
return true;
}
#endif
@@ -1862,17 +1871,16 @@ bool kbhit()
if (_headless_mode)
return _headless_kbhit();
- if (pending)
- return true;
-
if (_curses_has_key())
return true;
#ifdef USE_TILE_WEB
+ if (tiles_pending_key)
+ return true;
wint_t c = tiles.try_await_input();
if (c != 0)
{
- pending = c;
+ tiles_pending_key = c;
return true;
}
#endif
diff --git a/crawl-ref/source/tileweb.cc b/crawl-ref/source/tileweb.cc
index 9edd28bf0d..06b0008a73 100644
--- a/crawl-ref/source/tileweb.cc
+++ b/crawl-ref/source/tileweb.cc
@@ -421,6 +421,19 @@ static int _handle_cell_click(const coord_def &gc, int button, bool force)
return CK_MOUSE_CLICK;
}
+static char32_t _remove_first_character_utf8(string& text)
+{
+ if (text.empty())
+ return 0;
+ char32_t result = 0;
+ int length = utf8towc(&result, text.c_str());
+ if (!length)
+ text.clear();
+ else
+ text.erase(0, length);
+ return result;
+}
+
wint_t TilesFramework::_handle_control_message(sockaddr_un addr, string data)
{
JsonWrapper obj = json_decode(data.c_str());
@@ -433,7 +446,7 @@ wint_t TilesFramework::_handle_control_message(sockaddr_un addr, string data)
fprintf(stderr, "websocket: Received control message '%s' in %d byte.\n", msgtype.c_str(), (int) data.size());
#endif
- int c = 0;
+ wint_t c = 0;
if (msgtype == "attach")
{
@@ -607,6 +620,14 @@ wint_t TilesFramework::_handle_control_message(sockaddr_un addr, string data)
// (possibly just as a string, like the lua API for this)
process_command(CMD_GAME_MENU);
}
+ else if (msgtype == "text_input")
+ {
+ JsonWrapper text = json_find_member(obj.node, "text");
+ text.check(JSON_STRING);
+ ASSERT(m_pending_text_input.empty());
+ m_pending_text_input = text->string_;
+ c = _remove_first_character_utf8(m_pending_text_input);
+ }
return c;
}
@@ -616,6 +637,10 @@ wint_t TilesFramework::try_await_input()
if (m_sock_name.empty())
return 0;
+ wint_t c = _remove_first_character_utf8(m_pending_text_input);
+ if (c != 0)
+ return c;
+
fd_set fds;
int result;
while (true)
@@ -634,7 +659,7 @@ wint_t TilesFramework::try_await_input()
if (result <= 0)
return 0;
- wint_t c = _receive_control_message();
+ c = _receive_control_message();
if (c != 0)
return c;
}
@@ -660,6 +685,10 @@ wint_t TilesFramework::await_input(bool(*has_console_input)())
if (m_sock_name.empty())
return 0;
+ wint_t c = _remove_first_character_utf8(m_pending_text_input);
+ if (c != 0)
+ return c;
+
int result;
fd_set fds;
int maxfd = m_sock;
@@ -690,7 +719,7 @@ wint_t TilesFramework::await_input(bool(*has_console_input)())
{
if (FD_ISSET(m_sock, &fds))
{
- wint_t c = _receive_control_message();
+ c = _receive_control_message();
if (c != 0)
return c;
}
diff --git a/crawl-ref/source/tileweb.h b/crawl-ref/source/tileweb.h
index 54f2c70838..6501d27946 100644
--- a/crawl-ref/source/tileweb.h
+++ b/crawl-ref/source/tileweb.h
@@ -315,6 +315,8 @@ protected:
player_info m_current_player_info;
+ string m_pending_text_input;
+
void _send_version();
void _send_layout();
diff --git a/crawl-ref/source/webserver/game_data/static/game.js b/crawl-ref/source/webserver/game_data/static/game.js
index e8296803d0..56247b1053 100644
--- a/crawl-ref/source/webserver/game_data/static/game.js
+++ b/crawl-ref/source/webserver/game_data/static/game.js
@@ -387,7 +387,7 @@ function ($, exports, comm, client, key_conversion, dungeon_renderer, display,
function handle_mobile_input(e)
{
e.target.value = e.target.defaultValue;
- comm.send_message("input", { text: e.originalEvent.data });
+ comm.send_message("text_input", { text: e.originalEvent.data });
}
function handle_mobile_keydown(e)
diff --git a/crawl-ref/source/webserver/game_data/static/menu.js b/crawl-ref/source/webserver/game_data/static/menu.js
index b7eea1bb49..5657167870 100644
--- a/crawl-ref/source/webserver/game_data/static/menu.js
+++ b/crawl-ref/source/webserver/game_data/static/menu.js
@@ -736,7 +736,7 @@ function ($, comm, client, ui, enums, cr, util, options, scroller) {
input.off("focusout");
var enter = String.fromCharCode(13);
var text = input.val() + enter;
- comm.send_message("input", { text: text });
+ comm.send_message("text_input", { text: text });
restore();
ev.preventDefault();
@@ -1079,14 +1079,14 @@ function ($, comm, client, ui, enums, cr, util, options, scroller) {
if (event.which == 1)
comm.send_message("key", { keycode: 13 });
else if (event.which == 3)
- comm.send_message("input", { text: '\'' });
+ comm.send_message("text_input", { text: '\'' });
}
else if (menu.flags & enums.menu_flag.MULTISELECT)
{
if (event.which == 1)
comm.send_message("key", { keycode: 32 });
else if (event.which == 3)
- comm.send_message("input", { text: '\'' });
+ comm.send_message("text_input", { text: '\'' });
}
}
// TODO: don't rely on hotkeys here
diff --git a/crawl-ref/source/webserver/game_data/static/textinput.js b/crawl-ref/source/webserver/game_data/static/textinput.js
index dd59171242..cdd3759a90 100644
--- a/crawl-ref/source/webserver/game_data/static/textinput.js
+++ b/crawl-ref/source/webserver/game_data/static/textinput.js
@@ -135,7 +135,7 @@ function ($, comm, client, enums, util, options, ui) {
comm.send_message("key", { keycode: 21 });
comm.send_message("key", { keycode: 11 });
}
- comm.send_message("input", { text: text });
+ comm.send_message("text_input", { text: text });
// seed-selection handles this on its own, because there is first
// a validation step -- the input loop only terminates on abort
// or if there is an actual number to be found.
diff --git a/crawl-ref/source/webserver/game_data/static/ui-layouts.js b/crawl-ref/source/webserver/game_data/static/ui-layouts.js
index 2ca94c8b63..26922ede2b 100644
--- a/crawl-ref/source/webserver/game_data/static/ui-layouts.js
+++ b/crawl-ref/source/webserver/game_data/static/ui-layouts.js
@@ -73,7 +73,7 @@ function ($, comm, client, ui, enums, cr, util, scroller, main, gui, player, opt
if (colour)
$item.addClass("fg"+spell.colour);
$item.on("click", function () {
- comm.send_message("input", { text: letter });
+ comm.send_message("text_input", { text: letter });
});
});
$container.append($list);
--
Dungeon Crawl Stone Soup
|