Compare commits

..

No commits in common. "b18518465b035fbdeef53aa7fb409a330f9eb57c" and "f684d13a40a6cf22bf6bc09221ee024f31f78ad2" have entirely different histories.

3 changed files with 756 additions and 104 deletions

View File

@ -2,16 +2,14 @@
[![Releases](https://img.shields.io/github/v/release/FlamedDogo99/EaglerMobile?style=flat-square&logo=github&logoColor=white&label=GitHub&color=181717)](https://github.com/FlamedDogo99/EaglerMobile/releases) [![Releases](https://img.shields.io/github/v/release/FlamedDogo99/EaglerMobile?style=flat-square&logo=github&logoColor=white&label=GitHub&color=181716)](https://github.com/FlamedDogo99/EaglerMobile/releases)
[![Eaglercraft.com](https://img.shields.io/endpoint?url=https%3A%2F%2Fcellshield.info%2Fgs%3FspreadSheetId%3D1rkNuoBtzxp2m_psnMBCyaWw2BujITghrqE_2cKB6eW0%26cellRange%3DB3&style=flat-square&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABAlBMVEUAAAAAAAAwIRZPNyYkGRA6KBs9LB5jRjB5VzxLNiVoqD4%2FaCVsrEJbQCwrRBqXxmdmpzwnQBZ3t01zs0lxsUdwr0Zqqj9fnzRblzSGtVZlpTtjozhhoTdDbChHMSIuSR11tUssLCygz3CUw2SSwWGKuVp8vFJggEFgmzhcnDI7Tic1JRij03ONvF1YWFiBsFF5uU56p0xGRkZvUDdWdDZLdi9KdC5AUyxVlStGcCs0SCEkPRWcyW2IyF6Is1uDw1mHtleDrlZ9rE10okZnhUZYmC5Xly0%2BUSo5Yx2q2XqRvGSPvl9uqUdurkRvm0NAQEBZdTxaeTtUfjc3NzdPiig4SyTTBcNjAAAAAnRSTlMAJEqQKekAAAIRSURBVDjLfI7nbsIwFIUxxKKKrWzbmU1ISEIoCJDYo%2BzRPdS%2B%2F6vUJW3VJc79%2BX333lP4FQAKpwIug2QJTmBbruIFMcH%2F2HQVhbE4SYJgCv7ie8ZW9t3MmWHDiGNdBD9xjYlbQ3YVJzKUqxU1bMO9Bt%2FwbkdItSqGiqxZVhSFoaZROnwBOe4Mqavzds6woViMzjNXxvhG47Img3cBoVqHBhHTSZat5cA1CF6ElqMlRFVz4anr1USMRR5qaI68bVuMkI4kSZOjcLHpdj1kiqKitFr2lNrOvG3dqmrd93PhYeONPIQeTRHreowbzWbbVCUu7PfCUTiHsN%2BHvTT1zFam63ZzORkLkuDXBaGYvyif9SHsPac9hNZ6w1Rfx2PBF7jzJZRhuYTgKIUHD%2FFqA5UznkrlQyiV%2BUA4gvCAoC8MBny%2FyNc%2FL7wVTja3DsMwDAZM%2FVCHrBCgC%2BSQjvL23%2BVRtg9FgbZCYh%2F0kYyA6Elc6aobOpSsGkNP1QIOMgEHbqTDHhYlpEZFbGBGEPQJjFBJbTHGBqRvCxIAw9RWRV%2FnBgiHp0%2BjDv9rpF%2FfEWwCmdBR1uHC%2BlrAqQ4VTzRXrbWKxwzaDhIyb6qAFtcQoPb%2BhickTkGqK%2BcEpabZdmhkzdEMBEREhQh%2F%2BSuPy%2BEU4WuAsK1%2BQQAKYVi0%2B1K%2FI05yWOd%2F2K0TSLmfX7bzyHjL%2Frn%2B%2FymZL56MQephAAAAAElFTkSuQmCC&label=eaglercraft.com&color=%237fad55)](https://docs.google.com/spreadsheets/d/1rkNuoBtzxp2m_psnMBCyaWw2BujITghrqE_2cKB6eW0/edit?usp=sharing) [![Eaglercraft.com](https://img.shields.io/endpoint?url=https%3A%2F%2Fcellshield.info%2Fgs%3FspreadSheetId%3D1rkNuoBtzxp2m_psnMBCyaWw2BujITghrqE_2cKB6eW0%26cellRange%3DB3&style=flat-square&logo=data%3Aimage%2Fpng%3Bbase64%2CiVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAABAlBMVEUAAAAAAAAwIRZPNyYkGRA6KBs9LB5jRjB5VzxLNiVoqD4%2FaCVsrEJbQCwrRBqXxmdmpzwnQBZ3t01zs0lxsUdwr0Zqqj9fnzRblzSGtVZlpTtjozhhoTdDbChHMSIuSR11tUssLCygz3CUw2SSwWGKuVp8vFJggEFgmzhcnDI7Tic1JRij03ONvF1YWFiBsFF5uU56p0xGRkZvUDdWdDZLdi9KdC5AUyxVlStGcCs0SCEkPRWcyW2IyF6Is1uDw1mHtleDrlZ9rE10okZnhUZYmC5Xly0%2BUSo5Yx2q2XqRvGSPvl9uqUdurkRvm0NAQEBZdTxaeTtUfjc3NzdPiig4SyTTBcNjAAAAAnRSTlMAJEqQKekAAAIRSURBVDjLfI7nbsIwFIUxxKKKrWzbmU1ISEIoCJDYo%2BzRPdS%2B%2F6vUJW3VJc79%2BX333lP4FQAKpwIug2QJTmBbruIFMcH%2F2HQVhbE4SYJgCv7ie8ZW9t3MmWHDiGNdBD9xjYlbQ3YVJzKUqxU1bMO9Bt%2FwbkdItSqGiqxZVhSFoaZROnwBOe4Mqavzds6woViMzjNXxvhG47Img3cBoVqHBhHTSZat5cA1CF6ElqMlRFVz4anr1USMRR5qaI68bVuMkI4kSZOjcLHpdj1kiqKitFr2lNrOvG3dqmrd93PhYeONPIQeTRHreowbzWbbVCUu7PfCUTiHsN%2BHvTT1zFam63ZzORkLkuDXBaGYvyif9SHsPac9hNZ6w1Rfx2PBF7jzJZRhuYTgKIUHD%2FFqA5UznkrlQyiV%2BUA4gvCAoC8MBny%2FyNc%2FL7wVTja3DsMwDAZM%2FVCHrBCgC%2BSQjvL23%2BVRtg9FgbZCYh%2F0kYyA6Elc6aobOpSsGkNP1QIOMgEHbqTDHhYlpEZFbGBGEPQJjFBJbTHGBqRvCxIAw9RWRV%2FnBgiHp0%2BjDv9rpF%2FfEWwCmdBR1uHC%2BlrAqQ4VTzRXrbWKxwzaDhIyb6qAFtcQoPb%2BhickTkGqK%2BcEpabZdmhkzdEMBEREhQh%2F%2BSuPy%2BEU4WuAsK1%2BQQAKYVi0%2B1K%2FI05yWOd%2F2K0TSLmfX7bzyHjL%2Frn%2B%2FymZL56MQephAAAAAElFTkSuQmCC&label=eaglercraft.com&color=%237fad55)](https://eaglercraft.com)
[![License](https://img.shields.io/github/license/FlamedDogo99/EaglerMobile?style=flat-square)](https://github.com/FlamedDogo99/EaglerMobile/blob/main/LICENSE) [![License](https://img.shields.io/github/license/FlamedDogo99/EaglerMobile?style=flat-square)](https://github.com/FlamedDogo99/EaglerMobile/blob/main/LICENSE)
## About ## About
Eagler Mobile brings new functionality and benefits for the EaglerCraft web client by providing mobile-friendly touch controls, keyboard access, and other settings configurable through the EaglerCraft client. Eagler Mobile brings new functionality and benefits for the EaglerCraft web client by providing mobile-friendly touch controls, keyboard access, and other settings configurable through the EaglerCraft client.
So far, all devopement had been focused on the 1.8.8 client, however we hope to bring functionality to previous versions soon.
<div align="center"> <div align="center">
![Eagler Mobile Screenshot](images/preview.png) ![Eagler Mobile Screenshot](images/preview.png)

File diff suppressed because one or more lines are too long

View File

@ -6,13 +6,13 @@
// @downloadURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js // @downloadURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
// @license Apache License 2.0 - http://www.apache.org/licenses/ // @license Apache License 2.0 - http://www.apache.org/licenses/
// @match https://eaglercraft.com/mc/* // @match https://eaglercraft.com/mc/*
// @version 3.0.4 // @version 3.0.3
// @updateURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js // @updateURL https://raw.githubusercontent.com/FlamedDogo99/EaglerMobile/main/eaglermobile.user.js
// @run-at document-start // @run-at document-start
// @grant unsafeWindow // @grant unsafeWindow
// ==/UserScript== // ==/UserScript==
// This is generally a bad practice, but we need to run scripts in the main context before the DOM loads. Because we are only matching eaglercraft.com, unsafeWindow should be safe to use. // This is generally a bad practice, but we need to run scripts in the main context before the DOM loads. Because we are only matching eaglercraft.com, the use of unsafeWindow should be safe to use.
// If someone knows a better way of doing this, please create an issue // If someone knows a better way of doing this, please create an issue
try { try {
unsafeWindow.console.warn("DANGER: This userscript is using unsafeWindow. Unsafe websites could potentially use this to gain access to data and other content that the browser normally wouldn't allow!") unsafeWindow.console.warn("DANGER: This userscript is using unsafeWindow. Unsafe websites could potentially use this to gain access to data and other content that the browser normally wouldn't allow!")
@ -24,7 +24,7 @@ try {
value: window value: window
}); });
} }
// To-do: remove the mobile check is implement the dynamic enabling and disabling of individual features
function isMobile() { function isMobile() {
try { try {
document.createEvent("TouchEvent"); document.createEvent("TouchEvent");
@ -37,44 +37,43 @@ if(!isMobile()) {
alert("WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!"); alert("WARNING: This script was created for mobile, and may break functionality in non-mobile browsers!");
} }
clientWindow.crouchLock = false; // Used for crouch mobile control clientWindow.keyboardEnabled = false;
clientWindow.sprintLock = false; // Used for sprint mobile control clientWindow.crouchLock = false;
clientWindow.keyboardFix = false; // keyboardFix ? "Standard Keyboard" : "Compatibility Mode" clientWindow.sprintLock = false;
clientWindow.inputFix = false; // If true, Duplicate Mode clientWindow.keyboardFix = false;
clientWindow.blockNextInput = false; // Used for Duplicate Mode clientWindow.inputFix = false;
clientWindow.hiddenInputFocused = false; clientWindow.blockNextInput = false;
// Used for changing touchmove events to mousemove events // Used for changing touchmove events to mousemove events
var previousTouchX = null; var previousTouchX = null;
var previousTouchY = null; var previousTouchY = null;
var startTouchX = null; var startTouchX = null;
// charCodeAt is designed for unicode characters, and doesn't match the behavior of the keyCodes used by KeyboardEvents, thus necessitating this function // better charCodeAt function
String.prototype.toKeyCode = function() { String.prototype.toKeyCode = function() {
const keyCodeList = {"0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "backspace": 8, "tab": 9, "enter": 13, "shift": 16, "ctrl": 17, "alt": 18, "pause_break": 19, "caps_lock": 20, "escape": 27, " ": 32, "page_up": 33, "page_down": 34, "end": 35, "home": 36, "left_arrow": 37, "up_arrow": 38, "right_arrow": 39, "down_arrow": 40, "insert": 45, "delete": 46, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, "left_window_key": 91, "right_window_key": 92, "select_key": 93, "numpad_0": 96, "numpad_1": 97, "numpad_2": 98, "numpad_3": 99, "numpad_4": 100, "numpad_5": 101, "numpad_6": 102, "numpad_7": 103, "numpad_8": 104, "numpad_9": 105, "*": 106, "+": 107, "-": 109, ".": 110, "/": 111, "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, "num_lock": 144, "scroll_lock": 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "\u0060": 192, "[": 219, "\u005C": 220, "]": 221, "\u0022": 222}; const keyCodeList = {"0": 48, "1": 49, "2": 50, "3": 51, "4": 52, "5": 53, "6": 54, "7": 55, "8": 56, "9": 57, "backspace": 8, "tab": 9, "enter": 13, "shift": 16, "ctrl": 17, "alt": 18, "pause_break": 19, "caps_lock": 20, "escape": 27, " ": 32, "page_up": 33, "page_down": 34, "end": 35, "home": 36, "left_arrow": 37, "up_arrow": 38, "right_arrow": 39, "down_arrow": 40, "insert": 45, "delete": 46, "a": 65, "b": 66, "c": 67, "d": 68, "e": 69, "f": 70, "g": 71, "h": 72, "i": 73, "j": 74, "k": 75, "l": 76, "m": 77, "n": 78, "o": 79, "p": 80, "q": 81, "r": 82, "s": 83, "t": 84, "u": 85, "v": 86, "w": 87, "x": 88, "y": 89, "z": 90, "left_window_key": 91, "right_window_key": 92, "select_key": 93, "numpad_0": 96, "numpad_1": 97, "numpad_2": 98, "numpad_3": 99, "numpad_4": 100, "numpad_5": 101, "numpad_6": 102, "numpad_7": 103, "numpad_8": 104, "numpad_9": 105, "*": 106, "+": 107, "-": 109, ".": 110, "/": 111, "f1": 112, "f2": 113, "f3": 114, "f4": 115, "f5": 116, "f6": 117, "f7": 118, "f8": 119, "f9": 120, "f10": 121, "f11": 122, "f12": 123, "num_lock": 144, "scroll_lock": 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "\u0060": 192, "[": 219, "\u005C": 220, "]": 221, "\u0022": 222};
return keyCodeList[this]; return keyCodeList[this];
} }
// Overrides the addEventListener behavior to all code injection on keydown event listeners. This function has thrown TypeErrors on some Android devices because fn is not recognized as a function // Ignores keydown events that don't have the isValid parameter set to true
// This is used by Compatibility Mode to block invalid keyEvents
const _addEventListener = EventTarget.prototype.addEventListener; const _addEventListener = EventTarget.prototype.addEventListener;
Object.defineProperty(EventTarget.prototype, "addEventListener", { Object.defineProperty(EventTarget.prototype, "addEventListener", {
value: function (type, fn, ...rest) { value: function (type, fn, ...rest) {
if(type == 'keydown') { // Check if a keydown event is being added if(type == 'keydown') {
_addEventListener.call(this, type, function(...args) { _addEventListener.call(this, type, function(...args) {
if(!args[0].isValid && clientWindow.keyboardFix) { // Inject check for isValid flag and Compatibility Mode if(!args[0].isValid && clientWindow.keyboardFix) {
return; // When we are in Compatibility Mode, standard key presses will be ignored return;
} }
return fn.apply(this, args); // Appends the rest of the function specified by addEventListener return fn.apply(this, args);
}, ...rest); }, ...rest);
} else { // If it's not a keydown event, behave like normal } else {
_addEventListener.call(this, type, fn, ...rest); _addEventListener.call(this, type, fn, ...rest);
} }
} }
}); });
// Overrides preventDefault, because on some (Android) devices you couldn't type into hiddenInput // Allows typing in #hiddenInput
const _preventDefault = Event.prototype.preventDefault; const _preventDefault = Event.prototype.preventDefault;
Event.prototype.preventDefault = function(shouldBypass) { Event.prototype.preventDefault = function(shouldBypass) {
if(document.activeElement.id != "hiddenInput" || shouldBypass) { // activeElement is what element is currently focused if(document.activeElement.id != "hiddenInput" || shouldBypass) {
this._preventDefault = _preventDefault; this._preventDefault = _preventDefault;
this._preventDefault(); this._preventDefault();
} }
@ -204,7 +203,7 @@ inMenuStyle.textContent = `
document.documentElement.appendChild(inMenuStyle); document.documentElement.appendChild(inMenuStyle);
// The canvas is created by the client after it finishes unzipping and loading. When the canvas is created, this applies any necessary event listeners and creates buttons // The canvas is created by the client after it finishes unzipping and loading. When the canvas is created, this applies any necessary event listeners
function waitForElm(selector) { function waitForElm(selector) {
return new Promise(resolve => { return new Promise(resolve => {
if (document.querySelector(selector)) { if (document.querySelector(selector)) {
@ -232,6 +231,16 @@ function createTouchButton(buttonClass, buttonDisplay, elementName) {
return touchButton; return touchButton;
} }
function toggleKeyboard() {
const keyboardInput = document.getElementById('hiddenInput');
if (clientWindow.keyboardEnabled) {
clientWindow.keyboardEnabled = false;
keyboardInput.blur();
} else {
clientWindow.keyboardEnabled = true;
keyboardInput.select();
}
}
waitForElm('canvas').then(() => {insertCanvasElements()}); waitForElm('canvas').then(() => {insertCanvasElements()});
function insertCanvasElements() { function insertCanvasElements() {
@ -369,105 +378,67 @@ function insertCanvasElements() {
exitButton.addEventListener("touchstart", function(e){keyEvent("`", "keydown")}, false); exitButton.addEventListener("touchstart", function(e){keyEvent("`", "keydown")}, false);
exitButton.addEventListener("touchend", function(e){keyEvent("`", "keyup")}, false); exitButton.addEventListener("touchend", function(e){keyEvent("`", "keyup")}, false);
document.body.appendChild(exitButton); document.body.appendChild(exitButton);
// ---Input Handling--- // input for keyboard button
// This code is a mess, specifically because Android is so so SO inconsistent with how it handles the keyboard
// Some keyboards dispatch key events, some directly append text, and none of them meet the most basic standards supported by most other devices
// This mess is my attempt at dealing with that, and it will most likely only ever be triggered by Android
//
// It has three main modes.
// 1) Standard keyboard mode:
// This mode keeps the hiddenInput empty, saves the last key press, and on every keypress checks if it the keys are being pressed incorrectly.
// If there is a problem, it switches to compatibility mode, using beforeinput and input events instead of keydown and keyup
// 2) Compatibility mode:
// This most is most likely going to be used by Android, because Android only every dispatches keyCode 229 for any keypress
// When we enter this mode, we grab the last known key press and redispatch it, and programatically dispatch key events by reading e.inputType and e.data from beforeinput
// Unfortunately, Android is weird with this as well. Sometimes it only dispatches insertCompositionText events, and sometimes it gives the correct inputTypes as well
// Additionally, programmatically setting the input's text contents (BECAUSE ANDROID IGNORES PREVENTDEFAULT AGHHHHH) dispatches a repeat of the previous event
// Luckily, we can check if this happens when we first create the input, which necessitates the third mode:
// 3) Duplicate mode:
// If we are getting duplicate inputs, this mode ignores every other input if it matches the state saved in clientWindow.previousKey
// If users make it to this mode and still are having issues, it may be best just to remove support for their device
// ---Input Handling---
let hiddenInput = document.createElement('input', true); let hiddenInput = document.createElement('input', true);
hiddenInput.id = "hiddenInput" hiddenInput.id = "hiddenInput"
hiddenInput.classList.add("inMenu") hiddenInput.classList.add("inMenu")
hiddenInput.style.cssText = "position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index: -10;color: transparent;text-shadow: 0 0 0 black;"; // We hide the input behind a key because display: none and opacity:0 causes issues // We are hiding the text input behind button because opacity was causing problems.
hiddenInput.addEventListener("beforeinput", function(e) { // For some reason beforeinput doesn't have the same deletion problems that input has on Android hiddenInput.style.cssText = "position:absolute;top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;font-size:20px;z-index:-10;color: transparent;text-shadow: 0 0 0 black;";
e.stopImmediatePropagation(); // Android ignores this and the prevent default, so this will probably be removed in the future hiddenInput.value = " " //Allows delete to be detected before input is changed
e.preventDefault(true); // We pass a value because we've hijacked the prevent default function to have a "should bypass" boolean value hiddenInput.addEventListener("input", function(e) {
let inputData = (e.inputType == "insertLineBreak") ? "return" : (e.data == null ? "delete" : e.data.slice(-1)); // Saves the last key press. e.stopImmediatePropagation();
if(!clientWindow.lastKey) { // When we first set hiddenInput's text contents to " " we can use this to check if Duplicate Mode is needed e.preventDefault(true);
let inputData = e.data == null ? "delete" : e.data.slice(-1);
if(!clientWindow.lastKey) { // If we get an event before any keys have been pressed, we know that setting the hiddenInput creates duplicate input events, so we can apply the fix
clientWindow.console.warn("Enabling blocking duplicate key events. Some functionality may be lost.") clientWindow.console.warn("Enabling blocking duplicate key events. Some functionality may be lost.")
clientWindow.inputFix = true; clientWindow.inputFix = true;
} }
clientWindow.console.log(`Received input by ${e.inputType}: ${e.data} -> ${inputData}`);
hiddenInput.value = " ";
if(clientWindow.keyboardFix) { if(clientWindow.keyboardFix) {
if(e.inputType == "insertLineBreak") { // Detects return key press const sliceInputType = e.inputType.slice(0,1); // This is a really dumb way to do this because it's not future-proof, but its the easiest way to deal with Android
keyEvent("enter", "keydown"); if(sliceInputType== 'i') {
keyEvent("enter", "keyup"); const isDuplicate = (clientWindow.lastKey == inputData) && clientWindow.blockNextInput && clientWindow.inputFix;
} else { if(isDuplicate) {
const sliceInputType = e.inputType.slice(0,1); // Android doesn't constiently dispatch the correct inputType, but most of them either start with i for insert or d for delete, so this dumb solution should be good enough. clientWindow.blockNextInput = false;
if(sliceInputType== 'i' && e.data) { // Android sometimes always dispatches insertCompositionText inputTypes, so checking that e.data isn't null is necessary } else {
const isDuplicate = (clientWindow.lastKey == inputData) && clientWindow.blockNextInput && clientWindow.inputFix; let isShift = (inputData.toLowerCase() != inputData);
if(isDuplicate) { // If our key press matches the last unblocked key press and we are in duplicaye mode, ignore the input if(isShift) {
clientWindow.blockNextInput = false; keyEvent("shift", "keydown")
keyEvent(inputData, "keydown");
keyEvent(inputData, "keyup");
keyEvent("shift", "keyup")
} else { } else {
let isShift = (inputData.toLowerCase() != inputData); keyEvent(inputData, "keydown");
if(isShift) { // The Eaglerclient only uses e.key, e.keyCode and e.which, so we have to dispatch the shift key event separately keyEvent(inputData, "keyup");
keyEvent("shift", "keydown");
keyEvent(inputData, "keydown");
keyEvent(inputData, "keyup");
keyEvent("shift", "keyup");
} else {
keyEvent(inputData, "keydown");
keyEvent(inputData, "keyup");
}
clientWindow.blockNextInput = true;
} }
} else if (sliceInputType == 'd' || !e.data) { clientWindow.blockNextInput = true;
keyEvent("backspace", "keydown");
keyEvent("backspace", "keyup");
clientWindow.blockNextInput = false; // If we delete a character, there couldn't be a duplicate of the previous key press
} }
} else if (sliceInputType == 'd') {
keyEvent("backspace", "keydown");
keyEvent("backspace", "keyup");
clientWindow.blockNextInput = false;
} }
} }
clientWindow.lastKey = inputData // Saves the last key pressed clientWindow.lastKey = inputData
hiddenInput.value = " " //This previously allowed us to have a character to delete, but beforeinput doesn't require this. This does allow us to check wether Duplicate Mode is necessary though
}, false); }, false);
hiddenInput.addEventListener("input", function(e) { // Since we are using beforeInput for input detection, setting the text contents of hiddenInput causes weird behavior, so we use input instead hiddenInput.addEventListener("keydown", function(e) {
if (hiddenInput.value != " ") { // Avoid updating it if not needed so Duplicate Mode doesn't throw a fit
hiddenInput.value = " ";
}
}, false);
hiddenInput.addEventListener("keydown", function(e) { // Enables Compatibility Mode if we receive an invalid key press event
if((e.keyCode == 229 || e.which == 229) && !clientWindow.keyboardFix) { if((e.keyCode == 229 || e.which == 229) && !clientWindow.keyboardFix) {
clientWindow.console.warn("Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.") clientWindow.console.warn("Switching from keydown to input events due to invalid KeyboardEvent. Some functionality will be lost.")
clientWindow.keyboardFix = true; clientWindow.keyboardFix = true;
if(clientWindow.lastKey) { // Resend the last saved key press (which is being tracked by the beforeinput event listener) so the transition to Compatibility Mode isn't noticeable if(clientWindow.lastKey) {
keyEvent(clientWindow.lastKey, "keydown"); keyEvent(clientWindow.lastKey, "keydown");
keyEvent(clientWindow.lastKey, "keyup"); keyEvent(clientWindow.lastKey, "keyup");
} }
} }
}, false); }, false);
hiddenInput.addEventListener("blur", function(e) {
clientWindow.hiddenInputFocused = false;
});
document.body.appendChild(hiddenInput); document.body.appendChild(hiddenInput);
let keyboardButton = createTouchButton("keyboardButton", "inMenu"); let keyboardButton = createTouchButton("keyboardButton", "inMenu");
keyboardButton.style.cssText = "top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;" keyboardButton.style.cssText = "top: 0vh; margin: auto; left: 8vh; right:0vh; width: 8vh; height: 8vh;"
keyboardButton.addEventListener("touchstart", function(e){ keyboardButton.addEventListener("touchstart", function(e){e.preventDefault();hiddenInput.blur()}, false);
e.preventDefault(); keyboardButton.addEventListener("touchend", function(e){e.preventDefault();toggleKeyboard()}, false);
}, false);
keyboardButton.addEventListener("touchend", function(e){
e.preventDefault();
if(clientWindow.hiddenInputFocused) {
hiddenInput.blur()
} else {
hiddenInput.select()
clientWindow.hiddenInputFocused = true;
}
}, false);
document.body.appendChild(keyboardButton); document.body.appendChild(keyboardButton);
let placeButton = createTouchButton("placeButton", "inGame"); let placeButton = createTouchButton("placeButton", "inGame");
placeButton.style.cssText = "right:0vh;bottom:20vh;" placeButton.style.cssText = "right:0vh;bottom:20vh;"
@ -524,7 +495,7 @@ function insertCanvasElements() {
document.body.appendChild(pauseButton); document.body.appendChild(pauseButton);
let chatButton = createTouchButton("chatButton", "inGame"); let chatButton = createTouchButton("chatButton", "inGame");
chatButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;" chatButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 16vh; width: 8vh; height: 8vh;"
chatButton.addEventListener("touchstart", function(e){keyEvent("t", "keydown")}, false); // For some reason dispatching a keyup event for this closes the chat, which is really weird chatButton.addEventListener("touchstart", function(e){keyEvent("t", "keydown")}, false);
document.body.appendChild(chatButton); document.body.appendChild(chatButton);
let perspectiveButton = createTouchButton("perspectiveButton", "inGame"); let perspectiveButton = createTouchButton("perspectiveButton", "inGame");
perspectiveButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;" perspectiveButton.style.cssText = "top: 0vh; margin: auto; left: 0vh; right: 0vh; width: 8vh; height: 8vh;"
@ -602,7 +573,6 @@ customStyle.textContent = `
border: none; border: none;
} }
html, body, canvas { html, body, canvas {
height: 100svh !important;
height: -webkit-fill-available !important; height: -webkit-fill-available !important;
touch-action: pan-x pan-y; touch-action: pan-x pan-y;
-webkit-touch-callout: none; -webkit-touch-callout: none;