Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new library for communicating with other programs via Lua #1080

Merged
merged 2 commits into from
Mar 13, 2018

Conversation

Ashafix
Copy link
Contributor

@Ashafix Ashafix commented Dec 23, 2017

A library which allows easier communication between Emuhawk and other programs via Lua

  • Socket communication
  • HTTP communication (GET/POST)
  • Memory mapped files

All new functions are written in C# and exposed in Lua.

All Lua functions have "unit tests".
A simple Python package which tests the communications features is available here:
https://github.com/Ashafix/PyEmuHawk

New Lua functions
SocketServerScreenShot()
sends a screenshot to the Socket server

SocketServerScreenShotResponse()
sends a screenshot to the Socket server and retrieves the response

SocketServerSend(message)
sends a string [message] to the Socket server

SocketServerResponse()
receives a message from the Socket server

SocketServerSuccessful()
returns the status of the last Socket server action
boolean response: true or false

SocketServerSetTimeout(timeout)
sets the timeout in milliseconds for receiving messages

mmfSetFilename(filename)
Sets the filename for the screenshots

mmfGetFilename()
Gets the filename for the screenshots

mmfScreenshot()
Saves screenshot to memory mapped file

mmfWrite(filename, message)
Writes a string [message] to a memory mapped file [filename]

mmfRead(filename, expectedLength)
Reads a string from a memory mapped file [filename]. The expected length needs to be provided to deliver accurate reads.

httpTest()
tests HTTP connections

httpTestGet()
tests the default HTTP GET connection

httpGet(url)
makes a HTTP GET request, i.e. retrieves information from a URL

httpPost(url, message)
makes a HTTP POST request to the url and sends the string as {payload: message}

httpPostScreenshot()
HTTP POST requests to the default POST URL and sends the screenshot as base64 encoded string in {screenshot: base64_encoded_string}

httpSetTimeout(timeout)
Sets HTTP timeout in milliseconds

httpSetPostUrl(url)
Sets HTTP POST URL

httpSetGetUrl(url)
Sets HTTP GET URL

httpGetPostUrl()
Gets HTTP POST URL

httpGetGetUrl()
Gets HTTP GET URL

Changes

  • ArgParser.cs
    new options introduced
    --socket_port
    Sets the default socket port
    --socket_ip
    Sets the default socket IP
    --mmf
    Sets the default memory mapped filename
    --url_get
    Sets the default URL for GET requests
    --url_post
    Sets the default URL for POST requests

  • New dependencies
    System.Net.Http

New modules

  • Communication.cs
  • EmuLuaLibrary.Communication.cs
    exposes most the functions in Communication.cs as via comm.FunctionName() in Lua

Testing

  • Use a ROM, e.g. this public domain ROM
    http://pdroms.de/files/supernintendoentertainmentsystem/airwolf-25-08-1996
  • Start a command line interface, cd into your directory where emuhawk.exe is located and start Emuhawk
    emuhawk.exe --lua=TestCommunication_All.lua
  • Alternatively:
  • Start Emuhawk as usual
  • Start any ROM
  • Open the Lua console
  • Open the Lua script (TestCommunication_All.lua)
  • If TestCommunication_All.lua is not in the same directory as emuhawk.exe, provide the full path (e.g. --lua=C:\temp\TestCommunication_All.lua) or copy TestCommunication_All.lua to the same directory as emuhawk.exe
  • Expected output:

##########################################################
GET URL: http://192.168.178.39:9876/get
POST URL: http://192.168.178.39:9876/posts

Checking GET URL change
Get URL was successfully changed

Checking POST URL change
Post URL was successfully changed

Checking GET request
GET seems to work

Checking memory mapped filed
Memory mapped file was successfully written
MMF filename: BizhawkTemp_main
MMF filename successfully changed
Writing to MMF
MMF read and read OK

Tests finished
Please run TestCommunication_All.lua with the supplied Python server for a more comprehensive test

Using Python:
Start PyEmuhawk.py (https://github.com/Ashafix/PyEmuHawk) with Python3

Run EmuHawk.exe from the command line
Change the IP address to your localhost IP address
emuhawk.exe --lua=BizHawk\Assets\Lua\UnitTests\TestCommunication_All.lua --socket_ip=192.168.178.39 --socket_port=9999 --url_post=http://192.168.178.39:9876/posts --url_get=http://192.168.178.39:9876/get YourROM.sfc

Expected output:

Baseline: 0.78 secs
Socket server: 0.101 secs
Memory mapped files: 0.066 secs
Testing HTTP server
HTTP get: 0.031 secs
HTTP post: 0.309 secs
#####################
Testing HTTP server response
HTTP GET looks fine: No errors occurred
HTTP POST looks fine: No errors occurred
Trying to find minimal timeout for Socket server
Best timeout: 1 msecs
Best time: 0.184 secs

@vadosnaprimer
Copy link
Contributor

vadosnaprimer commented Dec 24, 2017

Fantastic job!!! Thanks a lot! Probably netplay will ever become possible with all these functions.

Why do the socket functions start from a capital letter though? What do you think of making them, for example, socketServerScreenShot() for consistency?

@Ashafix
Copy link
Contributor Author

Ashafix commented Dec 24, 2017

@vadosnaprimer : the Lua functions now all have the same camelCase names.
Good idea with netplay, didn't think about it, just wrote it for machine learning.

@vadosnaprimer
Copy link
Contributor

vadosnaprimer commented Dec 24, 2017

Netplay is VERY complicated, it has so much to be done to make everything perfect, no emulator has perfect netplay so far. Here's the detailed break down, to give you some insight:
http://tasvideos.org/forum/viewtopic.php?p=452926#452926
I'm not requiring netplay, just mentioning in case someone is interested.

EDIT: Sorry, wrong button.

@vadosnaprimer
Copy link
Contributor

@Ashafix can you resolve the conflict? I tried, but github's tool for this doesn't seem to save the file.

@zeromus
Copy link
Contributor

zeromus commented Mar 13, 2018

I'm working on it

@zeromus
Copy link
Contributor

zeromus commented Mar 13, 2018

This is great. I'm going to merge it, but here's my feedback

  1. I wish there were lua APIs for setting the same parameters set by commandline (easy)

  2. I wish the default connecting-to IP would be set from localhost until otherwise specified, keeping in mind that's totally nontrivial since every host can have several

  3. I wish this was architected in a more OO way (I know, it's probably not clean or simple with the way we have lua integrated) so that you could connect to several different servers; this would help facilitate using multiple worker nodes. Could be done by changing all the lua functions to work on a handle or something; there are similar examples in other lua libraries.

@zeromus zeromus merged commit 07da017 into TASEmulators:master Mar 13, 2018
zeromus added a commit that referenced this pull request Mar 13, 2018
# Conflicts:
#	BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj

merges PR #1080
closes PR #1080
(let's see if either of these can do it)
@Ashafix
Copy link
Contributor Author

Ashafix commented Mar 13, 2018

Thanks @zeromus for resolving the conflicts!

I'll start working on the suggested changes when I find some time. 1 and 2 should be probably doable but 3 would require some more input from other developers because it will surely break some other workflows.

@vadosnaprimer
Copy link
Contributor

@Ashafix you can simply hang at #bizhawk and discuss everything before going untrusted directions.

@zeromus
Copy link
Contributor

zeromus commented Mar 13, 2018

Well, nobody's using this feature yet but you, so it won't break anyone's workflow but yours. It's up to you. But I can tell you, from my perspective, there is a high level of interest in doing AI things with bizhawk that take a bunch of horsepower. Supporting multiple compute nodes (maybe even have some kind of workload manager done in lua or c#) would probably be useful to many people, even if it was a very canned solution, and could unlock a lot of interesting things.

I'm giving you commit permissions which you're free to use to iterate on your communication framework in this repository directly. Please see us on IRC at #bizhawk before tackling any other topics.

@vadosnaprimer
Copy link
Contributor

Congrats :)

@spiiin
Copy link

spiiin commented Apr 20, 2018

Did you have any ideas about callbacks implementation? For example, for creating interactively tool on C#/python, that can act as event.onmemoryread/write/execute lua functions.

@ezBeanie
Copy link

Can someone explain to me how exactly i can make use of the socketServer commands? i find myself unable to get any communication to my little python websocket server...

@Ashafix
Copy link
Contributor Author

Ashafix commented Feb 24, 2019

@ezBeanie :

Settings for Emuhawk:
--socket_ip=192.168.56.1 --socket_port=9990 --url_get=http://192.168.56.1:9876/get --url_post=http://192.168.56.1:9876/post

socket_ip is the IP of your socket server
socket_port is the port of your socket server
url_get and url_post are only for HTTP communication, so you can ignore them for now

  • Start Emuhawk from the command line with the parameters from above
  • Open a Lua console
  • Send a screenshot or message to the Python socket server:
    comm.socketServerScreenShotResponse()
    (it should return 'ack')
    comm.socketServerSend('hello')
    (it should show 'Sent : 5 bytes' but no response message)

Good luck!

@ezBeanie
Copy link

ezBeanie commented Feb 24, 2019

I tried that, HTTP Unit test and MMF test all work fine (i changed the order in PyEmuHawk.py to get any output):
Baseline: 1.666 secs Memory mapped files: 0.455 secs Testing HTTP server HTTP get: 1.028 secs HTTP post: 0.623 secs

but the socket test throws the following error:

NLua.Exceptions.LuaException: unprotected error in call to Lua API (0) at NLua.Lua.PanicCallback(IntPtr luaState) at lua_error(lua_State* ) at NLua.ObjectTranslator.throwError(IntPtr luaState, Object e) at NLua.Lua.SetPendingException(Exception e) at NLua.LuaMethodWrapper.call(IntPtr luaState) at NLua.MetaFunctions.runFunctionDelegate(IntPtr luaState) at lua_resume(lua_State* , Int32 ) at BizHawk.Client.EmuHawk.EmuLuaLibrary.ResumeScript(Lua script) at BizHawk.Client.EmuHawk.LuaConsole.<>c__DisplayClass57_0.<ResumeScripts>b__1() at BizHawk.Client.Common.LuaSandbox.Sandbox(Action callback, Action exceptionCallback)

Interestingly i didnt select NLua in options, i selected Lua+LuaInterface yet it throws NLua exceptions?

When i change to NLua it throws the following:
NLua.Exceptions.LuaScriptException: KopiLua.LuaNativeFunction

@Ashafix
Copy link
Contributor Author

Ashafix commented Feb 24, 2019

Hm, I managed to create a lot of error messages with this functionality but that one is new to me.
Which OS, Python and Emuhawk are you using?
I've only tried Windows 10 with Python 3.5 so far. Emuhawk was always compiled from source.

@ezBeanie
Copy link

ezBeanie commented Feb 24, 2019

I am Using Python 3.6, running Windows 10 and i used the latest developer build released about 2h ago. same errors when i use the latest stable build :( should i try installing Python 3.6 instead? i manage to get a socket connection when using the lua socket library but i want to use the socketServerScreenshot function

EDIT: i also tried the release where the feature was initially implemented, same results sadly...

@Ashafix
Copy link
Contributor Author

Ashafix commented Feb 24, 2019 via email

@ezBeanie
Copy link

When i try console.log(socketServerSend("test")) instead, it prints "Sent : -1 bytes" and the socket server is stuck at "waiting for connection". i did indeed load a rom, to be exact, "F1 World Grand Prix (U)" for the N64. i tried running the script while no rom is loaded and i get the same results

@coderesting
Copy link

I like the idea of communicating from Lua to other programs but is there currently a way to send data from f.e a python server to Lua?I tried the luasocket extention before to create a server on the Lua side but there seems to be a problem with the socket core dll. It would be nice to have a way to control the emulator with a python script.

@Ashafix
Copy link
Contributor Author

Ashafix commented Mar 17, 2019

@yannickbrandt : Yes, the Lua communications module supports it. You can send data from a Lua script in Emuhawk to a Python server and receive data coming from a Python (or any other) server.

e.g. in Lua the following command would take a screenshot, send it to the socket server and wait for a response:

resp = comm.SocketServerScreenShotResponse()

or just to send string

comm.SocketServerResponse()

The Lua scripts in this commit (e.g. https://github.com/raw/TASVideos/BizHawk/4d063f6061542921e66e8aeb23426d501fa0a7e1/Assets/Lua/UnitTests/TestCommunication_All.lua) and https://github.com/Ashafix/PyEmuHawk/blob/master/PyEmuhawk.py give some examples of how it could be done.

@coderesting
Copy link

@Ashafix Thanks for the fast response. I played around with your PyEmuhawk.py script and when I use comm.socketServerScreenShotResponse() it works perfektly but like @ezBeanie I have the problem that when I use comm.socketServerSend('test'), the client does not connect.

@Ashafix
Copy link
Contributor Author

Ashafix commented Mar 17, 2019

@yannickbrandt : The way the PyEmuhawk is currently set-up it looks for the last byte of an image or a windows line feed \r\n to know when the message is finished. Once it gets this message, it send ack back. You can modify the code to send any message back in response to your input. Please note, PyEmuhawk is the server and Emuhawk is the client, i.e. the server only sends stuff when it gets requests from the client.

Hope that helps a little bit!

Try the following steps:

  • start python PyEmuhawk.py
  • start Emuhawk with the output from the Python script, e.g.
    emuhawk --socket_ip=192.168.56.1 --socket_port=9990 --url_get=http://192.168.56.1:9876/get --url_post=http://192.168.56.1:9876/post
  • open a Lua console in Emuhawk
  • execute the following command:
    comm.socketServerSend("\r\n")
    The Lua console should show Sent : 2 bytes
    and the Python script SOCKET received
  • Next call print(comm.socketServerResponse()) and you should get ack in Lua

@coderesting
Copy link

@Ashafix Yes that works, I think the check for lineendings were my problem. Thanks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants