Does anyone know of any "hook" into the game client? C or C++ is fine
- I can wrap that in a python module - though an existing Python means
would be fantastic. I've been trying to use the win32api.SendKeys()
interface, but apparently it doesn't work with the game client - which
may be to be expected, since a lot of game clients tend to run closer
to the metal than office stuff.
Cheers!
NWNx should be your solution: http://nwnx.org/index.php?id=nwnx2
oh, and can you publish your solution once it's done? I'd be quite
interested in it...
NWNx, lovely though it is (and it is lovely), interfaces with the
server. I have it running on my own server. However, this project is
meant for a server upon which I GM but to which I do not have direct
access. Hence the bridge to the DM client. The server for which I GM
has a number (okay, a HUGE number) of text-based commands available,
so just being able to enter information into the chatbar would afford
me a fair amount of remote control over my client and avatar.
As I said, I already have a way to get information OUT... now I just
need to get it IN.
Thanks for the tip regardless, Christian.
Oh, and I'll post the code I have so far as soon as I get home -
suitably edited, of course. :)
> NWNx, lovely though it is (and it is lovely), interfaces with the
> server. I have it running on my own server. However, this project is
> meant for a server upon which I GM but to which I do not have direct
> access. Hence the bridge to the DM client. The server for which I GM
ah, I overlooked the "client" part, or rather simply assumed that
you'd have access to the server too :).
have you looked at AutoIt (http://www.autoitscript.com/autoit3/) ?
That's a tool that can send keystrokes, mouse movements and such to
any applications, as well as firing other scripts. You'd probably have
to create some "glue" application that fetches the input you give and
then passes it to a AutoIt macro as a command line parameter which
then puts it as text into your DM client.
My own preference is for AutoHotKey (http://www.autohotkey.com/), a
remarkably versatile scripting tool that I use to aid in GMing and
that I customized a few other automation scripts for. Unfortunately,
neither provides source, so I couldn't determine how to work directly
from python through their example. I did briefly consider the "glue"
you suggested, but then, inspiration (well, perseverance and carefully-
worded Google queries) struck!
By using ctypes.windll.user32.SendInput(), I was able to inject
keypresses and mouse movements BEFORE DirectInput (which is what I
assume NWN uses, because it fits all the signs and employs DirectX).
Right now, it's a bit hacked-together, because my laptop starts to
complain when I run the GM client on it, but I don't have access to my
principal computer at all the times that I might want to run my remote
IRC GM tool. As a result, task-switching isn't instantaneous, so I
have to allow a half-second pause before each command to make sure the
laptop has caught up.
What follows is the "NWNPost.py" section of the code. Again, this is
just roughly hacked together. Hopefully I'll have a version with a
bit more polish to post in a few days.
******************************************************************************
#NWNPost.py
from ctypes import *
import time
import os
import win32com.client
SHIFT_PAUSE = 0.002
ENTER_PAUSE = 0.1
PUL = POINTER(c_ulong)
class KeyBdInput(Structure):
_fields_ = [("wVk", c_ushort),
("wScan", c_ushort),
("dwFlags", c_ulong),
("time", c_ulong),
("dwExtraInfo", PUL)]
class HardwareInput(Structure):
_fields_ = [("uMsg", c_ulong),
("wParamL", c_short),
("wParamH", c_ushort)]
class MouseInput(Structure):
_fields_ = [("dx", c_long),
("dy", c_long),
("mouseData", c_ulong),
("dwFlags", c_ulong),
("time",c_ulong),
("dwExtraInfo", PUL)]
class Input_I(Union):
_fields_ = [("ki", KeyBdInput),
("mi", MouseInput),
("hi", HardwareInput)]
class Input(Structure):
_fields_ = [("type", c_ulong),
("ii", Input_I)]
KEYEVENTF_KEYUP = 0x2
KEYEVENTF_UNICODE = 0x4
KEYEVENTF_SCANCODE = 0x8
MAPVK_VK_TO_VSC = 0
def SendInput(txt):
i = Input()
i.type = 1
extra = c_ulong(0)
pextra = pointer(extra)
shift_key = windll.user32.MapVirtualKeyW(0x10, MAPVK_VK_TO_VSC)
for c in txt:
vk = windll.user32.VkKeyScanW(ord(c))
shift = (vk & 0xff00) >> 8
sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
i.ii.ki.wVk = 0
i.ii.ki.wScan = sc
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE
i.ii.ki.time = 0
i.ii.ki.dwExtraInfo = pextra
if shift:
time.sleep(SHIFT_PAUSE)
i.ii.ki.wScan = shift_key
windll.user32.SendInput(1, byref(i), sizeof(i))
i.ii.ki.wScan = sc
time.sleep(SHIFT_PAUSE)
windll.user32.SendInput(1, byref(i), sizeof(i))
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
windll.user32.SendInput(1, byref(i), sizeof(i))
if shift:
time.sleep(SHIFT_PAUSE)
i.ii.ki.wScan = shift_key
windll.user32.SendInput(1, byref(i), sizeof(i))
def SendEnter():
i = Input()
i.type = 1
extra = c_ulong(0)
pextra = pointer(extra)
vk = 0x0D
sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
i.ii.ki.wVk = 0
i.ii.ki.wScan = sc
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE
i.ii.ki.time = 0
i.ii.ki.dwExtraInfo = pextra
windll.user32.SendInput(1, byref(i), sizeof(i))
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
windll.user32.SendInput(1, byref(i), sizeof(i))
def SendKeyPress(key):
i = Input()
i.type = 1
extra = c_ulong(0)
pextra = pointer(extra)
vk = windll.user32.VkKeyScanW(ord(key))
sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
i.ii.ki.wVk = 0
i.ii.ki.wScan = sc
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE
i.ii.ki.time = 0
i.ii.ki.dwExtraInfo = pextra
windll.user32.SendInput(1, byref(i), sizeof(i))
def SendKeyRelease(key):
i = Input()
i.type = 1
extra = c_ulong(0)
pextra = pointer(extra)
vk = windll.user32.VkKeyScanW(ord(key))
sc = windll.user32.MapVirtualKeyW(vk&0xff, MAPVK_VK_TO_VSC)
i.ii.ki.wVk = 0
i.ii.ki.wScan = sc
i.ii.ki.time = 0
i.ii.ki.dwExtraInfo = pextra
i.ii.ki.dwFlags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP
windll.user32.SendInput(1, byref(i), sizeof(i))
def SendDMChat(text):
mytext = "/dm " + text
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate("Neverwinter Nights")
time.sleep(0.25)
SendEnter()
time.sleep(ENTER_PAUSE)
SendInput(mytext)
time.sleep(ENTER_PAUSE)
SendEnter()
def SendPlayerChat(player, text):
mytext = "/t \"" + player + "\" " + text
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate("Neverwinter Nights")
time.sleep(0.25)
SendEnter()
time.sleep(ENTER_PAUSE)
SendInput(mytext)
time.sleep(ENTER_PAUSE)
SendEnter()
def SendNormalChat(text):
shell = win32com.client.Dispatch("WScript.Shell")
shell.AppActivate("Neverwinter Nights")
time.sleep(0.25)
SendEnter()
time.sleep(ENTER_PAUSE)
SendInput(text)
time.sleep(ENTER_PAUSE)
SendEnter()
if __name__ == "__main__":
SendNormalChat("This is a test.")