Page 1 of 1

registerTimer() - How to call a class function

Posted: Tue Nov 04, 2008 11:26 pm
by Sgraffite
I'm trying to use registerTimer() to calll a class function, but I am getting the error:

Code: Select all

function arguments expected near ')'
I've tried calling it like this:

Code: Select all

registerTimer("update", 100, char:update);
registerTimer("update", 100, self:update);
if I just try to pass the function name without self: or char: I get this error:

Code: Select all

Non-function type passed to registerTimer() where afunction is expected
The update function does not require any arguments, am I doing something wrong?

Re: registerTimer() - How to call a class function

Posted: Wed Nov 05, 2008 12:48 am
by Administrator
The : operator is for calling functions. You should use the . (dot) operator in this case. These kind of specifics are a bit awkward for Lua, but you'll get used to it.

Example:

Code: Select all

SomeClass = { };
SomeClass.__index = SomeClass;

function SomeClass.create(_something)
  local tmp = { };
  setmetatable(tmp, SomeClass);
  tmp._something = _something;

  return tmp;
end

function SomeClass:someFunction()
  printf("This is a test.\n");
end

function main()
  local myClass = SomeClass.create(1);
  registerTimer("test", secondsToTimer(1), myClass.someFunction);

  while(true) do
    yrest(10);
  end
end
startMacro(main);

And, if you're using the class module (which you probably should unless you're writing a module yourself or something):

Code: Select all

SomeClass = class();

function SomeClass:test()
  printf("Hello\n");
end

function main()
  local myClass = class(SomeClass);

  registerTimer("test", secondsToTimer(1), myClass.test);

  while(true) do
    yrest(10);
  end
end
startMacro(main);
Here's a good page on Lua classes: http://lua-users.org/wiki/SimpleLuaClasses

Re: registerTimer() - How to call a class function

Posted: Wed Nov 05, 2008 10:06 pm
by Sgraffite
I should be able to figure this out, I was using that website to base my class structure from. It's got to be something really simple, I'm just not seeing it.

I've fixed the previous error, but I'm getting a different error now:

Code: Select all

attempt to index 'self' a nil value
By my understanding, creating the class and creating an instance of it, it should know what self is. As far as I can tell I did just that, but it does not know what self is.

Here is the code I'm using:
class_character.lua

Code: Select all

Character = class(function(obj,proc)
    obj.proc = proc;
    obj:init();
  end
)

function Character:init()
    character_ptr = 0x081CBEC;
    hp_max_offset = 304;
    mp_max_offset = 312;
    sp_max_offset = 320;
    
    self.hp_max = memoryReadIntPtr(self.proc, character_ptr, hp_max_offset);
    self.mp_max = memoryReadIntPtr(self.proc, character_ptr, mp_max_offset);
    self.sp_max = memoryReadIntPtr(self.proc, character_ptr, sp_max_offset);
end

function Character:update()
    character_ptr = 0x081CBEC;
    hp_offset = 300;
    mp_offset = 308;
    sp_offset = 316;
    
    pos_x = 16;
    pos_y = 20;
    pos_z = 24;
    pos_rot_x = 28;
    pos_rot_y = 36;
    
    targetid_addr = 0x006C7BC4; -- short, 65535 if none selected
    
    self.hp = memoryReadIntPtr(self.proc, character_ptr, hp_offset);
    self.mp = memoryReadIntPtr(self.proc, character_ptr, mp_offset);
    self.sp = memoryReadIntPtr(self.proc, character_ptr, sp_offset);
    
    self.x = memoryReadFloatPtr(self.proc, character_ptr, pos_x);
    self.y = memoryReadFloatPtr(self.proc, character_ptr, pos_y);
    self.z = memoryReadFloatPtr(self.proc, character_ptr, pos_z);
    self.rot_x = memoryReadFloatPtr(self.proc, character_ptr, pos_rot_x);
    self.rot_y = memoryReadFloatPtr(self.proc, character_ptr, pos_rot_y);
    printf("x="..self.rot_x.."\n");
    printf("y="..self.rot_y.."\n");
end
main:

Code: Select all

include( "class_character.lua" );

startKey = key.VK_INSERT;
stopKey = key.VK_DELETE;

function main()
    proc = openProcess(findProcessByExe("game.exe"));
    win = findWindow("Shaiya");
    attach(win);
    setPriority(PRIORITY_HIGH);

    --instantiate new character class
    local myChar = class( Character );
    --registerTimer("update", 100, myChar.update);
    
    while( true ) do
        myChar.update();
        yrest(500);
    end
end

startMacro(main);

Re: registerTimer() - How to call a class function

Posted: Wed Nov 05, 2008 10:55 pm
by Administrator
If I had to guess, it would be this line:

Code: Select all

myChar.update();
You should replace . with : to call the function as this is a specific instance of that class (and, hence, 'self' would be usable).

Re: registerTimer() - How to call a class function

Posted: Wed Nov 05, 2008 11:35 pm
by Sgraffite
Thanks a bunch, can't believe I didn't see that, was throwing me off for a while :(

Re: registerTimer() - How to call a class function

Posted: Thu Nov 06, 2008 6:17 am
by zer0
Yeah I often accidentally use "." instead of ":". :) And the error that it generates doesn't isolate the issue down easily. ;)

Re: registerTimer() - How to call a class function

Posted: Thu Nov 06, 2008 11:22 pm
by Sgraffite
I was messing around with your example some more, because I can't for the life of me get registerTimer to call a function that accesses anything involving "self", and have said function be able to access self.

Using this code:

Code: Select all

startKey = key.VK_INSERT;
stopKey = key.VK_DELETE;

SomeClass = class();

function SomeClass:test()
  self.name = 'Cow';
  printf("Hello I am "..self.name.."\n");
end

function main()
  local myClass = class(SomeClass);
  --myClass:test();
  registerTimer("test", secondsToTimer(1), myClass.test);

  while(true) do
    yrest(10);
  end
end
startMacro(main);

With the registerTimer line uncommented, it will throw an error about self being nil. Commenting out the registerTimer line, and uncommenting the line above it works just fine. Logically I don't understand why this would happen as they are both calling the same function.


Also I am using MicroMacro v0.98 and have noticed that after running versions of the same script many times (like making one change every time, and running the script afterwards), eventually it will seemingly cache that script at a certain point. When this happens you can run other scripts and it will still run the original one at the point that it was cached. Also changing the original script, after it seemed to cache it, will no longer have any effect. You can delete the entire contents of the script, save it, and it will still run in MicroMacro at the point it was cached. I'm using Ctrl + L to stop the scripts if that matters.

Re: registerTimer() - How to call a class function

Posted: Fri Nov 07, 2008 12:58 am
by zer0
Ahh I get what ur saying now. I had some code which produced the same effect so I had to have the timer call a function, that in turn calls the class function.

I think this is because using self:some_function(), is actually just syntactic sugar for my_class.some_function(self), and the registerTimer function does not allow the passing of arguments. Elverion can u comfirm if this is correct?? ;)
Is there a way to pass arguments to the function being timed in registerTimer()?

Re: registerTimer() - How to call a class function

Posted: Fri Nov 07, 2008 2:05 am
by Administrator
Ok, I looked into it. It seems the problem is because the function is not being called from the class itself. The 'best' way to go about resolving your problem here would be to do as follows:

Code: Select all

  registerTimer("test", secondsToTimer(1), function () myClass:func() end);
It simply creates a wrapping function to call instead of calling the class's function directly.

I'll look into rewriting the timer code (again) to see if I can get a simpler system that would allow for passing of arguments to the timed function.

Re: registerTimer() - How to call a class function

Posted: Fri Nov 07, 2008 2:32 am
by Sgraffite
Awesome, that works great, thanks!

Re: registerTimer() - How to call a class function

Posted: Fri Nov 07, 2008 3:12 am
by Administrator
I think this is because using self:some_function(), is actually just syntactic sugar for my_class.some_function(self), and the registerTimer function does not allow the passing of arguments. Elverion can u comfirm if this is correct?? ;)
Forgot to respond to this before. This is correct. Better than I could have stated, myself. As a work-around, you can pass 'self' into the function by yourself with the updated lib.lua. For example:

Code: Select all

Character = class();
function Character:init()
  self.HP = 100; -- initialize HP
end

function main()
  local myCharacter = class(Character);
  registerTimer("testing", secondsToTimer(1), myCharacter.init, myCharacter);
end
It's a silly example (why would you init more than once?), but you get the point. 'myCharacter' will be passed in as the hidden parameter 'self'. You can now pass unlimited* number of arguments to registerTimer, and they will all be sent to the resulting function call**.

Here's a better example:

Code: Select all

Character = class();
function Character:setName(name)
  self.name = name;
end

function Character:func(text)
  printf("Hello %s, my name is %s\n", text, self.name);
end


function main()
  local myClass = class(Character);
  myClass:setName("Bob");

  registerTimer("test", secondsToTimer(1), myClass.func, myClass, "Sally");

  while(true) do
    yrest(10);
  end
end
startMacro(main);


*Unlimited: at least until you reach a stack-limit. Nothing you need to worry about, though.
**Be aware that these values cannot be modified without re-calling registerTimer. For example:

Code: Select all

function timed(a)
  printf("A: %d\n", a);
end

function main()
  local num = 12;
  registerTimer("timed", secondsToTimer(1), timed, num);

  while(1) do
    yrest(100);
    num = num + 1;
  end
end
startMacro(main);
This will continue to print "A: 12" even though 'num' is constantly changing.