Alternative implementation of "wait".
#1

Hey,

As you may know, Y_Less once stated that "wait/Sleep is considered harmful" here:

https://sampforum.blast.hk/showthread.php?tid=257660

So anyways, I've been working on a library recently that allows the ability to use "Wait" inside another function without hanging the server. This is achieved by:
  • Loading the address and CIP of the current function and saving it somewhere.
  • Halting the execution of the current function (at the location Wait was used).
  • Setting up a timer for xx milliseconds that will call the saved function.
  • When the timer is called, jump to the saved function and CIP + 1.
(CIP means code instruction pointer, by the way).

The explanation above sounds complex, however the only to achieve this is to dig down into assembly by relying on the "LCTRL" and "SCTRL" instructions.

This will allow code like:

pawn Code:
Countdown(playerid)
{
    GameTextForPlayer(playerid, "~g~3", 3000, 3);
    Wait(1000);

    GameTextForPlayer(playerid, "~g~2", 3000, 3);
    Wait(1000);

    GameTextForPlayer(playerid, "~g~1", 3000, 3);
    Wait(1000);

    GameTextForPlayer(playerid, "~g~Go!", 3000, 3);
}
AFAIK, there's also a neat way to do it with y_inline, however the purpose of this is to implement a single-use function that can be used anywhere.

Is this a good idea or not? Thanks!
Reply
#2

I'd say good idea _Emmet instead of doing this, I'd like to see this released soon :P, anyways good luck with this.

pawn Code:
forward Wait1();
public Wait1()
{
    GameTextForPlayer(playerid, "~g~3", 3000, 3);
    SetTimer("Wait2", 1000, false);
}

forward Wait2();
public Wait2()
{
    GameTextForPlayer(playerid, "~g~2", 3000, 3);
    SetTimer("Wait3", 1000, false);
}

forward Wait3();
public Wait3()
{
    GameTextForPlayer(playerid, "~g~1", 3000, 3);
    SetTimer("WaitGo", 1000, false);
}

forward WaitGo();
public WaitGo()
{
    GameTextForPlayer(playerid, "~g~GO", 3000, 3);
}
Reply
#3

Quote:
Originally Posted by Y_Less
View Post
I did write a "W" macro in QWERTYUIOP that did this:

http://ysi.wikia.com/wiki/Library:QWERTYUIOP

But it hasn't been updated for YSI 4.0 (and doing so could be tricky). There are also numerous implementation issues (especially regarding the heap) discussed here:

http://forum.sa-mp.com/showthread.ph...05#post1944105

A stand-alone version might be useful, but one based on y_inline would take advantage of the vast optimisations done in there.
Great, nice. I noticed that the one on "QWERTYUIOP" does it much simpler than my method! I was thinking of using it, but are there any limitations that I should be aware of (e.g. using it in loops, in a return, etc)?
Reply
#4

You can do an easy trick with a Define

Code:
#define Wait(%0)<%1> return SetTimer("@wait_"#%1,%0,false); } \
					forward @wait_%1(); @wait_%1() {
usage:

Code:
public OnGameModeInit()
{
	// Don't use these lines if it's a filterscript
	SetGameModeText("Blank Script");
	AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
	print("1");
 	Wait(1000)<a>
	print("2");
	Wait(1000)<b>
	print("3");
	Wait(1000)<c>
	print("4");
	return 1;
}
I thought about a other way but i don't know if it possible to get the address of a goto label to jump to a label anywhere in your code.
Reply
#5

Quote:
Originally Posted by Y_Less
View Post
That is a good solution when it works, but if you have local variables it will fail:

pawn Code:
public OnGameModeInit()
{
    new a = 42;
    print("1");
    Wait(1000)<x>
    printf("2 %d", a); // Error.
    return 1;
}
But there is even the possibility to use SetTimerEx
Code:
#define Wait(%0)<%1,%2,%3> return SetTimerEx("@wait_"#%1,%0,false,%2,%3); } \
					forward @wait_%1(%3); @wait_%1(%3) {
but the only problem of this method is that you can't use strings
Reply
#6

Nice, but what if Wait is inside a loop, a return, or perhaps like this:

pawn Code:
if (Wait(1000)<a>)
Or perhaps this:

pawn Code:
public OnPlayerConnect(playerid)
{
    Wait(1000)<a>;
    SendClientMessage(playerid, -1, "Welcome."); // Undefined symbol "playerid"
}
Reply
#7

Quote:
Originally Posted by Emmet_
View Post
Nice, but what if Wait is inside a loop, a return, or perhaps like this:

pawn Code:
if (Wait(1000)<a>)
Or perhaps this:

pawn Code:
public OnPlayerConnect(playerid)
{
    Wait(1000)<a>;
    SendClientMessage(playerid, -1, "Welcome."); // No Undefined symbol "playerid" :D
}
with the secound method you can do something like that

Code:
#define Wait(%0)<%1,%2,%3> return SetTimerEx("@wait_"#%1,%0,false,%2,%3); } \
					forward @wait_%1(%3); @wait_%1(%3) {
Code:
public OnPlayerConnect(playerid)
{
    Wait(1000)<a,"i",playerid>;
    SendClientMessage(playerid, -1, "Welcome."); // No Undefined symbol "playerid" :D
}
Reply
#8

The idea of this concept is to introduce a Wait(ms) function that can be used on the fly, without having to pass any extra arguments or make adjustments to the code. This is a complex operation that can't be implemented in any other means of PAWN code, in this case we use assembly instructions for implementing the final code.

While I agree that the macro would be a nice method, it would entirely break with most things. I've had functions with over 10 arguments once, imagine doing this:

pawn Code:
Wait(10000)<a, "ddddffffff", playerid, playerobject, objectid, response, Float:fX, Float:fY, Float:fZ, Float:fRotX, Float:fRotY, Float:fRotZ);
It just doesn't seem convenient to me.
Reply


Forum Jump:


Users browsing this thread: 2 Guest(s)