[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  SMAUG
. -> [Folder]  Lua
. . -> [Subject]  Starting with Lua, coroutines and other stuffs (need patient people to help)

Starting with Lua, coroutines and other stuffs (need patient people to help)

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1 2  

Posted by Aiseant   (33 posts)  [Biography] bio
Date Thu 24 Feb 2011 10:36 AM (UTC)

Amended on Thu 24 Feb 2011 12:39 PM (UTC) by Aiseant

Message
Hello world

I'm starting with Lua and have some troubles with the way corountines work. I "just" want to make pauses in my mud (for instance, when you connect, it says "hello", and then "welcome" after a second without freezing the world for everyone else)

As a starting point, i'm using the wait I found wandering around in this forum, http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=8433. Here it is :

module (..., package.seeall)

local threads = {}

function update ()

  -- for each active thread, see if the time is up
  
  for k, v in pairs (threads) do
    if os.time () >= v then
      threads [k] = nil  -- delete from table now
      assert (coroutine.resume (k))
    end
  end
end

function wpause (seconds)
  threads [assert (coroutine.running (), "Must be in coroutine")] = os.time () + (seconds or 1)
  return coroutine.yield ()
end

function make (f)
  assert (type (f) == "function", "wait.make requires a function")
  coroutine.wrap (f) () -- make coroutine, resume it
end -- make


And I tried to use it, without any success, I have to admit.
For instance

function connections (arg)
           mud.send_to_char ("#flash1#")
           wait.make( function ()wait.wpause(1)end)
	   mud.send_to_char ("#flash2#")
           wait.make( function ()wait.wpause(1)end)
    return
end --connection

or

function connections (arg)
	wait.make( function ()
           mud.send_to_char ("#flash1#")
           wait.wpause(1)
	   mud.send_to_char ("#flash2#")
           wait.wpause(1)
	 end)
    return
end --connection
 

== no error of execution, except that ... it doesn't make any pause in the first case and just stop after #flash1# in the second case :D
I assume that I need to read a little bit about coroutines (I'm doing so) but 'speaking' with people is a powerful way to learn and understand, so I hope people here would help me :)

If I understand well what I've read, coroutine is a sort of multi-threading process (with no preemption) right ? So it basically allows to do what I want.

So, what I've to do with the code is make sure that the 'update' Lua function is regularly called by the C code, so my 'connection' Lua function will make steps, until it encounters a wait.wpause that will freeze it (with coroutine.yield () ) right ? (using my second coding case)

I have this : call_lua(ch, "wait_update", NULL);
in my update.c, during the char_update

And my function 'connections' is called when the player connect : call_lua (ch, "connections", "new_in");

The last argument "new_in" is because I intended to have a different message, depending if it is a new player or someone already known by the database.

So my real function is more like this :

function connections (arg)
 
  --blabla, other if with different argument, like "new_in" (but the text is very long
  
  if arg == "out" then
	wait.make( function ()
        mud.send_to_char ("Good bye runner")
	wait.wpause(1)
	mud.send_to_char ("You'll be back soon")
	end)
    return
  end  -- if listing

end -- function


I know at least three two things :

1/ my way of checking the arg is wrong, I know that. I'm modifying it, i found how to do what I wanted recently in a tuto

2/I don't get how the whole thing know which thread has to be release.I figure it our but still : Is 'assert (coroutine.running (), "Must be in coroutine")' returning some kind of unique ID ?

3/ you don't have to mention that I'm trying to do something well ahead of my actual level of Lua, I know that :) But the best way to learn is to do, and I have nothing else to do now than making this bloody pause

Regards,
Aiseant (french, so please forgive my strange way of writing english )
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #1 on Thu 24 Feb 2011 11:01 AM (UTC)

Amended on Thu 24 Feb 2011 11:03 AM (UTC) by Twisol

Message
([EDIT]: Ack, you edited while I was typing! Hopefully this post still helps you out.)

The thing is, coroutines don't run at the same time as other code. You decide when to switch from one coroutine to another, and nothing else happens until that coroutine yields back (or resumes yet another coroutine).

Lets look at the problem. You want to pause output for a user without pausing it for everyone else, right? Remember that only one coroutine runs at a time. If you were to give each user their own coroutine, you would easily be able to stop doing stuff for one user and pick right back up later.

Here's an example. The numbers in comments show what order things happen in.

function user_action(user)
  user:send("Hello!")                    -- 3
  coroutine.yield()                      -- 4
  user:send("Goodbye!")                  -- 7
end

-- ...

-- Set up a coroutine for a user.
local co = coroutine.create(user_action) -- 1
coroutine.resume(co, user)               -- 2
user:send("Stuff")                       -- 5
coroutine.resume(co)                     -- 6
user:disconnect()                        -- 8

(I just made up the 'user' stuff there to make the code make more sense - I don't have any experience with SMAUG.)

The output from this:
Hello!
Stuff
Goodbye!


In other words, coroutines let you stop in the middle of something and come back to it later. The "coming back" part is where you're having trouble. How does your program know when to come back? In MUSHclient, I would have a timer fire in 1 second to resume the coroutine. You need a similar way to know when the time has passed in SMAUG.

I don't know anything about SMAUG, so unfortunately I can't give you a real answer. Hopefully I helped explain what coroutines are and how they work, though.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #2 on Thu 24 Feb 2011 11:09 AM (UTC)

Amended on Thu 24 Feb 2011 11:10 AM (UTC) by Aiseant

Message
Thanks for your answer, it confirms what I though.
Basically, it is like a multi thread since you can handle/stop/resume etc, but you're only doing one at once.
If you switch fastly enough, you can give the impression of multi processing

I edit a lot, I'm sorry, but I always feel bad to let people thinking about my problems without updating with what I realized/learnt/forgot to put in the first place.
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #3 on Thu 24 Feb 2011 11:13 AM (UTC)

Amended on Thu 24 Feb 2011 11:15 AM (UTC) by Twisol

Message
Yes, that's right. It's often said that coroutines are good for event-based programming, because you can pause to wait for an event, and resume once that event occurs. (An "event" is just an external stimulus, and I'm really sorry if that term is hard to translate but I don't know how better to put it. :( In MUSHclient, timers, aliases, triggers, and plugin callbacks are all events.)

As for switching fast enough, yes... but the difference between system threads and Lua's coroutines is that in the former, the system decides when to switch, while in Lua, the thread decides when to switch.

No worries about editing, I do it too. ;)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #4 on Thu 24 Feb 2011 12:18 PM (UTC)

Amended on Thu 24 Feb 2011 04:26 PM (UTC) by Aiseant

Message
"event" is ok, as well as "trigger" (litteral translation in french gives the words we use in french for those, actually)

The way Lua manages variables is very disturbing, at least at the beginning : very flexible, but too much for my taste
I still don't know why the update is never done, will try to add some debug info

I found a site telling me that the right way to call a Lua function in C is to do

lua_getglobal(state,"function_name");
lua_call(state,0,0);


So the call : call_lua(ch, "wait_update", NULL);
is wrong ?

Anyway, if I try with the previous call, I don't know why but the script stops at the first pause. It seems that something is wrong with the update.
[Go to top] top

Posted by Nick Gammon   Australia  (23,017 posts)  [Biography] bio   Forum Administrator
Date Reply #5 on Thu 24 Feb 2011 08:36 PM (UTC)
Message
Well, where to start? It's great to learn by trying, that's much better than just wondering. If you try, and something doesn't work, well you now have learned something - what doesn't work.

Quote:

I found a site telling me that the right way to call a Lua function in C is to do:

lua_getglobal(state,"function_name");
lua_call(state,0,0);


The functions I gave hide this low-level stuff from you. If you look inside lua_scripting.c you will see that if you call "call_lua" it does this:


  • Looks up the function name you supplied in the global table (environment), and raises an error if not found, or not a function.

  • Pushes the argument string, if any

  • Calls CallLuaWithTraceBack which is designed to show a traceback (ie. what functions called what) if it fails

  • CallLuaWithTraceBack adds a traceback function to the stack

  • It then calls lua_pcall (Lua Protected call) to call your function

  • On failure it logs the error to the log file, and shows a message to the player ("A server scripting error occurred ...")


So really, it does the recommended stuff but with a whole lot of extra helper things, so your scripts don't just "silently fail" (or crash the server).

As for coroutines - if you are starting out with Lua they are probably one of the hardest things to get working, so for now I recommend you do stuff like greeting the player, saying goodbye etc. without the pauses.

I think I wrote an entire quest system without using coroutines. However for periodical stuff (eg. "You have a new quest") I relied up on the "looking" hook. That is, this gets called when you look around a room, which tends to happen when you enter one.

Plus there us an "update" hook which gets called every minute.

So in that you can see if something is outstanding (eg. "You have quests that are not finished").

You can make coroutines work - but you need to have a clear idea of how it is going to happen. Once you have yielded, as Twisol said, you need a mechanism for resuming. In the case of the "wait" module in MUSHclient, both timers and triggers check for outstanding coroutines, and then resume the applicable one. Browse through that more to see the general idea.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #6 on Thu 24 Feb 2011 09:55 PM (UTC)
Message
Hehe, Gammon, you're so right about it but you see, it is not that I particularly enjoy challenges (well ... in fact, I do, ok) but more that I cannot stand failure (especially when it is me failing at reaching a point I decided to reach), you know what I mean ? :D

Anyway, it seems that I really should look at this lua_scripting of yours ... full of surprises. Many thanks for making the work easier (I love those things), very comfortable. And as it is handling things right, I can understand a bit more about the way things work.

I learnt C in classes, and our last step was to make an OS real time with it, so I wouldn't dare claiming that I know everything about it, but I would say that I have some notion about threads, pausing it and restart it later. Lua is really enjoyable for me at the moment, because it forces me to think about those past lessons (which was great, btw) and also remember me when I first learnt Python (and how I was horrified about declarations and variables) :)

Hey ... you know what ... in fact, it is perfectly working \o/
Just that, as you said, I had to check wether or not the resuming was made. I said that I thought update wasn't called right ? Which was insane, since it was called during the update of the caracter ... well ... I put my call for "connections" at the exact point where the caracter is never updated ! So yeah, the thread was never resumed.
And when I continued the connection of the character, (reaching a point where it could be updated), I never tried to let it connected for a while (I was just checking that my function was called and correctly executed, after that, I didn't care) ... the whole point is that my update isn't made that often, so I had to wait a bit (a lot) before having the rest of my script. It was working, I just didn't saw it :-)

Now I have to check why this update character is so slow, seems strange, I assume that i can find a more regulate update, or make it myself.

Anyway, many thanks to both of you, stay tuned, I'll inform you of my progress with this ... and the rest ... as I cannot really test during the day, I was just writing Lua melted with non-code programming (i dunno if it makes sense in english, not real code, natural language but in code-shape )
to add a system of achievement, a check of too old players and empty organisations (clans) and erase it. Man ! I have work to do :p
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #7 on Thu 24 Feb 2011 11:10 PM (UTC)
Message
Aiseant said:
I was just writing Lua melted with non-code programming (i dunno if it makes sense in english, not real code, natural language but in code-shape )

I know what you mean; we call it pseudo-code, which is code that's not really code.

Glad to hear you're working things out! :)

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #8 on Mon 28 Feb 2011 02:29 PM (UTC)

Amended on Mon 28 Feb 2011 02:30 PM (UTC) by Aiseant

Message
Thanks twisol

Maybe you people can help me with a "stupid" thing : I would like to separate my various lua function into many .lua files, and I would like to have various folders for them.
Though, it seems that I'm failing at adding different path for my .lua file.
I tried to change the LUA_PATH in lua_scripting, but without success. I found some traces of the path into the liblua.a, do I need to modify this ?

For instance, I've my folders :
src (where I compile and where my startup.lua)
bin (where my exe is)
area (from where I'm launching my exe, and where I need to put my .lua currently)
lua (where I would like to put my .lua, to be inventive :) )

Thanks by advance for any help (even a "you searched wrong, go there <link>)



[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #9 on Mon 28 Feb 2011 11:31 PM (UTC)
Message
In Lua, the 'package.path' variable contains a list of paths which it will look in when you require() a file. Mine looks like this:

./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua

Split by ; to make it more readable and you can see all the paths it looks in. The ? shows where it inserts the name you passed to require().
./?.lua
/usr/local/share/lua/5.1/?.lua
/usr/local/share/lua/5.1/?/init.lua
/usr/local/lib/lua/5.1/?.lua
/usr/local/lib/lua/5.1/?/init.lua
/usr/share/lua/5.1/?.lua
/usr/share/lua/5.1/?/init.lua

As you can see, first it looks in the current working directory (which is relative to the main process, not the current file), and then it looks in a number of other locations. So if you want to add another path, just tack it onto either end of package.path (depending on when you want that path to be checked).
package.path = package.path + ";/mypath/to/my/libs/?.lua"

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #10 on Tue 01 Mar 2011 12:45 PM (UTC)

Amended on Tue 01 Mar 2011 03:16 PM (UTC) by Aiseant

Message
Yes, I figured this, but I cannot find where is this famous path

There's one in luaconf;h (not lua_scripting, as I said), #define LUA_PATH_DEFAULT
But I failed at adding my own path.

EDIT : of course, it's quite better when you do make the lib again, after your changes :) Thanks for your help.
[Go to top] top

Posted by Twisol   USA  (2,257 posts)  [Biography] bio
Date Reply #11 on Tue 01 Mar 2011 06:24 PM (UTC)
Message
Well, I guess that's one way to do it. But "package.path" is a Lua variable, not a C one (even if its default is probably set using that LUA_PATH_DEFAULT thing). You can just whip up a Lua script to change it at startup.

'Soludra' on Achaea

Blog: http://jonathan.com/
GitHub: http://github.com/Twisol
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #12 on Mon 07 Mar 2011 03:22 PM (UTC)
Message
Peek-a-boo, it's me again !

Pause is working. I'll try to make it "better", since I would like not to update the player until the end of the subroutine, but this is for another time.

Right now, I'm trying just to retrieve the room info, with
a simple local room = mud.room_info ()

Of course, I've got a lovely "attempt to index local room (a nil value)"

I'm calling this inside a function called when a mob is created. I assume I need to precise an argument for it, i'm not sure. Does Lua knows the context ? I mean, I saw that you can just do a local char = mud.char_info () or something, without argument, and assumed that Lua automatically use the context when the function has been called (implicitely knowing that the arg is the player current)

I think I'm wrong, since even the same attempt with mud.mob_info returns the same error.
[Go to top] top

Posted by Aiseant   (33 posts)  [Biography] bio
Date Reply #13 on Wed 06 Apr 2011 09:12 AM (UTC)

Amended on Wed 06 Apr 2011 03:55 PM (UTC) by Aiseant

Message
Up.

How can I retrieve the info of the room the character is ?

Except than by pushing the info before calling the lua function, I need to precise.

Thanks
[Go to top] top

Posted by Nick Gammon   Australia  (23,017 posts)  [Biography] bio   Forum Administrator
Date Reply #14 on Thu 07 Apr 2011 01:29 AM (UTC)
Message
I'm not sure what you have done, how much of the Lua stuff you implemented, and how you are calling it. In lua_scripting.c that function is implemented like this:


static int L_room_info (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
  EXIT_DATA * pexit;
  
  if (lua_isnumber (L, 1))
    room = get_room_index( check_vnum (L) );
 
  if (room == NULL) 
    return 0;  /* oops - not in room or specified room does not exist */
    

/// and so on


I don't know why that should give you "attempt to index local room (a nil value)".

Can you post more of the Lua code?

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


64,191 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]