Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ SMAUG
➜ Lua
➜ 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 page
Pages: 1 2
Posted by
| Aiseant
(33 posts) 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 )
| Top |
|
Posted by
| Twisol
USA (2,257 posts) 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:
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 | Top |
|
Posted by
| Aiseant
(33 posts) 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. | Top |
|
Posted by
| Twisol
USA (2,257 posts) 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 | Top |
|
Posted by
| Aiseant
(33 posts) 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. | Top |
|
Posted by
| Nick Gammon
Australia (23,133 posts) 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 | Top |
|
Posted by
| Aiseant
(33 posts) 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 | Top |
|
Posted by
| Twisol
USA (2,257 posts) 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 | Top |
|
Posted by
| Aiseant
(33 posts) 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>)
| Top |
|
Posted by
| Twisol
USA (2,257 posts) 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 | Top |
|
Posted by
| Aiseant
(33 posts) 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.
| Top |
|
Posted by
| Twisol
USA (2,257 posts) 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 | Top |
|
Posted by
| Aiseant
(33 posts) 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. | Top |
|
Posted by
| Aiseant
(33 posts) 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 | Top |
|
Posted by
| Nick Gammon
Australia (23,133 posts) 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 | 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.
69,780 views.
This is page 1, subject is 2 pages long: 1 2
It is now over 60 days since the last post. This thread is closed.
Refresh page
top