Log in

View Full Version : KeyPressRelay



Narcoz
03-03-2008, 12:21 PM
Synopsis: AutoHotKey Server-script to relay a subset of keys from one computer where you play World of Warcraft (or other games with small changes) with your main, to another computer where the WoW-alt-sessions are running together with the Client-script.

Default a..z and 0..9 keys are relayed, but it's very easy to extend this for any possibly combination that suits your setup

I've submitted these two scrips before but i did it in the wrong area (beginners) and besides it wasn't even tagged up as code. This it the updated current version of this software that i use myself:

Usage instructions below the source.

Server script:


; -------------------------------------------------
; ------------SERVERSCRIPT------------------
; -------------------------------------------------
; CONFIGURATION SECTION:

; Specify Your own Network's address and port.
;Network_Address = 127.0.0.1
Network_Address = 0.0.0.0
Network_Port = 8765
; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------


killbroadcast = 0
KEY_0=0
KEY_1=1
KEY_2=2
KEY_3=3
KEY_4=4
KEY_5=5
KEY_6=6
KEY_7=7
KEY_8=8
KEY_9=9
KEY_A=10
KEY_B=11
KEY_C=12
KEY_D=13
KEY_E=14
KEY_F=15
KEY_G=16
KEY_H=17
KEY_I=18
KEY_J=19
KEY_K=20
KEY_L=21
KEY_M=22
KEY_N=23
KEY_O=24
KEY_P=25
KEY_Q=26
KEY_R=27
KEY_S=28
KEY_T=29
KEY_U=30
KEY_V=31
KEY_W=32
KEY_X=33
KEY_Y=34
KEY_Z=35


conectioncheck=0

;Gui, Add, Text,, Send:
;Gui, Add, Edit, w100 vSendText
;Gui, Add, Text,, Repeat:
;Gui, Add, Edit, w40 vRepeat, 5
;Gui, Add, Text,, Delay (ms):
;Gui, Add, Edit, w40 vDelay, 50
;Gui, Add, Button, gSendviaNet, Send
Gui, Add, Text,, Key sender server (transmits wow keystrokes to client):
Gui, Show

Gosub Connection_Init
return

Connection_Init:
OnExit, ExitSub ; For connection cleanup purposes.

; set up a very basic server:
socket := PrepareForIncomingConnection(Network_Address, Network_Port)
if socket = -1 ; Connection failed (it already displayed the reason).
ExitApp

; Find this script's main window:
Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off

; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ = 1 ; Received when data is available to be read.
;FD_WRITE =
FD_CLOSE = 32 ; Received when connection has been closed.
FD_CONNECT = 20 ; Received when connection has been made.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_CLOSE|FD_CONNECT)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
ExitApp
}

Loop ; Wait for incomming connections
{
; accept requests that are in the pipeline of the socket
conectioncheck := DllCall("Ws2_32\accept", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
; Ws2_22/accept returns the new Connection-Socket if a connection request was in the pipeline
; on failure it returns an negative value
if conectioncheck > 1
{
MsgBox Incoming connection accepted
break
}
sleep 500 ; wait half 1 second then accept again
}
return

;SendviaNet:
;Gui, Submit, NoHide
;SendData(conectioncheck,SendText,Repeat,Delay)
;SentText =
;return

PrepareForIncomingConnection(IPAddress, Port)
; This can connect to most types of TCP servers, not just Network.
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative.
result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
; Since WSAStartup() will likely be the first Winsock function called by this script,
; check ErrorLevel to see if the OS has Winsock 2.0 available:
if ErrorLevel
{
MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
return -1
}
if result ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
{
MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}

AF_INET = 2
SOCK_STREAM = 1
IPPROTO_TCP = 6
socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
if socket = -1
{
MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}

; Prepare for connection:
SizeOfSocketAddress = 16
VarSetCapacity(SocketAddress, SizeOfSocketAddress)
InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family
InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port
InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr

; Bind to socket:
if DllCall("Ws2_32\bind", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
{
MsgBox % "bind() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}
if DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN")
{
MsgBox % "LISTEN() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}

return socket ; Indicate success by returning a valid socket ID rather than -1.
}

SendData(wParam,SendData, Repeat, Delay)
{
socket := wParam
; SendDataSize := VarSetCapacity(SendData)
; SendDataSize += 1
Loop % Repeat
{
; SendIt := SendData . "(" . A_Index . ")"
; SendDataSize := VarSetCapacity(SendIt)
; SendDataSize += 1

; MsgBox sending %SendIt% to the socket

; sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendIt, "Int", SendDatasize, "Int", 0)
; sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendIt, "Int", strlen(SendIt), "Int", 0)
sendret := DllCall("Ws2_32\send", "UInt", socket, "Str", SendData, "Int", strlen(SendData), "Int", 0)
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if WinsockError <> 0 ; WSAECONNRESET, which happens when Network closes via system shutdown/logoff.
; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
MsgBox % "send() indicated Winsock error " . WinsockError
sleep,Delay
}
;send( sockConnected,> welcome, strlen(welcome) + 1, NULL);
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}


~Pause::
KeyWait, Pause, D
If(%killbroadcast% = 0)
{
killbroadcast = 1
}
else
{
killbroadcast = 0
;WinGet, targetWindowIds, List, %windowTitle%
}
Return


~0::
KeyWait, 0
SendKey(KEY_0)
Return

~1::
KeyWait, 1
SendKey(KEY_1)
Return

~2::
KeyWait, 2
SendKey(KEY_2)
Return

~3::
KeyWait, 3
SendKey(KEY_3)
Return

~4::
KeyWait, 4
SendKey(KEY_4)
Return

~5::
KeyWait, 5
SendKey(KEY_5)
Return

~6::
KeyWait, 0
SendKey(KEY_6)
Return

~7::
KeyWait, 7
SendKey(KEY_7)
Return

~8::
KeyWait, 8
SendKey(KEY_8)
Return

~9::
KeyWait, 9
SendKey(KEY_9)
Return

~A::
KeyWait, A
SendKey(KEY_A)
Return

~B::
KeyWait, B
SendKey(KEY_B)
Return

~C::
KeyWait, C
SendKey(KEY_C)
Return

~D::
KeyWait, D
SendKey(KEY_D)
Return

~E::
KeyWait, E
SendKey(KEY_E)
Return

~F::
KeyWait, F
SendKey(KEY_F)
Return

~G::
KeyWait, G
SendKey(KEY_G)
Return

~H::
KeyWait, H
SendKey(KEY_H)
Return

~I::
KeyWait, I
SendKey(KEY_I)
Return

~J::
KeyWait, J
SendKey(KEY_J)
Return

~K::
KeyWait, K
SendKey(KEY_K)
Return

~L::
KeyWait, L
SendKey(KEY_L)
Return

~M::
KeyWait, M
SendKey(KEY_M)
Return

~N::
KeyWait, N
SendKey(KEY_N)
Return

~O::
KeyWait, O
SendKey(KEY_O)
Return

~P::
KeyWait, P
SendKey(KEY_P)
Return

~Q::
KeyWait, Q
SendKey(KEY_Q)
Return

~R::
KeyWait, R
SendKey(KEY_R)
Return

~S::
KeyWait, S
SendKey(KEY_S)
Return

~T::
KeyWait, T
SendKey(KEY_T)
Return

~U::
KeyWait, U
SendKey(KEY_U)
Return

~V::
KeyWait, V
SendKey(KEY_V)
Return

~W::
KeyWait, W
SendKey(KEY_W)
Return

~X::
KeyWait, X
SendKey(KEY_X)
Return

~Y::
KeyWait, Y
SendKey(KEY_Y)
Return

~Z::
KeyWait, Z
SendKey(KEY_Z)
Return


SendKey(key)
{
global killbroadcast
global conectioncheck
If(%killbroadcast% = 0)
{
killbroadcast = 1 ; stop other threads from sending right now!
if conectioncheck > 1 ; don't send before a client has connected.
{
SendData(conectioncheck,key,1,1)
}
killbroadcast = 0 ; Reactivate sending of keys.
}
}


guiclose:
ExitSub: ; This subroutine is called automatically when the script exits for any reason.
; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
; deallocated as if closesocket was called."
DllCall("Ws2_32\WSACleanup")
ExitApp

Narcoz
03-03-2008, 12:24 PM
Client script:



; -------------------------------------------------
;-----------CLIENTSCRIPT----------------------
; -------------------------------------------------
; CONFIGURATION SECTION:

; Specify address and port of the server.
Network_Address = 127.0.0.1
Network_Port = 8765
windowTitle=Warcraft
; ----------------------------
; END OF CONFIGURATION SECTION
; ----------------------------


KEY_0=0
KEY_1=1
KEY_2=2
KEY_3=3
KEY_4=4
KEY_5=5
KEY_6=6
KEY_7=7
KEY_8=8
KEY_9=9
KEY_A=10
KEY_B=11
KEY_C=12
KEY_D=13
KEY_E=14
KEY_F=15
KEY_G=16
KEY_H=17
KEY_I=18
KEY_J=19
KEY_K=20
KEY_L=21
KEY_M=22
KEY_N=23
KEY_O=24
KEY_P=25
KEY_Q=26
KEY_R=27
KEY_S=28
KEY_T=29
KEY_U=30
KEY_V=31
KEY_W=32
KEY_X=33
KEY_Y=34
KEY_Z=35


SetTitleMatchMode 2 ; match anywhere in the title

WinGet, targetWindowIds, list, %windowTitle%


Gui, Add, Button, gConnection_Init, Connect
Gui, Add, Edit, w150 vServerIp
;Gui, Add, Button, gClear, Clear
Gui, Add, Text,, Key reciever client (transfers keystrokes from the server to the client(s)):
Gui, Show

LinesReceived:=0
return

Connection_Init:
OnExit, ExitSub ; For connection cleanup purposes.

; Connect to any type of server:
Gui, Submit, NoHide
;socket := ConnectToAddress(Network_Address, Network_Port)
socket := ConnectToAddress(ServerIp, Network_Port)
if socket = -1 ; Connection failed (it already displayed the reason).
ExitApp

; Find this script's main window:
Process, Exist ; This sets ErrorLevel to this script's PID (it's done this way to support compiled scripts).
DetectHiddenWindows On
ScriptMainWindowId := WinExist("ahk_class AutoHotkey ahk_pid " . ErrorLevel)
DetectHiddenWindows Off

; When the OS notifies the script that there is incoming data waiting to be received,
; the following causes a function to be launched to read the data:
NotificationMsg = 0x5555 ; An arbitrary message number, but should be greater than 0x1000.
OnMessage(NotificationMsg, "ReceiveData")

; Set up the connection to notify this script via message whenever new data has arrived.
; This avoids the need to poll the connection and thus cuts down on resource usage.
FD_READ = 1 ; Received when data is available to be read.
FD_CLOSE = 32 ; Received when connection has been closed.
if DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", ScriptMainWindowId, "UInt", NotificationMsg, "Int", FD_READ|FD_CLOSE)
{
MsgBox % "WSAAsyncSelect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
ExitApp
}
return

;Clear:
;ShowReceived=
;LinesReceived:=0
;GuiControl,, MyEdit,
;return

ConnectToAddress(IPAddress, Port)
; Returns -1 (INVALID_SOCKET) upon failure or the socket ID upon success.
{
VarSetCapacity(wsaData, 32) ; The struct is only about 14 in size, so 32 is conservative.
result := DllCall("Ws2_32\WSAStartup", "UShort", 0x0002, "UInt", &wsaData) ; Request Winsock 2.0 (0x0002)
; Since WSAStartup() will likely be the first Winsock function called by this script,
; check ErrorLevel to see if the OS has Winsock 2.0 available:
if ErrorLevel
{
MsgBox WSAStartup() could not be called due to error %ErrorLevel%. Winsock 2.0 or higher is required.
return -1
}
if result ; Non-zero, which means it failed (most Winsock functions return 0 upon success).
{
MsgBox % "WSAStartup() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}

AF_INET = 2
SOCK_STREAM = 1
IPPROTO_TCP = 6
socket := DllCall("Ws2_32\socket", "Int", AF_INET, "Int", SOCK_STREAM, "Int", IPPROTO_TCP)
if socket = -1
{
MsgBox % "socket() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError")
return -1
}

; Prepare for connection:
SizeOfSocketAddress = 16
VarSetCapacity(SocketAddress, SizeOfSocketAddress)
InsertInteger(2, SocketAddress, 0, AF_INET) ; sin_family
InsertInteger(DllCall("Ws2_32\htons", "UShort", Port), SocketAddress, 2, 2) ; sin_port
InsertInteger(DllCall("Ws2_32\inet_addr", "Str", IPAddress), SocketAddress, 4, 4) ; sin_addr.s_addr

; Attempt connection:
if DllCall("Ws2_32\connect", "UInt", socket, "UInt", &SocketAddress, "Int", SizeOfSocketAddress)
{
MsgBox % "connect() indicated Winsock error " . DllCall("Ws2_32\WSAGetLastError") . "?"
return -1
}
return socket ; Indicate success by returning a valid socket ID rather than -1.
}

ReceiveData(wParam, lParam)
; By means of OnMessage(), this function has been set up to be called automatically whenever new data
; arrives on the connection.
{
Critical
global ShowReceived
global MyEdit
global LinesReceived

socket := wParam
ReceivedDataSize = 4096 ; Large in case a lot of data gets buffered due to delay in processing previous data.
VarSetCapacity(ReceivedData, ReceivedDataSize, 0) ; 0 for last param terminates string for use with recv().
Data := ""
Loop ; This loop solves the issue of the notification message being discarded due to thread-already-running.
{
ReceivedDataLength := DllCall("Ws2_32\recv", "UInt", socket, "Str", ReceivedData, "Int", ReceivedDataSize, "Int", 0)
if ReceivedDataLength = 0 ; The connection was gracefully closed,
ExitApp ; The OnExit routine will call WSACleanup() for us.
if ReceivedDataLength = -1
{
WinsockError := DllCall("Ws2_32\WSAGetLastError")
if ( WinsockError = 10035 ) { ; WSAEWOULDBLOCK, which means "no more data to be read".
break
}
if WinsockError <> 10054 ; WSAECONNRESET, which happens when Network closes via system shutdown/logoff.
; Since it's an unexpected error, report it. Also exit to avoid infinite loop.
MsgBox % "recv() indicated Winsock error " . WinsockError
ExitApp ; The OnExit routine will call WSACleanup() for us.
}
Data .= ReceivedData
}
; Otherwise, process the data received.
Loop, parse, Data, `n, `r
{
;LinesReceived++
;if (LinesReceived = 1) {
; ShowReceived = %LinesReceived%: %A_LoopField%
;} else {
; ShowReceived = %ShowReceived%`n%LinesReceived%: %A_LoopField%
;}
;Tooltip % ShowReceived
;GuiControl,, MyEdit, %ShowReceived%
PressKey(A_LoopField)
}
return 1 ; Tell the program that no further processing of this message is needed.
}

InsertInteger(pInteger, ByRef pDest, pOffset = 0, pSize = 4)
; The caller must ensure that pDest has sufficient capacity. To preserve any existing contents in pDest,
; only pSize number of bytes starting at pOffset are altered in it.
{
Loop %pSize% ; Copy each byte in the integer into the structure as raw binary data.
DllCall("RtlFillMemory", "UInt", &pDest + pOffset + A_Index-1, "UInt", 1, "UChar", pInteger >> 8*(A_Index-1) & 0xFF)
}

;Send specified key to all instances of the configured program.
PressKey(key)
{
global targetWindowIds
Loop, %targetWindowIds%
{
target_window := targetWindowIds%A_index%
InternalPressKey(key, target_window)
}
}

InternalPressKey(key, target_window)
{
global

Narcoz
03-03-2008, 12:25 PM
The rest of the client script (didn't fit in 1 post):




if key = %KEY_0%
{
ControlSend,, 0, ahk_id %target_window%
}
else
if key = %KEY_1%
{
ControlSend,, 1, ahk_id %target_window%
}
else
if key = %KEY_2%
{
ControlSend,, 2, ahk_id %target_window%
}
else
if key = %KEY_3%
{
ControlSend,, 3, ahk_id %target_window%
}
else
if key = %KEY_4%
{
ControlSend,, 4, ahk_id %target_window%
}
else
if key = %KEY_5%
{
ControlSend,, 5, ahk_id %target_window%
}
else
if key = %KEY_6%
{
ControlSend,, 6, ahk_id %target_window%
}
else
if key = %KEY_7%
{
ControlSend,, 7, ahk_id %target_window%
}
else
if key = %KEY_8%
{
ControlSend,, 8, ahk_id %target_window%
}
else
if key = %KEY_9%
{
ControlSend,, 9, ahk_id %target_window%
}
else
if key = %KEY_A%
{
ControlSend,, a, ahk_id %target_window%
}
else
if key = %KEY_B%
{
ControlSend,, b, ahk_id %target_window%
}
else
if key = %KEY_C%
{
ControlSend,, c, ahk_id %target_window%
}
else
if key = %KEY_D%
{
ControlSend,, d, ahk_id %target_window%
}
else
if key = %KEY_E%
{
ControlSend,, e, ahk_id %target_window%
}
else
if key = %KEY_F%
{
ControlSend,, f, ahk_id %target_window%
}
else
if key = %KEY_G%
{
ControlSend,, g, ahk_id %target_window%
}
else
if key = %KEY_H%
{
ControlSend,, h, ahk_id %target_window%
}
else
if key = %KEY_I%
{
ControlSend,, i, ahk_id %target_window%
}
else
if key = %KEY_J%
{
ControlSend,, j, ahk_id %target_window%
}
else
if key = %KEY_K%
{
ControlSend,, k, ahk_id %target_window%
}
else
if key = %KEY_L%
{
ControlSend,, l, ahk_id %target_window%
}
else
if key = %KEY_M%
{
ControlSend,, m, ahk_id %target_window%
}
else
if key = %KEY_N%
{
ControlSend,, n, ahk_id %target_window%
}
else
if key = %KEY_O%
{
ControlSend,, o, ahk_id %target_window%
}
else
if key = %KEY_P%
{
ControlSend,, p, ahk_id %target_window%
}
else
if key = %KEY_Q%
{
ControlSend,, q, ahk_id %target_window%
}
else
if key = %KEY_R%
{
ControlSend,, r, ahk_id %target_window%
}
else
if key = %KEY_S%
{
ControlSend,, s, ahk_id %target_window%
}
else
if key = %KEY_T%
{
ControlSend,, t, ahk_id %target_window%
}
else
if key = %KEY_U%
{
ControlSend,, u, ahk_id %target_window%
}
else
if key = %KEY_V%
{
ControlSend,, v, ahk_id %target_window%
}
else
if key = %KEY_W%
{
ControlSend,, w, ahk_id %target_window%
}
else
if key = %KEY_X%
{
ControlSend,, x, ahk_id %target_window%
}
else
if key = %KEY_Y%
{
ControlSend,, y, ahk_id %target_window%
}
else
if key = %KEY_Z%
{
ControlSend,, z, ahk_id %target_window%
}

} ; END OF InternalPressKey()


guiclose:
ExitSub: ; This subroutine is called automatically when the script exits for any reason.
; MSDN: "Any sockets open when WSACleanup is called are reset and automatically
; deallocated as if closesocket was called."
DllCall("Ws2_32\WSACleanup")
ExitApp

Narcoz
03-03-2008, 12:25 PM
Usage instructions:

1. Start a single wow on your main machine (the one you play on with a single session). Its here you press your physical keyboard.
2. Start all WoWs you intend to run on your second machine. This is the machine you want you keystrokes send remotely to.
3. Login all WoWs
4. Start the server on your main machine (the one mentioned in point #1)
5. Start the client on your alt machine (the one mentioned in point #2)
6. Write the IP-address of the main machine (#1) in the client window on the alt machine (#2) and press the connect button
7. On the main machine (#1) you should now get a "client connected" message. Confirm it and start playing.