Lua scripting in OpenMW

Everything about development and the OpenMW source code.
Post Reply
User avatar
urm
Posts: 83
Joined: 02 Jun 2017, 16:05
Gitlab profile: https://gitlab.com/uramer

Re: Lua scripting in OpenMW

Post by urm »

AnyOldName3 wrote: 15 Nov 2020, 00:24 I'm not convinced this has been thought through. Rendering works on the world state after all processing has been done, so you're working on the script data for the next frame if you're running scripts in parallel with rendering. That means a frame of latency for anything involving scripts, which is bad, and probably breaks things. If you want scripts not to have latency, they need to be a distinct phase in the frame after non-script updates and before rendering.
This is actually a good point. One example where this would fail would be a local script which just sets an actor's location to a constant. The expectation would be that the actor never moves, but with this structure they would move by one frame worth of core engine logic. Obviously, this particular use case can be implemented better, but more advanced behavior would be screwed.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

AnyOldName3 wrote: 15 Nov 2020, 00:24
Extracting the whole scripting to a separate thread is more important than multithreaded scripting. It should be safe because rendering and scripting work on different data.
I'm not convinced this has been thought through. Rendering works on the world state after all processing has been done, so you're working on the script data for the next frame if you're running scripts in parallel with rendering. That means a frame of latency for anything involving scripts, which is bad, and probably breaks things. If you want scripts not to have latency, they need to be a distinct phase in the frame after non-script updates and before rendering.
urm wrote: 15 Nov 2020, 13:42 This is actually a good point. One example where this would fail would be a local script which just sets an actor's location to a constant. The expectation would be that the actor never moves, but with this structure they would move by one frame worth of core engine logic. Obviously, this particular use case can be implemented better, but more advanced behavior would be screwed.
Maybe I miss something, but I don't see how it can affect actors. World state is not changing during rendering. If we process scripts during the rendering of the previous frame, results should be completely the same as it were if we run scripts after the rendering. The sequence of steps is not changed.

The only difference can be with the latency of user controls. I.e. one frame delay between pressing "activate" and seeing the scripted event. However it will not affect player movement and attacks because it is not directly controlled by scripts.

I think it makes sense to have a separate scripting thread as an optional feature that can be disabled in settings.
User avatar
AnyOldName3
Posts: 2668
Joined: 26 Nov 2015, 03:25

Re: Lua scripting in OpenMW

Post by AnyOldName3 »

We're already processing the world state during the rendering of the previous frame. If scripting happens then, too, then it's necessarily ignoring that frame's world state changes.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

AnyOldName3 wrote: 15 Nov 2020, 19:18 We're already processing the world state during the rendering of the previous frame. If scripting happens then, too, then it's necessarily ignoring that frame's world state changes.
Could you point me to the place in the code where it is happening?
User avatar
cc9cii
Posts: 523
Joined: 28 Mar 2013, 04:01

Re: Lua scripting in OpenMW

Post by cc9cii »

ptmikheev wrote: 14 Nov 2020, 18:03 I've written some ideas in a google doc. Open for comments.

Key points about threads and events:
  • Extracting the whole scripting to a separate thread is more important than multithreaded scripting. It should be safe because rendering and scripting work on different data.
  • Local scripts are always synchronized with the main thread. I.e. scripting rate is the same as frame rate.
  • In multiplayer global scripts are server-side and work asynchronous. I.e. there is no "frames" there.
  • Global scripts can not be distributed over several workers because for global scripts full access to the world state and direct communication with other global scripts is an essential functionality. So all global scripts are in a single thread. In future we can allow global scripts to start new worker threads, but such threads will not have any access to the game world and the event system.
  • Local scripts can be be distributed over several workers, but all scripts attached to the same actor should be processed by the same worker. Each worker sees the world state as it was at the beginning of the frame. All changes are synchronized by the main scripting thread after all local scripts are processed.
  • Sending an event is always a non-blocking operation that only adds the event to some queue. All local events will be delivered only after the end of the frame.
  • The engine itself (i.e. what is written in C++) doesn't support events with a result. But such functionality can be implemented as a Lua library on top of normal events.
I'm intrigued by this - would you mind adding some commentary on each bullet point to explain the thought behind them? Is this geared towards multiplayer setup?
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

cc9cii wrote: 15 Nov 2020, 23:32 I'm intrigued by this - would you mind adding some commentary on each bullet point to explain the thought behind them? Is this geared towards multiplayer setup?
My aim is to develop a universal scripting system that will be convenient for both singleplayer and multiplayer. Also it should allow to dehardcode most of mwmechanics and AI packages.
I haven't started coding yet. Still thinking about high level design.

> Extracting the whole scripting to a separate thread is more important than multithreaded scripting. It should be safe because rendering and scripting work on different data.

Currently scripting (mwscript) and rendering are both in the main thread and rendering takes most of the time. The new scripting system will presumably be used very extensively (especially when we deharcode game mechanics), so processing scripts can take time comparable to rendering, and moving it to a separate thread can be very useful.

> Local scripts are always synchronized with the main thread. I.e. scripting rate is the same as frame rate.

Local lua scripts - scripts that are attached to some actor and object. In multiplayer local lua scripts are client-side.
In future all vanilla scripts will be converted to local lua scripts and most of game mechanics can also become local lua scripts. To preserve current behaviour it should work synchronously with the main thread with the same frame rate.

> In multiplayer global scripts are server-side and work asynchronous. I.e. there is no "frames" there.

Global lua scripts are not intended only for multiplayer. It will be essential in singleplayer as well. But to make any script compatible with multiplayer the scripting API will not allow register per frame handler (i.e. run something every frame) in global scripts.

> Global scripts can not be distributed over several workers because for global scripts full access to the world state and direct communication with other global scripts is an essential functionality. So all global scripts are in a single thread. In future we can allow global scripts to start new worker threads, but such threads will not have any access to the game world and the event system.

"direct communication with other global scripts" means that one mod is a framework that is used by other mods. Events are not convenient here because events are non-blocking and will always have latency.

> Sending an event is always a non-blocking operation that only adds the event to some queue. All local events will be delivered only after the end of the frame.

Without such rule it would be hard to support multiplayer because most of the events should be sent via network. Also it is needed to support multithreading in local scripts.


Do you have any specific questions?
User avatar
cc9cii
Posts: 523
Joined: 28 Mar 2013, 04:01

Re: Lua scripting in OpenMW

Post by cc9cii »

Thanks for taking time to explain in detail. I am trying to get back into some work after a break of 3 months, and I thought scripting support might be something suitable. I'm thinking that if I'm doing something it may as well be somewhat compatible with what OpenMW is doing/planning.

At this point I'm trying to get a feel for what the high level design is. Have I understood correctly:

* there is a global script running (more than one?)
* an object may have local scripts running on separate threads
* local and global communicate asynchronously

EDIT: would you have an example of a global script?

EDIT2: I read the original post and the terminology such as global script makes much more sense
Last edited by cc9cii on 17 Nov 2020, 02:45, edited 1 time in total.
User avatar
AnyOldName3
Posts: 2668
Joined: 26 Nov 2015, 03:25

Re: Lua scripting in OpenMW

Post by AnyOldName3 »

Rendering is entirely done on the draw thread. That's why it's called the draw thread. If you press F3 until the profiler comes up, you'll see the draw thread overlaps most of the frame.

A part of the main thread is spent deciding what to render. This is labelled Cull in the profiler. Depending on the frame, it can be a big or small chunk. This operates on the scene graph which has just been updated by the previous step of the main thread, called Update in the profiler, and non-scenegraph-specific updates, like moving things around, that happen in the other sections of the main thread (e.g. physics, scripting and world).

OSG provides facilities to move the cull phase off the main thread, but because lots of the rest of the main thread makes changes to the scene graph, the synchronisation efforts (and double-buffering we'd need so that you can cull one frame while building the next one) would make that a net negative for performance. It'd also make the code a lot more complicated.

It's not true that the rendering-related traversals (Update and Cull) work on different data to the rest of the main thread.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

cc9cii wrote: 16 Nov 2020, 06:18 EDIT: would you have an example of a global script?
Sorry for delay with the answer. Still thinking how the API should look like. On the weekend I'll write an example with both local and global scripts.
ptmikheev
Posts: 69
Joined: 01 Jun 2020, 21:05
Gitlab profile: https://gitlab.com/ptmikheev

Re: Lua scripting in OpenMW

Post by ptmikheev »

Post Reply