UNPKG

@rtsdk/lance-topia

Version:

A Node.js based real-time multiplayer game server

280 lines (258 loc) 7.44 kB
// based on http://keycode.info/ // keyboard handling const keyCodeTable = { 3: 'break', 8: 'backspace', // backspace / delete 9: 'tab', 12: 'clear', 13: 'enter', 16: 'shift', 17: 'ctrl', 18: 'alt', 19: 'pause/break', 20: 'caps lock', 27: 'escape', 28: 'conversion', 29: 'non-conversion', 32: 'space', 33: 'page up', 34: 'page down', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 41: 'select', 42: 'print', 43: 'execute', 44: 'Print Screen', 45: 'insert', 46: 'delete', 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', 58: ':', 59: 'semicolon (firefox), equals', 60: '<', 61: 'equals (firefox)', 63: 'ß', 64: '@', 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', 91: 'Windows Key / Left ⌘ / Chromebook Search key', 92: 'right window key', 93: 'Windows Menu / Right ⌘', 96: 'numpad 0', 97: 'numpad 1', 98: 'numpad 2', 99: 'numpad 3', 100: 'numpad 4', 101: 'numpad 5', 102: 'numpad 6', 103: 'numpad 7', 104: 'numpad 8', 105: 'numpad 9', 106: 'multiply', 107: 'add', 108: 'numpad period (firefox)', 109: 'subtract', 110: 'decimal point', 111: 'divide', 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5', 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 123: 'f12', 124: 'f13', 125: 'f14', 126: 'f15', 127: 'f16', 128: 'f17', 129: 'f18', 130: 'f19', 131: 'f20', 132: 'f21', 133: 'f22', 134: 'f23', 135: 'f24', 144: 'num lock', 145: 'scroll lock', 160: '^', 161: '!', 163: '#', 164: '$', 165: 'ù', 166: 'page backward', 167: 'page forward', 169: 'closing paren (AZERTY)', 170: '*', 171: '~ + * key', 173: 'minus (firefox), mute/unmute', 174: 'decrease volume level', 175: 'increase volume level', 176: 'next', 177: 'previous', 178: 'stop', 179: 'play/pause', 180: 'e-mail', 181: 'mute/unmute (firefox)', 182: 'decrease volume level (firefox)', 183: 'increase volume level (firefox)', 186: 'semi-colon / ñ', 187: 'equal sign', 188: 'comma', 189: 'dash', 190: 'period', 191: 'forward slash / ç', 192: 'grave accent / ñ / æ', 193: '?, / or °', 194: 'numpad period (chrome)', 219: 'open bracket', 220: 'back slash', 221: 'close bracket / å', 222: 'single quote / ø', 223: '`', 224: 'left or right ⌘ key (firefox)', 225: 'altgr', 226: '< /git >', 230: 'GNOME Compose Key', 231: 'ç', 233: 'XF86Forward', 234: 'XF86Back', 240: 'alphanumeric', 242: 'hiragana/katakana', 243: 'half-width/full-width', 244: 'kanji', 255: 'toggle touchpad' }; /** * This class allows easy usage of device keyboard controls. Use the method {@link KeyboardControls#bindKey} to * generate events whenever a key is pressed. * * @example * // in the ClientEngine constructor * this.controls = new KeyboardControls(this); * this.controls.bindKey('left', 'left', { repeat: true } ); * this.controls.bindKey('right', 'right', { repeat: true } ); * this.controls.bindKey('space', 'space'); * */ class KeyboardControls { constructor(clientEngine) { this.clientEngine = clientEngine; this.gameEngine = clientEngine.gameEngine; this.setupListeners(); // keep a reference for key press state this.keyState = {}; // a list of bound keys and their corresponding actions this.boundKeys = {}; this.gameEngine.on('client__preStep', () => { for (let keyName of Object.keys(this.boundKeys)) { if (this.keyState[keyName] && this.keyState[keyName].isDown) { // handle repeat press if (this.boundKeys[keyName].options.repeat || this.keyState[keyName].count == 0) { // callback to get live parameters if function let parameters = this.boundKeys[keyName].parameters; if (typeof parameters === "function") { parameters = parameters(); } // todo movement is probably redundant let inputOptions = Object.assign({ movement: true }, parameters || {}); this.clientEngine.sendInput(this.boundKeys[keyName].actionName, inputOptions); this.keyState[keyName].count++; } } } }); } setupListeners() { document.addEventListener('keydown', (e) => { this.onKeyChange(e, true);}); document.addEventListener('keyup', (e) => { this.onKeyChange(e, false);}); } /** * Bind a keyboard key to a Lance client event. Each time the key is pressed, * an event will be transmitted by the client engine, using {@link ClientEngine#sendInput}, * and the specified event name. * * Common key names: up, down, left, right, enter, shift, ctrl, alt, * escape, space, page up, page down, end, home, 0..9, a..z, A..Z. * For a full list, please check the source link above. * * @param {String} keys - keyboard key (or array of keys) which will cause the event. * @param {String} actionName - the event name * @param {Object} options - options object * @param {Boolean} options.repeat - if set to true, an event continues to be sent on each game step, while the key is pressed * @param {Object/Function} parameters - parameters (or function to get parameters) to be sent to * the server with sendInput as the inputOptions */ bindKey(keys, actionName, options, parameters) { if (!Array.isArray(keys)) keys = [keys]; let keyOptions = Object.assign({ repeat: false }, options); keys.forEach(keyName => { this.boundKeys[keyName] = { actionName, options: keyOptions, parameters: parameters }; }); } // todo implement unbindKey onKeyChange(e, isDown) { e = e || window.event; let keyName = keyCodeTable[e.keyCode]; if (keyName && this.boundKeys[keyName]) { if (this.keyState[keyName] == null) { this.keyState[keyName] = { count: 0 }; } this.keyState[keyName].isDown = isDown; // key up, reset press count if (!isDown) this.keyState[keyName].count = 0; // keep reference to the last key pressed to avoid duplicates this.lastKeyPressed = isDown ? e.keyCode : null; // this.renderer.onKeyChange({ keyName, isDown }); e.preventDefault(); } } } export default KeyboardControls;