Coroutine / Schedule Lua code without freezing Wesnoth

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Coroutine / Schedule Lua code without freezing Wesnoth

Post by Yutmeal2 »

How to use coroutines included in multiplayer scenario?

I tried:

Code: Select all

local helper = wesnoth.require("lua/helper.lua")
function cotest()
    while 1 do
        os.execute("sleep " .. tonumber(0.2))
    end
    coroutine.yield()
end
co = coroutine.create(cotest)
[/code]

I get lua error:

attempt to index global 'coroutine' (a nil value)

Mostly I want to run lua function without freezing wesnoth.
Last edited by Yutmeal2 on October 18th, 2017, 4:29 am, edited 2 times in total.
Tad_Carlucci
Inactive Developer
Posts: 503
Joined: April 24th, 2016, 4:18 pm

Re: Coroutines

Post by Tad_Carlucci »

You can't use os.execute().

If you could, you'd never hit the yield because its outside the infinite loop.

Co-routines are NOT multi-tasking. Even if you did a co-routine to split up the workload, you'd still have the engine waiting until your entire process finished.
I forked real life and now I'm getting merge conflicts.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutines

Post by Yutmeal2 »

The thing is, it can't recognize "coroutine". Not "coroutine.yield()", not "coroutine.create(...)". The lua script can't even execute in wesnoth.

Is it possible to ask lua do something (like wait x seconds before changing a label) while wesnoth can still listen to user input?

Ideally, lua should pass control back for wesnoth in those x seconds and notify wesnoth that lua wants control at a specific time... if true multi-tasking is not possible.

I haven't tried using a wesnoth [while] loop to poll result from a lua script. May be I should.

Or a wesnoth while loop with a bunch of lua tags. Each checks if x seconds have passed. If yes, do action. If no, quickly exits. That would work if the player can still move units when wesnoth runs the while loop in background, or if wesnoth has an onrealtime event.
Tad_Carlucci
Inactive Developer
Posts: 503
Joined: April 24th, 2016, 4:18 pm

Re: Coroutines

Post by Tad_Carlucci »

A number of Lua library functions are disabled. Mainly because they're not portable (for instance os.* functions).

I don't recall seeing a 'sleep' function. You could probably write one in C++ but it may be hard to get it accepted because anything which halts the WML/Lua thread is going to be a problem.

I would suggest that you look for an alternate solution. Stopping the game for N seconds while you await a timeout is not going to play well.
I forked real life and now I'm getting merge conflicts.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutines

Post by Yutmeal2 »

I tried os.time() in lua on linux, and it works for 1.12. I don't know if that works on windows.

Code: Select all

wesnoth.message(os.time())
User avatar
Iris
Site Administrator
Posts: 6796
Joined: November 14th, 2006, 5:54 pm
Location: Chile
Contact:

Re: Coroutines

Post by Iris »

debug.traceback, os.clock, os.date, os.time, os.difftime are the only functions allowed from debug and os. From the Lua standard library only core, table, string, and math functions are allowed otherwise, and dofile and loadfile are disabled.
Author of the unofficial UtBS sequels Invasion from the Unknown and After the Storm.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutines

Post by Yutmeal2 »

The wesnoth while loop idea doesn't work. The game freezes for a while and then gives up on running the while loop.
I guess this means wesnoth doesn't listen to user input when it runs the while loop.

I was reading the Tetris source code, which creates four special units.
Whenever the player's cursor hovers the unit, wesnoth displays the unit, and runs the lua code.
The lua code moves and draw the tetromino.

Code: Select all

wesnoth.fire("unit", {
      x=x, y=y,
      side = not_me_side,
      type = "Fog Clearer",
      {"abilities",{{"dummy",{{"filter",{lua_function="tetris_notice"}},}},}},
      {"status",{petrified=true}},
    })
New idea: Put Lua code into an element of wesnoth's user interface that is redrawn regularly.
Wesnoth might run the lua script regularly while still listens to user input.
Probably the clock for turn limit can work half of the time...
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutines

Post by Yutmeal2 »

This works:

Code: Select all

local old_report_countdown = wesnoth.theme_items.report_countdown
old_time = os.clock()
function wesnoth.theme_items.report_countdown()
    new_time = os.clock()
    if new_time - old_time > 1.0 then
        old_time = new_time
        wesnoth.message(new_time)
    end
    return old_report_countdown()
end
What if one of the players pause the game in a multiplayer game? Is the timing still accurate for local time?

I wonder if I can insert lua functions into animation frames. hmm..

~~~~~~~~~~

I tried this code in 1.13.8. This still works, but the timing is not correct.
After the code prints "286.48", I would have to wait 30 seconds instead of 1 second before the code prints "287.48".
Maybe it is because wesnoth 1.13.8 switch to Lua5.2?
The user interface of 1.12 constantly call "report_countdown()", but I guess 1.13.8 doesn't do that.
When I change 1.0 to 0.01, Wesnoth prints the messages much more frequently. So, the user interface still call "report_countdown()" frequently.
Timing by os.clock() is incorrect.

~~~~~~~~~~

If I use os.time() instead of os.clock(), the timing is correct. But I only get 1 second resolution for timing instead of millisecond resolution.

New Idea: Make the code adaptively calculate how many fake miliseconds from os.clock() is equivalent to 1 real second from os.time().
That way, I get an approximate timer with milisecond resolution.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Yutmeal2 »

Timing by calculating approximate conversion between fake time and real time in every second.
It seems not accurate for counting 0.1 seconds.

Code: Select all

old_report_countdown = wesnoth.theme_items.report_countdown
old_clock = os.clock()
old_time = os.time()
old_clock2 = os.clock()
real_dt = 1.0
fake_dt = real_dt
dif = 1.0

function wesnoth.theme_items.report_countdown()
new_clock = os.clock()
new_time = os.time()

if new_time > old_time  then
    old_time = new_time
    dif = (new_clock - old_clock + dif) / 2
    old_clock = new_clock
    wesnoth.message("real sec per fake sec: " .. dif)
    fake_dt = real_dt * dif
end

if new_clock - old_clock2 > fake_dt then
    old_clock2 = new_clock
    wesnoth.message("time: "..os.time() .. ", clock: " .. os.clock())
end

return old_report_countdown()
end
New idea: The scheduler can use approximate time without increasingly lagging behind the actual time
if it knows how to play catch up.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Yutmeal2 »

It would be nice if wesnoth's c++ can provide accurate time to lua.
Like wesnoth.realtime().
User avatar
Sapient
Inactive Developer
Posts: 4453
Joined: November 26th, 2005, 7:41 am
Contact:

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Sapient »

well, of course you are going to lose accuracy if you keep resetting old_time.

FYI, there is also [set_variable] time=stamp, but I don't think you need that.
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."
gfgtdf
Developer
Posts: 1431
Joined: February 10th, 2013, 2:25 pm

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by gfgtdf »

To stop wesntoh from freezeing (to 'pull' user input) wesnoth.delay(0) is usually used.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Yutmeal2 »

Sapient,

Hi. I don't quite get the part about resetting.

I would think the lua virtual machine is fast enough and resetting old_time would only lose accuracy below milliseconds.

The problem is that this old code gives perfectly accurate time in wesnoth 1.12:

Code: Select all

local old_report_countdown = wesnoth.theme_items.report_countdown
old_time = os.clock()
function wesnoth.theme_items.report_countdown()
    new_time = os.clock()
    if new_time - old_time > 1.0 then
        old_time = new_time
        wesnoth.message(new_time)
    end
    return old_report_countdown()
end
It is supposed to print a message every 1 seconds.
But in wesnoth 1.13, I have to wait like about 30 seconds to get one message from the program.
If I move the cursor more often, I have to wait about 45 seconds.
Having the Lua VM to slow down 30x in 1.13 is a bit crazy.
I don't know why because I haven't dived into the c++ part of Wesnoth.
If anyone knows the reason, I am all ears.

I wish wesnoth can provide a function that give accurate system time with milliseconds resolution.
This is because os.clock() in Lua is somehow too unreliable...

When will particle effect be implemented? If it is not in the near future, I might try to make a Lua hack.
Mostly I just want to make an elf clock for fun.

Hmm. I didn't know about "time=stamp"! I might give it a try and see how accurate it is.

@gfgtdf thanks for the tip. If all else fail, I might consider taking control of every thing from Lua side and just occasionally takes in user input.

Upate:

Found "wesnoth.get_time_stamp()" at viewtopic.php?f=10&t=46370&start=0
Yutmeal2
Posts: 13
Joined: October 14th, 2017, 3:02 am

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Yutmeal2 »

  • Timer that catches up with target.
  • At most 1 millisecond unrecoverable lag/gain per firing.
  • Works in 1.13
  • 3% cpu load ...

Code: Select all

local old_time = wesnoth.get_time_stamp()
local old_report_countdown = wesnoth.theme_items.report_countdown

local target = 1000
local dif, new_time
local target2 = target
function wesnoth.theme_items.report_countdown()
    new_time = wesnoth.get_time_stamp()
    dif = new_time - old_time - target2
    if dif >= 0 then
        old_time = new_time
        target2 = target - dif
        wesnoth.message(old_time)
    end
    return old_report_countdown()
end

  • Timer that catches up with target.
  • No unrecoverable lag/gain
  • Works in 1.13
  • 11% cpu load ...

Code: Select all

local old_report_countdown = wesnoth.theme_items.report_countdown
local target = 1000
local new_time
local target_time = wesnoth.get_time_stamp() + target
function wesnoth.theme_items.report_countdown()
    new_time = wesnoth.get_time_stamp()
    if new_time >= target_time then
        target_time = target_time + target
        wesnoth.message(new_time)
    end
    return old_report_countdown()
end
  • Timer that catches up with target.
  • No unrecoverable lag/gain
  • Works in 1.13
  • 3% cpu load ...

Code: Select all


local old_report_countdown = wesnoth.theme_items.report_countdown
local target = 1000
local dif
local target_time = wesnoth.get_time_stamp() + target
function wesnoth.theme_items.report_countdown()
    dif = wesnoth.get_time_stamp() - target_time
    if dif > 0 then
        wesnoth.message(target_time)
        target_time = target_time + target
    end
    return old_report_countdown()
end

  • 2.5% cpu load

Code: Select all


local old_report_countdown = wesnoth.theme_items.report_countdown
local target = 1000
local target_time = wesnoth.get_time_stamp() + target
function wesnoth.theme_items.report_countdown()
    if wesnoth.get_time_stamp() - target_time > 0 then
        wesnoth.message(target_time)
        target_time = target_time + target
    end
    return old_report_countdown()
end

User avatar
Celtic_Minstrel
Developer
Posts: 2158
Joined: August 3rd, 2012, 11:26 pm
Location: Canada
Contact:

Re: Coroutine / Schedule Lua code without freezing Wesnoth

Post by Celtic_Minstrel »

For the record, coroutines in Lua are supported only starting in 1.13, so since you're using 1.12, they won't be available.
Author of The Black Cross of Aleron campaign and Default++ era.
Former maintainer of Steelhive.
Post Reply