Hard Light Productions Forums
Modding, Mission Design, and Coding => The Scripting Workshop => Topic started by: Nuke on March 29, 2011, 11:57:34 pm
-
so the other day i stumbled onto a lua module for serial communication. ive used them before, my lua tga parser used a different module to work with binary files (this ran in the lua command line shell, not in freespace). apparently you stick a dll (or other platform specific application extension) into the working directory, then in lua you type something like
libname = require(dllname)
and by some magic libname becomes a table of all the functions, variables, etc that the module has to offer. this works on almost every other lua interpreter i use. except in freespace.
someone on #scp said that this feature of lua has been disabled. i was wondering what it would take to enable it.
-
:bump:
because of the number of replies to my initial post (read: 0), i think i must point out the possible benefits to being able to load modules (http://www.lua.org/manual/5.1/manual.html#5.3). the library i want to load would support serial communication. this would allow communication with external hardware (such as arduino, or any other mcu dev board), allowing anyone to build an interactive cockpit or (with the right motor drivers and encoders) full motion simulation. could also lead to a slew of alternative input, such as imu-based head tracking. i should point out that this is not the only library available.
i mentioned the pack library, which provides numerous functions for working with binary file data. i used this library to parse tga files, for example. one use for this library is to read certain parts of a pof file, for example (such as an in-engine pof editor). probibly not the sanest of ideas but its merely an example.
there is a whole list of libraries here (http://lua-users.org/wiki/LibrariesAndBindings), which can be used by throwing the binary into the fs2path and using require. there is certainly the possibility to get into the trouble by trying to use certain modules. there are also modules that only work on specific platforms, but it seems there are many useful modules that support all windows and *nix platforms. this would certainly be an advanced feature that is not recommended for the amateur scripter. but the package library (as its called in the manual) is part of lua and should be supported by the engine.
you could even write your own library and turn it into a precompiled module. this allows scripters to add code-level functionality without modifying the engine.
generally i think this would be a very useful thing to have.
-
Sure, but this needs someone familiar with the lua implementation. Also, it would be good to know what kind of error FSO throws when trying to load a library.
-
essentially the engine chokes on the first line in the script
rs232 = require("luars232")
and gives me this error
LUA ERROR: [string "serial-sct.tbm - On Frame"]:1: unexpected symbol near '='
------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: (null)
Name of: (null)
Function type: (null)
Defined on: 0
Upvalues: 0
Source: (null)
Short source:
Current line: 0
- Function line: 0
------------------------------------------------------------------
------------------------------------------------------------------
LUA Stack:
------------------------------------------------------------------
------------------------------------------------------------------
i tested the library in lua binaries (the ones from here (http://luabinaries.sourceforge.net/)) and this line loaded the library just fine, and i was able to talk to my arduino by sending bytes (the value determined the brightness of an led) to the mcu.
if i continue skip the error and continue i get this
---------------------------
Error!
---------------------------
LUA ERROR: [string "serial-sct.tbm - On Game Init"]:1: module 'luars232' not found:
no field package.preload['luars232']
no file '.\luars232.lua'
no file 'D:\FreeSpace2\lua\luars232.lua'
no file 'D:\FreeSpace2\lua\luars232\init.lua'
no file 'D:\FreeSpace2\luars232.lua'
no file 'D:\FreeSpace2\luars232\init.lua'
no file '.\luars232.dll'
no file 'D:\FreeSpace2\luars232.dll'
no file 'D:\FreeSpace2\loadall.dll'
------------------------------------------------------------------
ADE Debug:
------------------------------------------------------------------
Name: require
Name of: global
Function type: C
Defined on: -1
Upvalues: 0
Source: =[C]
Short source: [C]
Current line: -1
- Function line: 1
------------------------------------------------------------------
------------------------------------------------------------------
LUA Stack:
require
------------------------------------------------------------------
------------------------------------------------------------------
[ This info is in the clipboard so you can paste it somewhere now ]
Use Yes to break into Debugger, No to continue.
and Cancel to Quit
---------------------------
Yes No Cancel
---------------------------
it could be that perhaps the engine expects the library to be in a certain place. but what i get from everyone i talk to on the scp board is that this was intentionally disabled when the scripting system was implemented (probably because it could be exploited with the correct library to gain access to the system). it could just be that my usage is all wrong too.
*edit*
on second thought it may have just been a bug on my script. apparently you need some dll files from the reference interpreter. i managed to get the library to load and printed out the variables and function names. havent managed to make it work yet. might try some other module though, serial is hard to get working when it works.
-
Try:
require "luars232"
-
In the documentation, they seem to even omit the space, ie require"luars232".
-
ive seen both variations in documentation. in fact both methods appear to work (see my edit on my previous post). i just haven't managed to get the library to work. i managed to load the rs232 library and print out the contents of the library with this:
rs232 = require"luars232"
--print out the vars and functions of the library so i can see what all it can do
ba.print("\n\nLua rs232 module:\n\n") --make it easy to find in debug spew
for k,v in pairs(rs232) do
if type(v) == "function" then
ba.print("Function: "..k.."()\n")
else
ba.print("Variable("..type(v).."): "..k.." Value: "..v.."\n")
end
end
which will cause this to be printed out in the debug spew:
Lua rs232 module:
Variable(number): RS232_ERR_FLUSH Value: 4
Variable(number): RS232_BAUD_19200 Value: 3
Variable(number): RS232_ERR_IOCTL Value: 10
Variable(number): RS232_ERR_PORT_CLOSED Value: 11
Variable(string): _VERSION Value: 1.0.0
Variable(number): RS232_ERR_CONFIG Value: 5
Variable(number): RS232_PARITY_NONE Value: 0
Variable(number): RS232_ERR_OPEN Value: 2
Variable(number): RS232_BAUD_38400 Value: 4
Function: error_tostring()
Variable(number): RS232_ERR_UNKNOWN Value: 1
Variable(number): RS232_FLOW_OFF Value: 0
Variable(number): RS232_FLOW_XON_XOFF Value: 2
Variable(number): RS232_PARITY_ODD Value: 1
Variable(number): RS232_STOP_1 Value: 0
Variable(string): _TIMESTAMP Value: Jan 6 2010 10:25:59
Variable(number): RS232_ERR_NOERROR Value: 0
Variable(number): RS232_FLOW_HW Value: 1
Variable(number): RS232_ERR_WRITE Value: 7
Variable(number): RS232_DATA_8 Value: 3
Variable(number): RS232_STOP_2 Value: 1
Variable(number): RS232_BAUD_9600 Value: 2
Variable(number): RS232_BAUD_4800 Value: 1
Variable(number): RS232_ERR_READ Value: 6
Variable(number): RS232_ERR_TIMEOUT Value: 9
Variable(string): _BUILD Value: $Id: luars232.c 13 2010-01-06 09:15:31Z sp $
Variable(string): _COPYRIGHT Value: Copyright (c) 2009 Petr Stetiar <[email protected]>, Gaben Ltd.
Function: open()
Variable(number): RS232_PARITY_EVEN Value: 2
Variable(number): RS232_BAUD_115200 Value: 6
Variable(number): RS232_DATA_5 Value: 0
Variable(number): RS232_ERR_CLOSE Value: 3
Variable(number): RS232_BAUD_2400 Value: 0
Variable(number): RS232_DATA_6 Value: 1
Variable(number): RS232_ERR_SELECT Value: 8
Variable(number): RS232_DATA_7 Value: 2
Variable(number): RS232_BAUD_57600 Value: 5
so its loading the function names and variable from somewhere. so i assume its loading the library. i cant say however that its working. im gonna try the pack library, since i can just use my tga parser script to test it. it doesnt use any low level calls, it just formats chars to specific data types and stores them in the appropriate lua type and vise versa. maybe get it to spew out an asciicat in the debug spew (or maybe i could just use gr.drawPixel()) :D
-
Perhaps assigning the loaded library to a variable is the wrong thing to do. If it is okay to do, then it probably works similarly to Python's "import ... as ...", where you have to reference it by the name you assign it to:
import struct as s
s.unpack('2fi', f.read(12))
-
i just did what was in the library example. in lua all objects or classes are essentially tables with metatables and member functions (lua is a table oriented language). this is how modules expose their functionality to the lua interpreter, by simply providing a table containing all the functions, variables, objects, and library specific types (userdata) etc that the library has to offer. at this point im pretty sure the library has been loaded. those variables and functions had to come from somewhere, and they dont seem to crash anything when their used. but i can think of many other reasons why serial would not be working. at this point i think its a good idea to test other modules and see if any of them can be made to work.
-
Well, if some of the functions work when you load the module like that, then the module is most likely loaded, and I can't think of anything else. Good luck!
-
Just thought to remind that there actually was a good reason - security - why lua file access (amongst other things) were limited in its FreeSpace Open implementation.
-
im not saying that you couldn't do something really stupid by loading a module. there are some modules that can get into your system and cause all kinds of mayhem. these are mostly to be used for system management scripts for administrative purposes, but they could easily be used for exploits. needless to say, if some shady individual tells you to put a dll file into your fs2 dir, and gives you a script to run, you better think twice. on the other hand there are modules that may serve useful purposes.
-
But imagine a user comes into the community out of the woodwork with a new mod to try out. It has enough data to be considered a full mod, but maybe there's a script and a module tacked in that no one thinks to look at because it's buried inside a VP file, or several folders deep. Lots of people would probably try it out, and if there was anything malicious going on, you wouldn't catch it for a while I'd bet. So locking down the abilities of the Lua interpreter a bit is probably necessary.
-
How about this then: Let us only load modules that are in FS2's root dir. I know there's a risk here, but at some point, we have to trust modders to not be assholes. (If we're really paranoid, we could pop up a warning like "Script x is trying to access module y, do you wish to continue? Yes/No/Don't ask again for this module")
-
Or having some kind of approved module list that the interpreter would accept to execute?
THe problem with that would be the updating thing...
-
Actually, you don't ever have to trust modders. You shouldn't trust anything you didn't create. And anything you do create should still be scrutinized ;)
I'm not saying module loading shouldn't be done, but that there are inherent risks we shouldn't just try to bypass.
-
How about this then: Let us only load modules that are in FS2's root dir. I know there's a risk here, but at some point, we have to trust modders to not be assholes. (If we're really paranoid, we could pop up a warning like "Script x is trying to access module y, do you wish to continue? Yes/No/Don't ask again for this module")
thats the way it works anyway. though i think there are ways to change the module path (see here (http://www.lua.org/manual/5.1/manual.html#pdf-module)). you could also probably get away with sticking the module in the system dir. this is obviously not recommended, but it could be a source of an exploit. also must point out that in addition to the module dll you also need lua5.1.dll in your fs dir. so if you freespace dir starts looking like your system32 dir, id start to worry.
-
If this was intentionally disabled it was due to security concerns. I did make an effort to limit the Lua library to avoid having unrestrained access to the rest of the computer.
I would suggest a graphical prompt that says something like, "The mod you are trying to load is attempting to load a Lua library 'library_name_here' with checksum '24301401940' that isn't on the approved libraries list. Lua Libraries can contain malicious code and are able to access anything on or connected to your computer, as well as the internet. If you know this library is from a source that you trust, click 'OK'. If not, click 'Cancel'."
If the user clicks 'OK', it stores the DLL/checksum using the FS2Open registry API and doesn't prompt again the next time they try to start FS2Open.
You'd probably also need some kind of command-line utility to disable the prompt for testing a library YOU are compiling.
My preference would be to see these 'dangerous' abilities (like accessing the serial ports.....) that a mod wants to use stated up-front to the user in a list like Android uses. And have them be a part of the scripting API. But I doubt that's a fast/easy enough solution here.
Not volunteering to code either... :p
But yes, if there was a definite decision it was to keep the scripting system from being able to play outside its sandbox, it was for security rather than technical reasons.
-
You could probably also use the Lua interface to write to a symlink in the FS2Open directory that's pointed at a file on a memory-mapped mountpoint/drive, while an external program reads whatever the Lua script writes, parses it, and sends it to the serial port.
This could actually be faster than using a direct Lua->serial interface alone, depending on what you're doing.
-
i think the reason the library didnt work was because of threading or timing issues. serial just wont work if the timing is bad. usually you get around the issue by running the send and receive code in another thread and only work with the buffers. but it gave every indication that the module had been loaded.
i considered using named pipes pipes, someone also suggested a tcp/ip interface. the latter would work in loopback mode to talk to software on the same machine, or could talk to devices on the network. this would also facilitate talking to external hardware (arduino users could just get an ethernet shield) and would provide some serious throughput. only real reason i was looking at serial is is its something i could do with hardware i have on hand, it would provide adequate throughput for many of the potential applications.
-
I like the TCP/IP interface idea.
Serial port is very touch and go. There are a lot of variables involved in serial port communication that I don't totally understand. When I worked with it I was fortunate to know all the bitrate/parity/stop bit stuff.
-
its not that complicated, most of the variables deal with the structure of the packet (like data bits, parity bits, and stop bits), baud rate and some other less important settings. generally speaking the settings at both ends need to be identical. real issue is you need to have real time control over the uart and some things just cant wait for the frame to end. arduino uses a usb to serial bridge right on the dev board (newer version replace this with an mcu which can be re-programmed for native usb) frankly i'm surprised that serial is still used at all when you have interfaces like i2c, spi, 1-wire, etc.
-
i am now looking at the luasocket module. this provides facilities for socket programming in lua. i am considering using a udp socket to communicate with external software and other devices (such as arduino + ethernet shield). since i dont have an ethernet shield, i will use loopback and try to talk to another lua interpreter on the local computer for the time being. if i manage to make it work, this would allow both ipc and a hardware interface with pretty good speed as well.
this kinda thing would be capable of streaming graphics in real time. and pretty much every other application i can think of. it also supports http so you could probably browse the net on one of the rtt displays in your fighter's cockpit, provided someone scripts a web browser. this library is well documented and seems to be fairly easy to use. like i downloaded a web page with 2 lines of script, and you guys were worried about serial ports :D
-
well its official freespace module support is indeed enabled. managed to get the a weapon energy printout in another lua interpreter using loopback, here is the "server" script that runs in the game's interpreter:
#Conditional Hooks
$Application: FS2_Open
$State: GS_STATE_GAME_PLAY
$On State Start:
[
--server
socket = require("socket")
ip = "127.0.0.1"
port = 1666
destport = 2666
udp = socket.udp()
udp:setsockname(ip, port)
udp:sendto("connection established", ip, destport)
timestamp = 0
]
$On Frame:
[
shp = mn.Ships["Alpha 1"]
--write out the ship's weapon energy in the range of 0-100
if shp:isValid() and mn.getMissionTime() > timestamp then
timestamp = mn.getMissionTime() + 5 --update every 5 seconds
local wepeng = math.floor((shp.WeaponEnergyLeft / shp.WeaponEnergyMax)*100)
--make it stringy
local message = "Weapon energy at "..wepeng.."%."
--send it to the client
udp:sendto(message, ip, destport)
end
]
$On State End:
[
udp:sendto("end connection", ip, destport)
]
#End
in some other lua interpreter (http://luabinaries.sourceforge.net/) out side of the game, run this script. this mostly just runs an infinite loop, waiting for data, and then printing it out.
--client
socket = require("socket")
ip = "127.0.0.1"
port = 2666
udp = socket.udp()
udp:setsockname(ip, port)
while true do
data = udp:receive()
if type(data) ~= "nil" then
print(data)
if data == "end connection" then
break
end
end
end
note for this to work the luasockets (http://w3.impa.br/~diego/software/luasocket/home.html#download) library must be installed in both the freespace dir, and the external interpreters dir. in addition you need to copy the lua5.1.dll (comes with lua binaries) to your freespace root dir.
i havent tested it over the network yet, but i dont see why it wouldnt work if loopback works. but this allows for ipc and ,at some point, communication with hardware (need to find money for an ethernet shield now). i should also point out that this was a quick and dirty hack, and some error handeling code definately needs to be thrown in.
-
i just tested this over the network and it worked fine. and i should also point out that the client at the other end need not be written in lua. it should work with any other socket library for any other programming language you can think of. only reason i used the lua client was because i didnt want to learn two libraries in the same day. you could probably even talk to some service on the web, like twitter, and tell the world whenever you blow up a sathanas beam cannon. you could probibly go as far as creating an achievements system for the game and mods, if you were so inclined.
hardware experimentation is gonna have to wait though, until i can get the cash for that ethernet shield. of course i could easily write a proxy program to relay data from loopback to the serial port and vise versa. i could slap something together with lua in a few minutes, however i think id rather try doing this in c/++, for speed, and for error checking (serial has no inherent checking so i like to use 7 or 8 bit crcs), something not so easily written in lua. i think i also want to try tcp and see what kind of performance that can do. tcp would be better suited to everything except streaming (udp would handle that better).
as far as streaming goes the game doesnt really give me anything as far as reading textures. i can create them, draw them and use them, but you cant read the raw texel data. id like to be able to stream out render targets and other textures in real time. this would require some c code in the engine. cramming a 24 bit 512*512 texture through the tubes 30 times a second would require 189 megabits of throughput. pushing that through gigabit ethernet would be easy, but likely 100mbit would not be able to handle it. you can also downsample the texture pretty fast in real time, convert a 24 bit image into 16 or 8 bit could be done with some inexpensive shift operations. 8 bit would still require 63 megabit. block compression like you find in dxt1 would half that to 32megabit, probably fast enough to move over wifi, and serial if you reduce resolution and framerate.
for speed you would probably just use a 2d engine at the other end for panel elements, only streaming over the sprites, and only once at init. frame to frame you would just give commands to the 2d renderer on the external device. so you want to do a sensor panel like the one on the hud. you send the background sprite, raster font for text readouts, indicator bars and other graphic elements. every frame you tell it what to draw where and how. the little 3d ship icon could be drawn as a tiny render target, which would be streamed un-compressed, but its not like you are transfering the whole gauge, unless of course you want to implement a 3d renderer at the other end. likely whatever device will not have a lot of cpu power. probibly an mcu with some kind of graphics processor. if you wanted to do a little rear view camera display on a small lcd with fast refresh and high resolution, your hardware starts to get expensive.
-
Well, my question would be why you're transmitting bitmaps, when you could just as well leave all the rendering **** to the client at the other end.
-
Well, my question would be why you're transmitting bitmaps, when you could just as well leave all the rendering **** to the client at the other end.
you can only go so far with primitives. might be able to draw a wireframe or a gradient, but its likely it would not look as good as a bitmap. you could skip that whole process and just copy the bitmaps over to the other device. this has some advantages, it could be pre-econverted to the right pixel format. perhaps the other device might be a re-purposed pda, might be a single board computer (usually used for embedded applications), in which case it would likely have the storage to cache all the various bitmaps it would use to draw graphics. on the other hand its just as likely being an mcu or two on a protoboard. you might have a few k of ram, some flash (which is refered to as program memory on an avr), and maybe an eeprom, though its not hard to drive an sd card from an mcu, it might add complexity and cost to the system. it would also likely be built to be multipurpose, maybe other games would want to use it, and it might create a situation where you have to re-flash the firmware to change graphics sets.
now that i think about it i dont think id need to do anything in c to send your typical texture. i could just read it with c file as text and pipe it off to the device. it would of course need to be capable of parsing it. that would not quite work for render targets. going back to the rearview camera idea, you would really need to limit your resolution. i find 512^2 to be really kind of large for a render target. i would use something like 320*240*12bpp*30fps = 28mbit. 12bbp is a common bit depth on small low res lcd screens. it doesnt need the same quality as your monitor. its also not hard to convert 24->12, just drop the 4lsbs of all 3 channels, and scrunch 2 pixels into 3 bytes. still this is probibly not the best way to do this. support multiple dispalys where each display can have different camera setup, and then drive the little mfd monitors from the video card.
of course the point here isnt to draw coders off of there current tasks, the point is to get a bunch of hardware hackers interested in freespace. get the game to support a hodgepodge of open source sim hardware. much of what needed in terms of development is hardware and software external to freespace, and some lua. working camera displays are something that can be put off till after we got freespace working in a sim and someone says "now what".
-
I'm just a bit worried about the throughput the engine can give you before it impacts performance.
-
i wonder if 15/20 fps would not be' enough for a rear camera no?
-
20 would look ok. i figure most gauges would only need to update about 10 times a second, 15 would look better. 20 works out good because its about 1/3 of your usual framerate, therefore you only need to update the render target every 3 frames, giving you 2 frames in which to stream out the contents of the render target. the game computer runs fast enough to do the work, but the mcu or embedded system that youre using to drive an mfd display, would likely need time to commit the image to its framebuffer. the mcu on arduino is only 16 mhz, where most operations take 1 cycle (multiplies take 2 and floating point math takes more). the chip can clock up to 20 mhz, any faster and its overclocking. likely you will be working with an external sram (dual ported) for a frame buffer, big enough to store 2 frames for double buffering. you might have an mcu just to handle the output from the ram. it would just do its own thing. reading the sram and driving whatever display (could be vga, composite, or digital lcd). it would likely have some lines to talk to the other mcu, telling it what buffer its reading from at the time. these would trigger an interrupt on another mcu, and tell it what buffer it can write to. that mcu would read the stream through its network interface. and place the pixels in the buffer. the parts for this would not be expensive, at least to proto it in a breadboard, price shoots up when you start needing custom pcbs. so this hardware is what kind of limits what you can do, freespace will run circles around it.
-
I wonder : why not using simple "miniscreen" for the mcu? and let the computer do all the math job via a software running in background on the pc?
freespace is monothreaded so it give space for many things else at the same time.
-
because an mcu isnt a screen :D
an mcu is a complete computer on a single chip. its suited to runing a single, simple program in real time. its is at the heart of %99 of all electronics these days. and i bet your computer has many of them inside of it. every mouse, every joystick every keyboard has at least 1. they are also very cheap. point here is to get hardware hackers all over the game. but you do have a point. you can run a 2d engine in another program, and output it to a small screen. you can use the loopback demo i posted earlier to communicate to the engine. you can get little 3.5" sreens off of ebay for like $30. they are usually for car rearview cameras and have a composite interface. most video cards have at least one composite out connection. multi card setups would give you more. it still doesnt let you capture a render target. so that rear-view mfd wouldn't be doable. actually all you would need for that is the pointer to the render target. after that it should be fairly easy to extract the data, and freepace doesnt need to do anything.
-
oops dp
-
ooops a bit confused with mcu ; thought that was the 2nd name of mfdin dcs a10 for exemple.
anyway once again you interested me, i just changed my 3 screen, so i was thinking about saving one for doing this :
(http://lh4.ggpht.com/_2nZGbOZAW0k/TM0mQTYl5wI/AAAAAAAADJY/hztI3E09dLM/s640/MFD-carte-AV.JPG)
(http://lh3.ggpht.com/_2nZGbOZAW0k/TM0mOVDcSsI/AAAAAAAADJM/3iBQmRdS_z8/s512/Pit-Warthog-MFD.JPG)
under my triplehead setup.
-
this is exactly the kind of thing i want to stir up. im quite fond of these little chinese made rearview camera monitors. (http://cgi.ebay.com/TFT-LCD-Car-Screen-Mini-3-5-Inch-Color-Camera-Monitor-/270741631159?pt=LH_DefaultDomain_0&hash=item3f097570b7) i own one and its not a bad little screen to have, and are fairly cheap, though they are not quite the right shape for a cockpit mfd like that one. not to say that it coudn't be used.
-
i wrote a relay script using the rs232 and luasocket libraries. to sned data from udp loopback to serial. and managed to get my led on my arduino to change its brightness based on weapon energy. it worked at first but at some point it stopped working. i think im gonna blame the rs232 library. i think lua is just not fast enough to keep up with the timing, or perhaps windows ran it on the same core as freespace. so i think i definitely want to write a c relay application. this is just an interim solution till i can get ahold of an ethernet shield.
i think my next step with be to drive some hobby servos to make a scale model of a full motion simulator. i can build it out of legos, so i could probibly have something working by the end of the day, if i dont end up playing starcrack2 all day.
-
i just ordered my ethernet shield. its one of those cheap chinese knockoffs, so its going to take several weeks to get here. il probibly start a new interfacing with hardware thread when it gets here. save the trouble of having to use middleware.
-
got the ethernet shield, took me all day to realize i was using the wrong ethernet shield library. but i found the right one and did a ping to check connectivity. it all seems to work now. i might take some hardware servos and do a simple set of analog gauges on a panel as kind of a hello world. perhaps tomorrow, but i have one hell of a cold so dont bet on it. i think il start a new thread though, but id ask that posts regarding security concerns of module loading to stay here, and il make a point to direct people to this thread to discuss them. that said id like to point out that it is currently possible to interface the game to whatever hardware you could cobble together around an arduino, or other mcu dev platform.