Moon Logic deprecated

by mk-fg

Adds Lua-programmable circuit network combinator. Based on Sandboxed LuaCombinator and LuaCombinator2 mods. Probably won't work in multiplayer games.

Content
2 years ago
1.0 - 1.1
4.97K
Circuit network

g [lua-qiz] Is there away to "yield" for a tick from a script?

1 year, 9 months ago
(updated 1 year, 9 months ago)

I'm looking for a way to stop at a point in the script, return control to the game, and then continue in the next tick. Looks like delay = n doesn't take effect till the script fully returns.

Use case I'm looking to solve without implementing a full state machine is to put out a set of outputs to an AAI Zone Controller for one tick then clear the outputs and continue. Ideal would be something like below:

if red["signal-0"] > 0 then -- ready for next point, output a pulse to mark next zone tile.
out["signal-x-tile"] = test_ul_tile_x
out["signal-y-tile"] = test_ul_tile_y
out["zone-x-green"] = 1
yield(1) -- yield for one tick; delay = x only has effect at end of script.
out= {}
end

Not sure if this exists, and not worth adding if it doesn't - but thought I'd check.

1 year, 9 months ago

There's no easy way that I know of.

Lua has coroutines that work like that, but don't think they're available in factorio's lua in particular.
And even if they were, doubt they could've been used in the context of this mod, as factorio doesn't allow to put code objects into savegames, much less running code objects with some kind of frozen state in them.

One way to implement it can be some kind of special syntax for splitting code string into multiple parts to run separately, but that'd break your example above immediately due to if...end block, and I think would be much more clear to implement yourself, rather than relying on some weird templating magic full of special-cases (like that "no blocks" rule).

So yeah, doesn't exist, and don't think there's a good way to implement it in the context of the mod itself.

But should be fairly obvious for how to do such thing with the combinator code, as you can save state between runs in globals or even "out" var.

E.g. something like this:

if var.flush then
  irq, out, delay, var.flush = 'signal-0', {}, 1e10
  return
end

out['signal-x-tile'] = test_ul_tile_x
out['signal-y-tile'] = test_ul_tile_y
out['zone-x-green'] = 1
...

var.flush = true

For splitting code into even more such blocks, I'd recommend using common "state machine" pattern:
https://mods.factorio.com/mod/Moon_Logic/discussion/62299bb657d5edc3b2da26dd

Which allows to debug the code and reason about how it works very easily, once you explicitly track which block will be run next and can see all possible transions from there. ("var.flush" in example above is kinda that "state" in its simpliest boolean form)

1 year, 9 months ago
(updated 1 year, 9 months ago)

I did it in a much simpler way with a table based state machine.

I created a table, "states" of functions with one for each state and a variable for next_state. On each tick, the script calls the function in next_state, which does one step, then sets next_state to states.<state to go to next>.

There is one special state: state.PULSE_OUTPUTS. That state function uses a special variable next_next_state. When I need to pulse an output, I just sent next_next_state to where to go after the pulse, and set next_state = state PULSE_OUTPUT, then return. PULSE_OUTPUT clears the outputs before setting the next state and returning. Hid the mechanics in a simple function pulse(state, delay) I can call from any other state.

Script itself is just the state table definition with anonymous or named function assignments, a quick initialization if next_state == nil, and a call to next(state).

Works great and has the benefit of naturally breaking up the code into small UPS friendly chunks.

New response