Okay, so I have found out how to get Lua and Scratch to communicate (finally :I)
Lua and Scratch like to communicate over a UDP network. I've tried it using TCP, but I kept getting errors with my communication.
(Mainly 'message too big; bad size field?' errors.)
If any of the more intelligent with Squeak (I'm dumbfounded by it, I'm more of a Lua person ) know how to fix this, please tell me.
First, you want to host a mesh session on the Scratch window. I'm expecting you to know how to do this, and so won't provide tutorials.
Second, open up any of your favoured text editors, and paste this in.
Before we do that, you need to have Luaforwindows installed, as that's how I have most of this code.
Here's the code you want to paste in:
local socket = require("socket") host = host or "localhost" port = port or 42001 if arg then host = arg[1] or host port = arg[2] or port end host = socket.dns.toip(host) udp = assert(socket.udp()) assert(udp:setpeername(host, port)) print("Using remote host '" ..host.. "' and port " .. port .. "...") while 1 do line = io.read() if not line or line == "" then os.exit() end assert(udp:send('broadcast "'..line..'"')) end
Save the file as a .lua for later.
In Scratch, make a chunk of code similar to this:
when I receive [Hello v] say [Hello!]Okay, done? Good.
Offline
I meant, once I have received it in Lua, what does it LOOK like?
I mean something like
broadcast "message"
That's how Lua sends a broadcast to Scratch. I want to know how Scratch sends the same broadcast to Lua.
Offline
I was under the impression it was a TCP connection, not UDP -- though the article doesn't actually mention that anywhere...
Offline
Yup, think it's TCP
Offline
Actually, going through the code, I get this as a comment:
"Process incoming UDP commands."
That's in Scratch-Networking>ScratchServer>private-server>processUDPCommands
And anything I see about networking has 'UDP' in it.
It's weird how Scratch uses an UDP connection, while other things (Going to use Magnie's Little Server That Can as an example) use TCP connections.
Quite odd, isn't it?
EDIT: Here's the code for 'startHosting', which makes me certain that Scratch uses UDP and not TCP.
startHosting "Open a socket on my port and start accepting connections." Socket initializeNetwork. self shutdownServer. serverSocket _ Socket new. serverSocket listenOn: self class portNumber backlogSize: 20. incomingUDPSocket _ Socket newUDP setPort: self class portNumber.
Interesting, isn't it?
Last edited by DigiTechs (2013-04-09 16:47:18)
Offline
Well, the Python example is using TCP.
Offline
I have a question for those who have worked with Scratch Networking:
How do you receive a message from Scratch? I tried using:
udp:receive()
in my Lua code to receive my message, but I seem to not get any message out of it.
EDIT: If I use TCP, I get the error mentioned in the top. Also, I seem to get an error when joining a Lua UDP server saying 'unable to connect to [ip]', with no error message. But Lua says I get a timeout error.
Last edited by DigiTechs (2013-04-09 17:22:11)
Offline
DigiTechs wrote:
If I use TCP, I get the error mentioned in the top.
Sounds like you're sending a malformed message It's definitely TCP.
Offline
DigiTechs wrote:
using TCP gives me this malformed message thing.
Check your message!
Offline
Okay, an update on the TCP client/server bundle:
I've got Scratch to be able to CONNECT to the TCP client, but I am unable to send messages to the other connected clients from one Scratch instance.
Here's my code. It's a little unorganised and everything's in funny places, so it'll be good if you have a vague understanding of Lua, or anything similar to it (Python is a little similar, but not the same definitely.)
--Local variables local socket = require("socket") local newline = "\n" --Create server local server = assert(socket.bind("127.0.0.1", 42001, 100)) server:settimeout(1) server:setoption('tcp-nodelay', true) local client = assert(socket.tcp()) local currsel io.write("Created servers."..newline) --Newset code function newset() local reverse = {} local set = {} return setmetatable(set, {__index = { insert = function(set, value) if not reverse[value] then table.insert(set, value) reverse[value] = table.getn(set) end end, remove = function(set, value) local index = reverse[value] if index then reverse[value] = nil local top = table.remove(set) if top ~= value then reverse[top] = index set[index] = top end end end }}) end set = newset() --Insert servers in set io.write("Inserting servers in set."..newline) set:insert(server) --Connect code local conn = coroutine.create(function() while true do local readable, _, errk = socket.select(set, nil) for _, input in ipairs(readable) do currsel = input if input == server then io.write("Waiting for clients..."..newline) local new = input:accept() if new then new:settimeout(1) io.write("Inserting client in set."..newline) set:insert(new) end else local line, err = client:receive() if err then io.write(tostring(err)..newline) input:close() io.write("Removing client from set."..newline) else io.write("Echoing line '"..line.."'."..newline) client:send(line) end end end end end) --Create echo client server:accept() client:connect("127.0.0.1", 42001) client:setoption('tcp-nodelay', true) --Start the connection code coroutine.resume(conn)
Offline
OKAY, I think I have sending.. buggy but working to a small extent. Here's an error message (YAY, THE SAME.) of what I get when I send messages to scratch. Maybe the people who are better than me at Squeak can tell me what I'm doing wrong.
message too big; bad size field? MessageSocket(Object)>>error: [] in PasteUpMorph>>runStepMethods [] in BlockContext>>ifError: MessageSocket(Object)>>error: MessageSocket>>nextMessage ScratchServer>>processCommandFrom: [] in ScratchServer>>processIncomingCommands OrderedCollection>>do: ScratchServer>>processIncomingCommands ScratchServer>>stepServer
That's my error message. What I'm thinking is 'OH COME ON, DO I NEED TO ENCODE YOU IN MD5 OR SOMETHING?!'
Offline
The message field error (if you are getting it in Scratch) is relating to the fact you probably aren't adding the length of the message to the beginning. http://wiki.scratch.mit.edu/wiki/Remote_Sensor_Protocol
Scratch is thinking the length of the message is 'broa' (or 1634693730 characters) long. So what you want to do is change the message so it's <4 byte size><message>. So 'broadcast "message"' is 19 characters long, so you want to send this:
\0\0\0\19broadcast "message"
I think, at least it's something along those lines. But basically the length of the message is stored in 4 bytes, and the message size is in base-256 or 255.
That is also the same format Scratch sends the messages in. Also, if anything, it's probably better to stick with TCP rather than UDP until you know what you are doing (I don't really use UDP just because it has a risk of sending the packets in the wrong order). Comment on my profile on the Beta website for any additional help (since I don't visit here very often anymore).
Offline
UDP seems to be communicating with scratch easier.
But thanks for the info.
EDIT: Okay, so I did that but I'm still getting the error.
Here's what I send:
'\\0\\0\\0\\'..string.len('broadcast "TESTMSG"')..'broadcast "TESTMSG"'
Since \ is Lua's Escape Character, I have to double it up for it to work. But this time I'll try and just use one single one.
EDIT 2: When I use the single one I get an error... :I
EDIT 3: Okay, now this is the format of my begin code: \0\0\0\\ which DOESN'T give me the message to big error. I think another way I'll do it will be better.
EDIT 4: In fact, what I tried didn't work. I'm kinda still confused.
Last edited by DigiTechs (2013-04-12 12:23:21)
Offline
\ is usually the escape character, which is why I said '\0\0\0\0' instead of '\\0\\0\\0\\0' since it is necessary to escape the character.
\0\0\0\0 means the message is '0' characters long and basically tells Scratch that it's closing the session. Send this message exactly as it is:
\0\0\0\19broadcast "message"
And with the same code (without the double-escape) send:
'\0\0\0\'....string.len('broadcast "TESTMSG"')..'broadcast "TESTMSG"'
I don't know exactly how the length is generated, I just use this (in Python):
def add_length(cmd): # Defines the length of each message. n = len(cmd) a = array('c') a.append(chr((n >> 24) & 0xFF)) a.append(chr((n >> 16) & 0xFF)) a.append(chr((n >> 8) & 0xFF)) a.append(chr(n & 0xFF)) return (a.tostring() + cmd)
If you understand bit shifting and stuff, you should be able to recreate the function in Lua. chr() is a function that changes a character from an integer (0-255) to the character equivalent. If Lua has a built-in function like that, use this instead (Lua code):
'\0\0\0'..chr(string.len('broadcast "TESTMSG"'))..'broadcast "TESTMSG"'
since it is more likely to work correctly (since I don't know exactly how to use escape characters to get the equivalent character.
Does that make sense?
Offline
Okay, I've got it working. I've managed to send a message to scratch from Lua using my TCP server!
Here's what I had to do to fix it:
num = 19 result = loadstring("local s = '\\"..num.."' return s")()
Basically, the \ character is Lua's Escape character, and I needed a way to turn "\19" into what the Lua Command Line said was !! (one character, just two in one). I asked some friends and this is what they came up with. And it works!
EDIT: Magnie, this might be what you're looking for to turn the backslash into it's escape character.
Last edited by DigiTechs (2013-04-12 15:03:01)
Offline
It might be. I've never even touched Lua, so I wouldn't know.
I actually think it's supposed to be '\x13' (not sure how you get 19 into that, but that's what I have from Python). I hope it fixed your problem. The next step is decoding the string that you receive.
Offline