Implement synchronization of Lua scripts to handle events: Lua coroutine

Source: Internet
Author: User

Requirement

Affected by wow, Lua is increasingly used in games. Scripts are used in games to plan and compile game rules. In practical use,
We will bind many host language functions to the Lua script so that the script can control the program running more. For example, we can bind functions such as npcdialog.
In Lua, you can control the NPC dialog box displayed in the game in the script.
We are now faced with the requirement that some features of the Host Program cannot block the program logic (especially for game programs), but
To facilitate planning, we need to make the script look blocked. For example, the npcdialog script contains the following code: ret = npcdialog ("Hello bitch ")
If ret = OK then print ("OK") End

For planning, npcdialog should be blocked. Unless the player operates this dialog box, click OK or close, the function will not return. For
For the Host Program C ++, how can we implement this function:

 

 

Static int do_npc_dialog (lua_state * l)
{
This. style. Display = 'none'; codehighlighter1_49_169_open_text.style.display = 'none'; codehighlighter1_49_169_closed_image.style.display = 'inline'; interval = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif "alt =" "align =" TOP "> {
This. style. Display = 'none'; items = 'none'; codehighlighter1_49_169_open_image.style.display = 'inline'; codehighlighter1_49_169_open_text.style.display = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif "alt =" "align =" TOP "> {
Const char * content = lua_tostring (L,-1 );

Lua_pushnumber (RET );
Return 1;
}

Obviously, this function cannot be blocked, otherwise it will block the entire game thread, which is not feasible for the server. However, if the function returns immediately
It does not collect players' operations on that dialog box.
In summary, what we need to do is to make the script feel that an operation is blocked, but in fact the Host Program is not blocked.

 

Event Mechanism

The simplest implementation (which may be elegant for C programmers) is to use the event mechanism. The Operation Result of the dialog box is used as an event.
In fact, no function in the script is blocked. To process the processing results of some "blocking" functions, the script registers the event processor (same as the GUI event) with the Host Program.
The processing is actually the same. For example, the script can be like this: function onevent (RET)
If ret = OK then print ("OK") End
End
-- Register event handler
Seteventhandler ("onevent ")
Npcdialog ("Hello bitch ")

The host program saves the onevent function name of the event processor. After the player operates the dialog box, the host program calls back the onevent in the script to complete the operation.
In fact, I believe many people do. In this way, a code stream for sequential execution is divided into many blocks. But for sleep
What about such a script call? For example:

 

 

-- Do job
Sleep (10)
-- Do Job B
Sleep (10)
-- Do job C

Using the event mechanism may split the code:

Function onjoba
-- Do job
Seteventhandlerb ("onjobb ")
Sleep (10)
End
Function onjobb
-- Do Job B
Seteventhandlerc ("onjobc ")
End
Function onjobc
-- Do job C
End
-- Script starts here
Seteventhandlera ("onjoba ")
Sleep (10)

The code looks a little ugly. The most important thing is that it is not easy to write, and planning may be crazy. I think, for non-professional programmers
Sequential execution may be easier to understand.

 

Solve it

Our solution has only one sentence: When the script is executed into a blocking operation (such as npcdialog), the script is suspended and the host program completes an operation.
Run the script from the starting point.
This is not a hypothetical function. Before I started to implement this function, I thought Lua did not support this function. I imagine the following operations:
Script:
Ret = npcdialog ("Hello bitch ")
If ret = 0 then print ("OK") End
Host Program: static int do_npc_dialog (lua_state * l)
{
This. style. Display = 'none'; codehighlighter=49_assist_open_text.style.display = 'none'; codehighlighter=49_eclip_closed_image.style.display = 'inline'; inline = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif "alt =" "align =" TOP "> {
This. style. Display = 'none'; items = 'none'; codehighlighter1_49_1__open_image.style.display = 'inline'; codehighlighter1_49_1__open_text.style.display = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif "alt =" "align =" TOP "> {

Lua_suspend_script (L );

}

An operation has been completed in a certain place:
Lua_resume_script (L );
When I implemented this function, I suddenly found that the actual situation is similar to what I thought here (a bit of shame ).

 


Coroutine

Coroutine is a thread-like thing in Lua, but it is actually more similar to fiber. That is to say, it is a non-preemptible thread, and Its Switching depends on
It depends on you, and you decide when to switch tasks. Read more about Lua manual.
Coroutine supports the following typical operations: lua_yield and lua_resume, that is, we need to suspend and continue execution.
Lua_state seems to be a coroutine, or, according to another statement in the Lua document, it is a thread. The reason why I use 'seems' is
Because I cannot determine it myself, I can only say that lua_state looks like a coroutine.
Lua provides lua_newthread to manually create a coroutine, and then place the newly created coroutine on the top of the stack, just like other new
The same object. There is a post on the Internet saying that the things created by lua_newthread are different from those created by calling coroutine. Create in the script, but according to my
They are the same. Lua_newthread returns a lua_state object, so we can see from here that "lua_state looks like
Coroutine ". In addition, some people on the Internet say it is costly to create a new coroutine, but what is the cost of a lua_state? Of course, I did not
Test.
Lua_yield is used to suspend a coroutine, but this function can only be used within coroutine. You can see its parameters.
Lua_resume is used to start a coroutine. It can be used to start coroutine when it is not running or restart coroutine when it is suspended.
. Lua_resume is returned in either of the following situations: coroutine suspended or executed; otherwise, lua_resume is not returned.
Lua_yield and lua_resume correspond to the script functions coroutine. Yield and coroutine. Resume. We recommend that you write the script program to feel coroutine,
For example:

Function main ()
Print ("Main start ")
Coroutine. Yield ()
Print ("main end ")
End
CO = coroutine. Create (main );
Coroutine. Resume (CO)

Really Solve it

 

You may think that we define a main for the script, then create a coroutine in lua_newthread in the Host Program, and then put the main in,
When the script calls a 'block' operation of the Host Program, the host Program obtains the previously created coroutine and then yield. When the operation is complete, resume
.
In fact, the method is correct, but there is no need to create another coroutine. As mentioned previously, a lua_state looks like a coroutine,
We always have a lua_state. This lua_state is like main coroutine. (Like your main thread)
The idea is like this, because there are still some problems in the specific implementation, so I will list the code for each step.
When the initial lua_state is set to lua_state * l = lua_open ();
Luaopen_base (L );

Register the host program functions required by the script to L:

Lua_pushcfunction (L, sleep );
Lua_setglobal (L, "my_sleep ");

Loading and executing a script file is slightly different:

Lual_loadfile (L, "test. Lua ");
{
This. style. Display = 'none'; codehighlighter1_60_73_open_text.style.display = 'none'; codehighlighter1_60_73_closed_image.style.display = 'inline'; inline = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif "alt =" "align =" TOP "> {
This. style. Display = 'none'; items = 'none'; codehighlighter1_60_73_open_image.style.display = 'inline'; codehighlighter1_60_73_open_text.style.display = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif "alt =" "align =" TOP "> lua_resume (L, 0);/*** // call resume */

You need to suspend coroutine in your 'block' function:

Return lua_yield (L, 0 );

Note: The lua_yield function is very special and must be called as a return statement. Otherwise, the call will fail. I am not sure about the specific cause. And here,
Is it the return value of lua_cfunction, will it cause an error? Because the lua_cfunction Convention returns the number of returned values of this function for the script.
The actual situation is that in some examples I have seen this arrangement lua_yield, so I do what they do.

 

After this operation is completed (for example, if the player operates the dialog box), the host program needs to wake up coroutine: lua_resume (L, 0 );

 

This is the general procedure. If you want to create a new lua_state separately, it will be very troublesome. At first, I did that and I could not achieve it myself.
Expected results.

 

8.13 supplement

 Sometimes, the function we provide to the script needs to return some values to the script. For example, npcdialog returns the operation result, we only need to lua_resume in the Host Program.

Previously, the return value can be pushed. Of course, the second parameter lua_resume needs to be set to the number of returned values.

2.9.2010
The second parameter of lua_yield (L, nresults) specifies the number of values returned to lua_resume. Lua_pushnumber (L, 3 );
Return lua_yield (L, 1 );
..
Int ret = lua_resume (L, 0 );
If (ret = lua_yield)
{
This. style. Display = 'none'; codehighlighter=134_assist_open_text.style.display = 'none'; codehighlighter=134_eclip_closed_image.style.display = 'inline'; inline = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif "alt =" "align =" TOP "> {
This. style. Display = 'none'; items = 'none'; codehighlighter1_134_1__open_image.style.display = 'inline'; codehighlighter1_134_1__open_text.style.display = 'inline ';
} "Src =" http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif "alt =" "align =" TOP "> {
Lua_number r = lual_checknumber (L,-1 );
}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.