Post new topic    
Page «  1, 2
Metal King Slime
Send private message
 
 PostMon Apr 20, 2015 5:53 pm
Send private message Reply with quote
Ah, James posted while I was replying.

There's no reason for that script/protocol to check that the server acknowledged an integer before sending the next piece of data; that's truly madness.

So if you were going to implement a protocol by reading and writing to a socket stream, the script would look like this:
Code:
script, check socket ok, socket, begin
  if(socket is connected(socket) == false) then(
    # Suppose we implement sockets to close if something goes wrong (e.g. timeout)
    get socket error string(socket, 2)
    show text box(6)  # Network error: ${S2}
    wait for text box
    exit returning(false)
  )
  exit returning(true)
end

script, check for server acknowledgement, socket, valid reply=1, begin
  if(socket receive(socket) <> valid reply) then(
    show text box(7)  # server refused request
    wait for text box
    exit returning(false)
  )
  exit returning(true)
end

script, send high score to server, score, name string id, begin
  # Return value is the slot that the high score was saved in, 1 for first place, 2 for second place, etc...
  # or -1 to indicate that saving the score failed
  return(-1)
  variable(socket)
  socket := connect to server($0="server:foo")   # Adding a lump to define server addresses seems unnecessary work
  if(check socket ok(socket) == false) then(exit script)

  socket send(socket, 37) # command 37 tells the server that the next data sent will be a high score
  socket send(socket, score)
  socket send string(socket, name string id)

  # Waiting on a socket causes the data we've queued up to be flushed (sent).
  wait for socket(socket)
  if(check socket ok(socket) == false) then(exit script)
  if(check for server acknowledgement(socket)) then(
    return(socket receive(socket))  # slot
  )
  disconnect socket(socket)
end

There's only one wait plus the server connection, so splitting the script into two in order to continue gameplay scripts while waiting isn't so bad.

However I think that messages are more natural to deal with than streams. And apologies, I've been assuming all along that send and receive would send "packets" rather than being stream-orientated, because that's what I'm used to. But when I say "message" I don't mean a packet, but just some data bracketed by start- and end-of-message. (Bundling a stream of data into packets is actually done automatically by TCP -- in fact you can't see anything about the TCP packets.) But actually it makes the scripting interface more complex to delineate messages (just look at the mess I brainstormed on the wiki). But marking the end of a message can be done implicitly by "wait for socket".

There are advantages to having data bundled into messages, including that if the message isn't understood then the whole package can be ignored instead of getting into a confused state. (Therefore, there's no need to do error checking after every int.) And it lets you check just once that a message has arrived, rather than having to worry that every call to "socket receive" will block for data or with an error (however it could still throw an error if the next data item was a string rather than an int).

BTW James, you can write just "do (.... break ...)" instead of "while(1)do( ... break ... break)".
Liquid Metal King Slime
Send private message
 
 PostMon Apr 20, 2015 6:06 pm
Send private message Reply with quote
After reading the wiki article, and what you wrote, the message interface sounds much better than anything stream based

I really like the look of the example commands right at the bottom of the wiki article

Code:

start server message(server)
add int to message(message, data)
add string to message(message, data)
add slice to message(message, data)   # etc
finish message(message)


TMC wrote:
BTW James, you can write just "do (.... break ...)" instead of "while(1)do( ... break ... break)".


Nice! Didn't realize we could do that :)
Slime Knight
Send private message
 
 PostWed Apr 22, 2015 2:13 am
Send private message Reply with quote
Multitasking scripts I personally see as being very difficult to manage, as then you would need to worry about 2 scripts trying to say alter the same objects/variables at the same time... This is one big issue when you get into Multithreaded programming, and unless the plotscript engine can properly manage and lock variables/objects so that 2 scripts don't try to change it at the same time, there are going to be a large number of plotscript bug reports coming in... I thought I'd add those 2 cents.

For socket support, when I initially thought about adding it, I was thinking of how JavaScript also a single tasking/single threaded language does it. HTML5 recently introduced WebSockets, which I have been using a fair amount, and they work perfectly without any support for multitasking/multithreading. In fact, most network servers I make in Python don't use threads, they use events and async IO, which the Linux kernel has native support for via the select system call. Windows and BSDs also have similar system calls. Multitasking/threading is most definitely not a requirement for socket support of any kind. Here is a Python WebSockets project I made as an interesting example:
https://bitbucket.org/kveroneau/hackrun

WebSockets does it nice and elegantly, and it uses events/callbacks to dispatch when data comes in, and when errors might occur. So, I completely agree with using a callback system to handle incoming data and other events.

Also, unlike say HTTP protocol, sockets don't really need to block until a reply comes back. In fact, no socket application would ever do this in a production application. It would block the entire GUI causing the application to hang... Rather, the application sends out data, and then the data coming back is processed once it's ready. In most of my own socket programming, I use OpCodes to delegate where incoming data should go, as seen in the application I linked to above. OpCodes are normally a single byte, much like a typical bytecode.
Metal King Slime
Send private message
 
 PostWed Apr 22, 2015 6:36 am
Send private message Reply with quote
By multitasking we don't mean multithreading; the interpreter remains single threaded. We mean cooperative multitasking, i.e. fibres or co-routines (see also "green threads"). This confusion is one reason I wanted to avoid the term "multitasking", but it's a perfectly accurate term and nothing better has been suggested. I decided to call a stack of executing scripts a fibre (references to threads are definitely banned). Each tick, every active fibre will be executed in order until it quits or hits a wait command. Currently, only one fibre can be active. The truth is, implementation-wise there's little difference to the current situation, which is basically just due to DOS-era memory allocation (the interpreter was limited a single stack).

I find it interesting that websockets seems to expose only messages (by letting you register an onmessage callback), rather than by reading from the socket as a stream. It certainly makes it look simple.

If sockets were treated as streams then it's a bit awkward to read from them. You would have to call a function to check whether there's data ready before every single read call, as attempting to read when there's no data available would have to either block (which isn't so bad once we have fibres) or cause an error. On the other hand, if you try to read off the end of a message, it's obviously an error.
Display posts from previous:
Page «  1, 2