From c81014d6eff92c7c4a19be6f0bce6ef530cfdb9f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 25 Oct 2015 19:28:27 +1100 Subject: [PATCH] Add CpeTextHotKey support. --- ClassicalSharp/2D/Screens/ChatScreen.cs | 2 +- ClassicalSharp/2D/Screens/NormalScreen.cs | 4 + ClassicalSharp/ClassicalSharp.csproj | 3 + ClassicalSharp/Entities/LocalPlayer.cs | 5 +- ClassicalSharp/Game/InputHandler.cs | 19 ++++- ClassicalSharp/Hotkeys/HotkeyList.cs | 85 +++++++++++++++++++ ClassicalSharp/Hotkeys/LwjglToKey.cs | 73 ++++++++++++++++ .../Network/NetworkProcessor.CPE.cs | 24 +++++- ClassicalSharp/Network/NetworkProcessor.cs | 2 +- 9 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 ClassicalSharp/Hotkeys/HotkeyList.cs create mode 100644 ClassicalSharp/Hotkeys/LwjglToKey.cs diff --git a/ClassicalSharp/2D/Screens/ChatScreen.cs b/ClassicalSharp/2D/Screens/ChatScreen.cs index 186b2369b..1ae840b67 100644 --- a/ClassicalSharp/2D/Screens/ChatScreen.cs +++ b/ClassicalSharp/2D/Screens/ChatScreen.cs @@ -201,7 +201,7 @@ namespace ClassicalSharp { return textInput.HandlesKeyPress( key ); } - void OpenTextInputBar( string initialText ) { + public void OpenTextInputBar( string initialText ) { if( !game.CursorVisible ) game.CursorVisible = true; suppressNextPress = true; diff --git a/ClassicalSharp/2D/Screens/NormalScreen.cs b/ClassicalSharp/2D/Screens/NormalScreen.cs index 2faca2af9..8f95e4f1f 100644 --- a/ClassicalSharp/2D/Screens/NormalScreen.cs +++ b/ClassicalSharp/2D/Screens/NormalScreen.cs @@ -121,6 +121,10 @@ namespace ClassicalSharp { return false; } + public void OpenTextInputBar( string text ) { + chat.OpenTextInputBar( text ); + } + public override bool HandlesMouseScroll( int delta ) { return chat.HandlesMouseScroll( delta ); } diff --git a/ClassicalSharp/ClassicalSharp.csproj b/ClassicalSharp/ClassicalSharp.csproj index 33a1fe1b1..8bdcdc851 100644 --- a/ClassicalSharp/ClassicalSharp.csproj +++ b/ClassicalSharp/ClassicalSharp.csproj @@ -138,6 +138,8 @@ + + @@ -234,6 +236,7 @@ + diff --git a/ClassicalSharp/Entities/LocalPlayer.cs b/ClassicalSharp/Entities/LocalPlayer.cs index a49e4ca8f..e213a5a28 100644 --- a/ClassicalSharp/Entities/LocalPlayer.cs +++ b/ClassicalSharp/Entities/LocalPlayer.cs @@ -264,7 +264,7 @@ namespace ClassicalSharp { PitchDegrees = Utils.LerpAngle( lastPitch, nextPitch, t ); } - internal void HandleKeyDown( Key key ) { + internal bool HandleKeyDown( Key key ) { KeyMap keys = game.InputHandler.Keys; if( key == keys[KeyMapping.Respawn] && canRespawn ) { Vector3I p = Vector3I.Floor( SpawnPoint ); @@ -289,7 +289,10 @@ namespace ClassicalSharp { flying = !flying; } else if( key == keys[KeyMapping.NoClip] && canNoclip ) { noClip = !noClip; + } else { + return false; } + return true; } /// Calculates the jump velocity required such that when a client presses diff --git a/ClassicalSharp/Game/InputHandler.cs b/ClassicalSharp/Game/InputHandler.cs index cebae3100..e19f14eb3 100644 --- a/ClassicalSharp/Game/InputHandler.cs +++ b/ClassicalSharp/Game/InputHandler.cs @@ -1,11 +1,13 @@ using System; using OpenTK; using OpenTK.Input; +using ClassicalSharp.Hotkeys; namespace ClassicalSharp { public sealed class InputHandler { + public HotkeyList Hotkeys = new HotkeyList(); Game game; public InputHandler( Game game ) { this.game = game; @@ -268,12 +270,25 @@ namespace ClassicalSharp { } else if( key == Keys[KeyMapping.Screenshot] ) { game.screenshotRequested = true; } else if( game.activeScreen == null || !game.activeScreen.HandlesKeyDown( key ) ) { - if( !HandleBuiltinKey( key ) ) { - game.LocalPlayer.HandleKeyDown( key ); + + if( !HandleBuiltinKey( key ) && !game.LocalPlayer.HandleKeyDown( key ) ) { + HandleHotkey( key ); } } } + void HandleHotkey( Key key ) { + string text; + bool more; + + if( Hotkeys.IsHotkey( key, game.Keyboard, out text, out more ) ) { + if( !more ) + game.Network.SendChat( text ); + else if( game.activeScreen is NormalScreen ) + ((NormalScreen)game.activeScreen).OpenTextInputBar( text ); + } + } + MouseButtonEventArgs simArgs = new MouseButtonEventArgs(); bool SimulateMouse( Key key, bool pressed ) { if( !(key == mapLeft || key == mapMiddle || key == mapRight ) ) diff --git a/ClassicalSharp/Hotkeys/HotkeyList.cs b/ClassicalSharp/Hotkeys/HotkeyList.cs new file mode 100644 index 000000000..3758db829 --- /dev/null +++ b/ClassicalSharp/Hotkeys/HotkeyList.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using OpenTK.Input; + +namespace ClassicalSharp.Hotkeys { + + /// Maintains the list of hotkeys defined by the client and by SetTextHotkey packets. + public sealed class HotkeyList { + + struct Hotkey { + public Key BaseKey; + public byte Flags; // ctrl 1, shift 2, alt 4 + public string Text; // contents to copy directly into the input bar + public bool MoreInput; // whether the user is able to enter further input + } + + List hotkeys = new List(); + + /// Creates or updates an existing hotkey with the given baseKey and modifier flags. + public void AddHotkey( Key baseKey, byte flags, string text, bool more ) { + if( !UpdateExistingHotkey( baseKey, flags, text, more ) ) + AddNewHotkey( baseKey, flags, text, more ); + } + + /// Removes an existing hotkey with the given baseKey and modifier flags. + /// Whether a hotkey with the given baseKey and modifier flags was found + /// and subsequently removed. + public bool RemoveHotkey( Key baseKey, byte flags ) { + for( int i = 0; i < hotkeys.Count; i++ ) { + Hotkey hKey = hotkeys[i]; + if( hKey.BaseKey == baseKey && hKey.Flags == flags ) { + hotkeys.RemoveAt( i ); + return true; + } + } + return false; + } + + bool UpdateExistingHotkey( Key baseKey, byte flags, string text, bool more ) { + for( int i = 0; i < hotkeys.Count; i++ ) { + Hotkey hKey = hotkeys[i]; + if( hKey.BaseKey == baseKey && hKey.Flags == flags ) { + hKey.Text = text; + hKey.MoreInput = more; + hotkeys[i] = hKey; + return true; + } + } + return false; + } + + void AddNewHotkey( Key baseKey, byte flags, string text, bool more ) { + Hotkey hotkey; + hotkey.BaseKey = baseKey; + hotkey.Flags = flags; + hotkey.Text = text; + hotkey.MoreInput = more; + hotkeys.Add( hotkey ); + // sort so that hotkeys with largest modifiers are first + hotkeys.Sort( (a, b) => b.Flags.CompareTo( a.Flags ) ); + } + + /// Determines whether a hotkey is active based on the given key, + /// and the currently active control, alt, and shift modifiers + public bool IsHotkey( Key key, KeyboardDevice keyboard, + out string text, out bool moreInput ) { + byte flags = 0; + if( keyboard[Key.ControlLeft] || keyboard[Key.ControlRight] ) flags |= 1; + if( keyboard[Key.ShiftLeft] || keyboard[Key.ShiftRight] ) flags |= 2; + if( keyboard[Key.AltLeft] || keyboard[Key.AltRight] ) flags |= 4; + + foreach( Hotkey hKey in hotkeys ) { + if( (hKey.Flags & flags) == hKey.Flags && hKey.BaseKey == key ) { + text = hKey.Text; + moreInput = hKey.MoreInput; + return true; + } + } + + text = null; + moreInput = false; + return false; + } + } +} diff --git a/ClassicalSharp/Hotkeys/LwjglToKey.cs b/ClassicalSharp/Hotkeys/LwjglToKey.cs new file mode 100644 index 000000000..b9a0f8711 --- /dev/null +++ b/ClassicalSharp/Hotkeys/LwjglToKey.cs @@ -0,0 +1,73 @@ +using System; +using OpenTK.Input; + +namespace ClassicalSharp.Hotkeys { + + /// Maps LWJGL keycodes to OpenTK keys. + public static class LwjglToKey { + + public static Key[] Map = new Key[256]; + + static int curIndex = 0; + static void Add( Key key ) { + Map[curIndex++] = key; + } + + static void Add( string value ) { + for( int i = 0; i < value.Length; i++ ) { + Add( (Key)(value[i] - 'A' + (int)Key.A) ); + } + } + + static void Skip( int amount ) { + curIndex += amount; + } + + static LwjglToKey() { + Add( Key.Unknown ); Add( Key.Escape ); + for( int i = 0; i < 9; i++ ) + Add( (Key)(i + Key.Number1) ); + Add( Key.Number0 ); Add( Key.Minus ); + Add( Key.Plus ); Add( Key.BackSpace ); + Add( Key.Tab ); Add( "QWERTYUIOP" ); + Add( Key.BracketLeft ); Add( Key.BracketRight ); + Add( Key.Enter ); Add( Key.ControlLeft ); + Add( "ASDFGHJKL" ); Add( Key.Semicolon ); + Add( Key.Quote ); Add( Key.Tilde ); + Add( Key.ShiftLeft ); Add( Key.BackSlash ); + Add( "ZXCVBNM" ); Add( Key.Comma ); + Add( Key.Period ); Add( Key.Slash ); + Add( Key.ShiftRight ); Skip( 1 ); // TODO: multiply + Add( Key.AltLeft ); Add( Key.Space ); + Add( Key.CapsLock ); + for( int i = 0; i < 10; i++ ) + Add( (Key)(i + Key.F1) ); + Add( Key.NumLock ); Add( Key.ScrollLock ); + Add( Key.Number7 ); Add( Key.Number8 ); + Add( Key.Number9 ); Add( Key.KeypadSubtract ); + Add( Key.Number4 ); Add( Key.Number5 ); + Add( Key.Number6 ); Add( Key.KeypadAdd ); + Add( Key.Number1 ); Add( Key.Number2 ); + Add( Key.Number3 ); Add( Key.Number0 ); + Add( Key.KeypadDecimal ); Skip( 3 ); + Add( Key.F11 ); Add( Key.F12 ); + Skip( 11 ); + for( int i = 0; i < 6; i++ ) + Add( (Key)(i + Key.F13) ); + Skip( 35 ); Add( Key.KeypadAdd ); + Skip( 14 ); Add( Key.KeypadEnter ); + Add( Key.ControlRight ); Skip( 23 ); + Add( Key.KeypadDivide ); Skip( 2 ); + Add( Key.AltRight ); Skip( 12 ); + Add( Key.Pause ); Skip( 1 ); + Add( Key.Home ); Add( Key.Up ); + Add( Key.PageUp ); Skip( 1 ); + Add( Key.Left ); Skip( 1 ); + Add( Key.Right ); Skip( 1 ); + Add( Key.End ); Add( Key.Down ); + Add( Key.PageDown ); Add( Key.Insert ); + Add( Key.Delete ); Skip( 7 ); + Add( Key.WinLeft ); Add( Key.WinRight ); + } + } +} \ No newline at end of file diff --git a/ClassicalSharp/Network/NetworkProcessor.CPE.cs b/ClassicalSharp/Network/NetworkProcessor.CPE.cs index fb6456249..398d95ae5 100644 --- a/ClassicalSharp/Network/NetworkProcessor.CPE.cs +++ b/ClassicalSharp/Network/NetworkProcessor.CPE.cs @@ -1,6 +1,7 @@ using System; using ClassicalSharp.TexturePack; using OpenTK.Input; +using ClassicalSharp.Hotkeys; namespace ClassicalSharp { @@ -57,7 +58,7 @@ namespace ClassicalSharp { "EmoteFix", "ClickDistance", "HeldBlock", "BlockPermissions", "SelectionCuboid", "MessageTypes", "CustomBlocks", "EnvColors", "HackControl", "EnvMapAppearance", "ExtPlayerList", "ChangeModel", - "EnvWeatherType", "PlayerClick", // NOTE: There are no plans to support TextHotKey. + "EnvWeatherType", "PlayerClick", "TextHotKey", }; void HandleCpeExtInfo() { @@ -126,6 +127,27 @@ namespace ClassicalSharp { game.Inventory.CanChangeHeldBlock = canChange; } + void HandleCpeSetTextHotkey() { + string label = reader.ReadAsciiString(); + string action = reader.ReadAsciiString(); + int keyCode = reader.ReadInt32(); + byte keyMods = reader.ReadUInt8(); + + if( keyCode < 0 || keyCode > 255 ) return; + Key key = LwjglToKey.Map[keyCode]; + if( key == Key.Unknown ) return; + + Console.WriteLine( "CPE Hotkey added: " + key + "," + keyMods + " : " + action ); + if( action == "" ) { + game.InputHandler.Hotkeys.RemoveHotkey( key, keyMods ); + } else if( action[action.Length - 1] == '\n' ) { // more input needed by user + action = action.Substring( 0, action.Length - 1 ); + game.InputHandler.Hotkeys.AddHotkey( key, keyMods, action, true ); + } else { + game.InputHandler.Hotkeys.AddHotkey( key, keyMods, action, false ); + } + } + void HandleCpeExtAddPlayerName() { short nameId = reader.ReadInt16(); string playerName = Utils.StripColours( reader.ReadAsciiString() ); diff --git a/ClassicalSharp/Network/NetworkProcessor.cs b/ClassicalSharp/Network/NetworkProcessor.cs index a98c23196..09df91b0d 100644 --- a/ClassicalSharp/Network/NetworkProcessor.cs +++ b/ClassicalSharp/Network/NetworkProcessor.cs @@ -455,7 +455,7 @@ namespace ClassicalSharp { HandleMessage, HandleKick, HandleSetPermission, HandleCpeExtInfo, HandleCpeExtEntry, HandleCpeSetClickDistance, - HandleCpeCustomBlockSupportLevel, HandleCpeHoldThis, null, + HandleCpeCustomBlockSupportLevel, HandleCpeHoldThis, HandleCpeSetTextHotkey, HandleCpeExtAddPlayerName, HandleCpeExtAddEntity, HandleCpeExtRemovePlayerName, HandleCpeEnvColours, HandleCpeMakeSelection, HandleCpeRemoveSelection, HandleCpeSetBlockPermission, HandleCpeChangeModel, HandleCpeEnvSetMapApperance,