glkote
glkote copied to clipboard
Don't ignore user input events
Currently timer events might interfere with the user input events. With this fix user events are not lost.
Trying to reconstruct the intention here -- I think the scenario in question is:
- Timer event goes out
- Player triggers input
- The other end sends the update which corresponds to the timer event
The player input is ignored because "the server is busy" (there may not be an actual server, but that's what the input/update cycle represents). This is by design.
There's two problems with trying to resend the input this way:
- The input may no longer be meaningful by the time it's handled. The timer update may close windows, request different input, or just change the game state so that the player doesn't want to do that command any more.
- The timer update may not arrive in the next tick. It will in Quixe, where everything is running client-side, but with a RemGlk/GlkOte pair the response may be arbitrarily delayed. I think this patch would get into a hot loop (of defer_func()) until the update came back. Not fatal, but not ideal either.
Not sure what to say here. I guess it's a rare race condition. Or not rare, if a game frequently updates some timer on screen.
The input may no longer be meaningful by the time it's handled.
Won't the window or input IDs be no longer valid? In that case user would just get a warning - not a fatal error, right? Isn't that a better user experience than just ignoring the input forcing user to try again?
The timer update may not arrive in the next tick.
You mean there might be an infinite loop of us trying to send user input via defer_func while there's a constant updates from timer? For such a game without this patch it would be unplayable, I think.
In that case user would just get a warning - not a fatal error, right?
It shouldn't be a fatal error, but I hate to make assumptions.
You mean there might be an infinite loop of us trying to send user input via defer_func while there's a constant updates from timer?
Say we send out the timer input event to a server as an RPC (an AJAX request, over a websocket, etc). Then we have to wait for a response, which will come in the form of a new glkote_update() call. It ought to come soon, but it won't arrive before the deferfunc() call triggers. And it might not be very soon; we're at the mercy of network and server lag.
I've been thinking of adding an event queue to my GlkOte, but aside from arrange events I haven't noticed there being an need for it yet. It could be different if there was a game that was doing massive amounts or calculations.
One case to consider is a rapid timer that usually does nothing. (E.g. you tick every second and watch for a clock time, or the contents of a file, or some other external input. Until the input happens, the timer event is a no-op.)
In that case, it is safe to re-post a user input with only the generation number changed. You wouldn't want to use defer_func(); you'd want to use a queue, like Dannii said. Probably limit it to a single user input at a time. So more of a single-item cache than a queue. (If the player enters multiple commands in the time it takes the server to respond to one, that's not going to end well.)
However, this might be a hard case to detect. It's great if the update object is { type: 'update', gen: N } with no other data -- then you know nothing has changed. But the spec doesn't require the update to be minimal. It might be { type: 'update', gen: N, content:[] }. Or it might redraw the status window with the same contents, for no visible change. I don't remember offhand how minimal Quixe and RemGlk are.