FlamedDogo99 / EaglerMobile

A userscript that allows EaglerCraft to run on mobile browsers. As of June 15, 2023, the Eagler Mobile script is directly integrated with the main EaglerCraft website!
Apache License 2.0
18 stars 20 forks source link

[Bug] Can't type in chat #13

Closed FlamedDogo99 closed 4 months ago

FlamedDogo99 commented 4 months ago

Describe the Bug Reported by @Realplurr:

You cannot type into the chat

Steps to Reproduce

  1. Start EaglerCraft client with Eagler Mobile
  2. Join a server or create a new world
  3. Press the chat šŸ’¬ button on the top row of buttons
  4. Press the keyboard āŒØļø button on the top row of buttons
  5. Note that typing does not type into the chat

Additional Information Browser google and chrome. Latest version available from google play market. Phone redmi 8 using google keyboard and android version 10, MIUI 12.5.3 (stable version) 337540109-5adc1b21-62a0-4dd4-ab78-ae4e27f32181

There is also no blinking "_" when opening the chat

FlamedDogo99 commented 4 months ago

Well Iā€™ve ran into some dead-ends for working on this one. First of all thereā€™s no BlueStacks build for M2 Macā€™s so I canā€™t replicate this issue. Additionally, because the cursor isnā€™t event blinking in the textbox, it means that thereā€™s something going wrong internally with the Eagler client, instead of it just being an issue with how Android does key events. I did notice that the client does have focus, blur and onMouseEnter event listeners, so Iā€™ll see if simulating those firing like a non-mobile device changes anything. Again, itā€™s just hard to diagnose without being able to identify or replicate the underlying the problem.

Edit: I've managed to get Android studio set up, so this may not be a problem anymore

FlamedDogo99 commented 4 months ago

Because Android doesn't dispatch keyboard events correctly, my suspicion is that it isn't dispatching mouseenter and focus events correctly either. In theory this could be why the client isn't accepting inputs. #14 handles dispatching these events in hope of getting the client to function correctly. If someone could test this, it would be greatly appreciated.

FlamedDogo99 commented 4 months ago

Update: I managed to get Android Studio set up, and Iā€™ve made some progress on fixing this issue, but it is a mess.

  1. It turns out that when Android dispatches an input event, it uses insertCompositionText instead of insertText, and instead of returning the updated value, it returns the entire string.
  2. On Firefox, the event is dispatched twice, but on Chrome itā€™s dispatched once but it resets the caps lock
  3. On both Firefox and Chrome, setting the inputā€™s value to a different string doesnā€™t effect how many characters you can delete, which is really inconvenient because it means we somehow have to find another way to detect deletion

With how inconsistent everything is, Iā€™m struggling to find a workable solution.

colbster937 commented 4 months ago

wait if your on android then hear me out. pojavlauncherā€¦ it has controller support and can run the latest version

RealPlurr commented 4 months ago

I mean its not eaglercraft and we are speaking currently about eaglercraft

RealPlurr commented 4 months ago

Because Android doesn't dispatch keyboard events correctly, my suspicion is that it isn't dispatching mouseenter and focus events correctly either. In theory this could be why the client isn't accepting inputs. #14 handles dispatching these events in hope of getting the client to function correctly. If someone could test this, it would be greatly appreciated.

One minute leme push it to client and test

RealPlurr commented 4 months ago

I got this new "fix" and now mobile buttons and stuff wont even appear

RealPlurr commented 4 months ago

I have no clue how I need to open F12 to send you errors

FlamedDogo99 commented 4 months ago

I am not sure if this is possible. Androidā€™s doesnā€™t just handle keyboard input differently than other devices, it handles it inconsistently.

Example 1 > I am picking this up now and want to summarize some of the research I did to get into the issue. There is a lot of information in a lot of posts added across various issues and it is hard to follow that, especially since some of that already got outdated either due to changes in our code or due to changes in browsers. > > For that reason, I want to sum up once again what's going on and what problems we have with typing on Android. Thanks to @f1ames for setting foundations for that. > > So, deleting is incorrectly handled on Android. For non-collapsed selection, twice as much content is deleted than it was selected. At the beginning, I thought it is a lot of magic, quirkiness and nonsense but after digging deeper and deeper I think that we might be able to do something there. Okay. How does it happen? > > **tl;dr: Input handling differs a lot between Android and desktop and we need to cater our algorithms for those differences. Not sure if one-solution-to-fit-all will be possible. On Android, the content is first removed programmatically by us (in one of the event handlers) and then also remove mutations are generated because our events handling algorithms are not suited for how Android Chrome behaves.** > > First of all, deletion is not recognized as deletion because in `keydown` event, the `keyCode` is `229`. So `DeleteObserver` does not interfere. If, for example, R was pressed (instead of Backspace) then it also gets `229`. > > We have `injectUnsafeKeystrokeHandler`. `229` is not a safe key code but it is also a key code connected with compositions so, depending on other factors, content in the selection sometimes is and sometimes isn't deleted. Let's assume it was deleted. `injectUnsafeKeystrokeHandler` is hooked on `keydown` event. After it is handled, other (internal) browser mechanisms kick in. They base on the selection state to perform further actions on the content. > > If the content is deleted, then the selection is set to be collapsed. This is where the problems start. Selection behaves differently on Android than it does on the desktop. _I am not sure if it is a browser's fault (Android Chrome vs desktop Chrome) or IME's fault (virtual keyboard vs physical keyboard)_. So let's see what happens using plain contenteditable without CKE5. We will test Backspace and R on non-collapsed selection. > > I modified @f1ames codepen to check what exactly happens: https://codepen.io/anon/pen/rELxwd?editors=1010. You can uncomment and change the `if` statement to `beforeinput` to test the differences. > > Desktop Backspace scenario. It works the same for collapsed and non-collapsed selection: > > 1. Selection remains unchanged on `keydown` and `beforeinput`. > 2. Browser performs "deletion" action treating current selection like it was the selection before pressing Backspace. This is important, I'll get back to that later. > 3. Mutations are generated. > > Desktop R scenario: > > 1. Selection remains unchanged on `keydown` and `beforeinput`. > 2. Browser performs "insert `R`" action treating current selection like it was the selection before pressing R. If the selection is non-collapsed, the selection's content is replaced with `R`. If the selection is collapsed, `R` is inserted. > 3. Mutations are generated. > > What happens on Android though is more magical. I am sure it can be somehow explained given that all virtual keyboard input is treated kind-of-like-IME-or-composition (according to Chrome devs/docs). But, unfortunately, it differs from what happens on a desktop by quite a lot. > > Android Backspace scenario: > > 1. Selection becomes collapsed to its end on `keydown`. > 2. Browser somehow remembers how many characters were to be removed and on `beforeinput` extends the selection properly (so back to the selection that was set before pressing Backspace). Note, that if the selection was collapsed before pressing Backspace, it gets extended by one character (the one to be removed). > 3. The action ("deletion") is performed exactly on the current selection. It is different than on a desktop, where the selection is not extended if it was collapsed. This is important if we are to change the selection in one of those events. > 4. Mutations are generated. > > Android R scenario: > > First of all, if the selection was non-collapsed, there are two "actions" performed here. First is deletion and it works like above. Then events for "insert `R`" are generated. _TBH it is quite nice that the browser separates those actions, unfortunately, it is not a standard behavior_. > > 1. Selection remains unchanged on `keydown` and `beforeinput`. > 2. "R" is inserted. > 3. Mutations are generated. > > The next step was to check how changing DOM selection in events callbacks affects what happens on Android: > > 1. If selection is collapsed at some position on `keydown`, on `beforeinput` it is extended from that position. But it is still extended, so the browser remembers somehow how many characters needed to be removed. For example, if `Foo [bar] baz` was selected, on `keydown` selection is collapsed at `Foo bar baz[]`, then `baz` will get removed. > 2. If selection is changed on `beforeinput`, the new selection range is used to apply changes. For example, if `Foo [bar] baz` was selected, on `beforeinput` selection is changed to `Foo bar [baz]` then `baz` will get removed. If selection would get collapsed, nothing would be removed. > 3. However, after the action is handled, on `keyup` the selection is moved by the number of characters that were supposed to be removed. > > At this point, I was happy with the research I've done. I understood how typing works on Android and how to change our scripts to handle it correctly. **Then I decided to switch keyboard to GBoard and see how it works. And I got extremely disappointed.** > > It seems that GBoard is the default keyboard, while I tested Switfkey (which is default on Huawei). Honestly, I didn't think which one I am testing and that the differences might be so big. > > So, long story short GBoard treats every typing as composition. This is because of auto-correction / word suggestions (when I turned them off it started to work similarly to Swiftkey). I guess one could argue that it makes some sense šŸ¤·ā€ā™‚but really it wasn't something I expected. > > So, fired events, their data, selection behavior, browser reacting to selection change on events -- it is all different. Even removing a character is treated as a composition change and `insertCompositionText` is fired instead of `deleteContentBackward`. It fires even more events than Pinyin keyboard on the desktop. Even moving selection inside a word fires composition events. It is also bugged itself. On clean contenteditable selecting a word (or generally, some text between two spaces) and pressing a letter key sometimes ends up in removing only the first character in the selection instead of everything in the selection. And it happens randomly (although pressing the keyboard key for a longer period of time seems to cause it every time). > > Fortunately, removing selected content generates events as on Swiftkey. Unfortunately, replacing selecting text does not provide two events as on Swiftkey. Instead, one `beforeinput` with `inputType: 'insertText'` is fired. Thankfully, it behaves as expected when selection is changed on `beforeinput`. So, given that the bugs on Android are connected with non-collapsed suggestions handling, we might get somewhere (as long as the bug described above do not happen, that is). > > After this research, it is obvious that our current solution won't work well on Android. Our handling is done on `keydown` where content is deleted and selection collapsed but the selection change has no effect on Android because it gets re-collapsed and content is deleted again. > > We cannot go fully into `beforeinput` because most desktop browsers do not provide that event. So we need a hybrid solution. We need to listen to `keydown` and to `beforeinput`. It seems that there's no place after both of them where we could check if `beforeinput` was fired/handled. I mean, there is, but it is `input` event and AFAIR there is too late for taking actions at that point. > > So, I think that we need dedicated handling on `keydown` for desktop browsers and dedicated handling on `beforeinput` for Android. Not sure if it is a good idea to sniff if `beforeinput` event is available, just better use UA I guess. We can skip handling in `keydown` (or set high priority listener that will cancel `keydown`) on Android and make correct handling for Android in `beforeinput`.
Example 2 > Samsung keyboard, pre-installed on Samsung devices, works even differently šŸ˜µšŸ˜µšŸ˜µ. From a very quick check, it looks like it works similarly to Chrome desktop. It fires correct `keydown` event with keycode `8` (Backspace) and do not extend selection on `beforeinput`.
Example 3 > PLOT TWIST: When you switch to Chinese (Pinyin) on GBoard, it starts to behave like Samsung keyboard, that is, when using Backspace, it suddenly starts sending "correct" `keydown` events and stops expanding selection on `beforeinput`.

This is extremely discouraging, because it means that thereā€™s no solution that we can guarantee will work for every different way that itā€™s inconsistent.

Iā€™ll keep working with this, but the solution may just be adding a check that will tell users if their device isnā€™t supported.

Itā€™s not the answer I was hoping for, but it might be all we can get until Android fixes the multitude of broken things on their end.

FlamedDogo99 commented 4 months ago

This should be fixed with #20, tested with Android Studio on: