[Question] Lua scripting.
juliolitwin opened this issue · comments
Your project is incredible, it's rare to see emulation projects for C# lovingly developed! :)
There isn't a discord group or anything like that, so I would like to ask about an issue regarding lua.
It's not necessarily about MS, but rather your implementation of Lua, I saw that some of your scripts wait for a response from the user, such as ask_menu, could you explain to me how you manage this?
Sample:
if target.field == mapFields.getID("NLCSubwayStation") then
selection = self.ask_menu("Hello. Would you like to buy a ticket for the subway?", {
[0] = "Kerning City of Victoria Island"
})
if selection == 0 then
ticket = charInventory.selectTicketToVictoriaFromNLC()
cost = charInventory.getPriceOfTicket(ticket)
if self.ask_yes_no("The ride to Kerning City of Victoria Island takes off every " .. target.as_continent().term .. " minutes, beginning on the hour, and it'll cost you #b" .. cost .. " mesos#k. Are you sure you want to purchase #b#t" .. ticket .. "##k?") then
charInventory.buyTicket(ticket, cost)
else
self.say("You must have some business to take care of here, right?")
end
end
When ask_menu is called, you call AskMenu (in C#), but in the Lua background, it waits for the return at the exact point where it stopped executing? Do you make any kind of use of threads/coroutines?
this is the important part were registering an entire conversation interface for 2 speakers
this exposes the accessibility to lua to call the func with params
so the interface is loaded in the script globals and then the entire source is evaluated if it finds reference to the global it can call the C# function.
the heavy lifting is done by moonsharp, thats where the magic of converting a DynValue result which is placeholder for lua types gets auto converted to CLR type.
On top of what @Bia10 said,
The whole request/response system is called Conversations in the project. Do check out FieldUser::Converse
and FieldUser::Prompt
to see where the logic flow starts for script dialogs.
Essentially, there is a Channel<object>
in each conversation context which is basically an async queue that handles request and responses from the client. This creates the stateless 'awaiting' mechanism that powers every dialog that requires user input.
Every time a method like Self.AskMenu(..)
is called, the conversation context reads from the async queue and if its empty, it waits.. forever. And on the packet handling side, whenever a response is received by the server, the server pushes into the async queue which finally unblocks the read and continues on with the conversation.
See:
Closing this issue, feel free to continue the discussion under the 'Discussions' tab.