Jump to content


Photo

Quick lua question...


23 replies to this topic

#1 momoka

momoka

    Newbie

  • Members
  • Pip
  • 6 posts

    Posted 16 August 2015 - 05:11 PM

    Hiya! Sorry I am new to the forums and coding but not to xi. I'm actually having fun learning lua and testing things out on my whm!

     

    Can someone please explain to me how to make a function loop? I've created a bind with ctrl+d that I'd like to use to stop and break the loop. I tried doing a more simple method by increasing a value x times, in increments of 1, but couldn't get that to work either. I'd prefer the bind method with a break. I've looked up tutorials on how to do loops in lua but it's honestly a lil confusing T_T

     

    Ty for the help!



    #2 Iryoku

    Iryoku

      Advanced Member

    • Windower Staff
    • 488 posts

      Posted 16 August 2015 - 09:21 PM

      There are a few options for "looping". Which choice is right depends on what you are trying to achieve.
       
      The simplest way to loop is to just use Lua's built-in flow control structures: for, while, and repeat...until. A for loop should generally be preferred if the number of loop iterations is known in advance (i.e. loop from 1 to 100, loop though all keys in a table, etc.). A while loop is prefered in cases where the number of iterations may not be known in advance, or may be infinite (loop until a key is pressed, loop until a socket is closed, etc.). A repeat...until loop is like a while loop, but the condition is checked after the loop body, and the loop body always executes at least once. This form makes certain types of algorithms simpler, but is rarely used.
       
      One thing you will quickly realize is that if you have a long-running loop you will block the game; everything will freeze until the loop completes. The solution to call coroutine.yield() periodically. This function tells the Windower scheduler handler that you would like to give up your current execution slice; that is, it's ok for it to let other code run, but you have more work to do so it should schedule you to run again as soon as possible.
       
      Note: There is one important caveat. Events treat coroutine.yield() (and its cousin coroutine.sleep()) as a return, if you need to return any values from an event, you must pass those as arguments to the first call to coroutine.yield() in the event handler.
       
      Another way you can achieve looping is by using the Windower scheduler itself. You can manually schedule a function to run at some point in the future using coroutine.schedule().

      function do_stuff()
          -- some stuff you want to do periodically
          if should_keep_running then
              coroutine.schedule(do_stuff, 10)
          end
      end
      
      -- start
      should_keep_running = true
      coroutine.schedule(do_stuff)
      
      -- stop
      should_keep_running = false

      This will run do_stuff() every 10 seconds until stoppped.

      Another option is to use the functions lib, which is built using the primitives I've already descussed.

      require('functions')
      
      function do_stuff()
          -- some stuff you want to do periodically
      end
      
      -- start
      should_keep_running = true
      functions.loop(do_stuff, 10, function() return should_keep_running end)
      
      --stop
      should_keep_running = false

      There are even a few other constructions that are possible (using coroutine.close() to stop the loop for instance), but this should be enough to get you started.


      • Arcon likes this

      #3 sdahlka

      sdahlka

        Advanced Member

      • Members
      • PipPipPip
      • 324 posts

        Posted 17 August 2015 - 05:01 AM

        i would like to say one thing about looping in lua (this is from my experience) with windower

        this takes about 0.03 seconds

         

        local a = 0
        while a<10
         a=a+1
        end
         

         

         

        this will take about 10 seconds

         

        local a = 0
        while a<10000
         a=a+1
        end
         

        and both will lock the game till thay complete and might even cause a memory over flow and crash the game or your comp

        so make sure that you do not use something like this as it will likely lock you out of being able to push any buttons

         

        repeat
              ---anything
        until--button press
         

        the only loop i recommend is the for do end loop and even this can cause ffxi to stall but because it works on tables its no ware as bad

         

        here are 2 examples

         

        function sleep(sec)
            local ttime = os.clock()
            while (ttime+sec)>os.clock() do
               --ffxi locked
            end
        end
        sleep(10)--sleep for 10 seconds
        --or
        function sleep(sec)
            local ttime = os.clock()
            repeat
               --ffxi locked
            until (ttime+sec) =< os.clock()
        end
        sleep(10)--sleep for 10 seconds
         
        

        no mater which you chose they both will lock ffxi for 10 seconds



        #4 Iryoku

        Iryoku

          Advanced Member

        • Windower Staff
        • 488 posts

          Posted 17 August 2015 - 06:11 AM

          Fundamentally Lua's built-in loop constructs are all the same; they all boil down to a conditional jump at the machine code level. In fact, all of the loops can be constructed as composites of if...then and repeat...until statements. For instance,

          while condition do
              do_stuff()
          end

          is exactly the same as

          if condition then
              repeat
                  do_stuff()
              until not condition
          end

          and a for loop can be similarly deconstructed.

          for k, v in pairs(t) do
              print(k, v)
          end

          can be deconstructed into

          do
              local iter = pairs(t)
              local k, v = iter()
              if k ~= nil then
                  repeat
                      print(k, v)
                      k, v = iter()
                  until k == nil
              end
          end

          Similarly,

          for i = s, e, inc do
              print(i)
          end

          can be deconstructed as

          do
              local i = s
              local _e = e
              local _inc = inc
              local _done
              if _inc > 0 then
                  _done = i >= _e
              else
                  _done = i <= _e
              end
              if not _done then
                  repeat
                      print(i)
                      i = i + _inc
                      if _inc > 0 then
                          _done = i >= _e
                      else
                          _done = i <= _e
                      end
                  until _done
              end
          end

          These are the kinds of deconstructions that are done by the compiler automatically at runtime, so they really are all built from the same fundamental building blocks. The different loops are just there to provide syntactic convenience. They will all block the game.



          #5 sdahlka

          sdahlka

            Advanced Member

          • Members
          • PipPipPip
          • 324 posts

            Posted 17 August 2015 - 07:31 AM

            @Iryoku

            actualy i think the actual raw for

             

            for k, v in pairs(t) do
                print(k, v)
            end
             

            is

             

            do
                local iter = pairs(t)
                local k, v = iter()
                ::a::
                if k ~= nil then
                    print(k, v)
                    k, v = iter()
                    goto a
                end
            end
             


            #6 Iryoku

            Iryoku

              Advanced Member

            • Windower Staff
            • 488 posts

              Posted 17 August 2015 - 04:18 PM

              Well, the reality is that all of the loops are compiled into bytecode (or machine code in the case of LuaJIT) using conditional jumps (essentially a conditional goto), the only difference between what I gave and what really happens is there's an extra conditional jump, which increases the code size slightly, but has nearly zero performance cost, in fact LuaJIT can optimize away the extra jump.

               

              My point wasn't to show the exact deconstructions that Lua uses, but to show that all of the loops are basically equivalent, but layer on extra syntactic convenience, and hence the performance characteristics of all of them are basically the same.

               

              By the way, this is true in nearly all languages, a fact that optimizing compilers make great use of.



              #7 Whiipyhot

              Whiipyhot

                Newbie

              • Members
              • Pip
              • 3 posts

                Posted 18 August 2015 - 06:58 AM

                Hi, I think I have similar issue to momoka.  I am trying to repeat  input /item "Silt Pouch" <me> as my function but cant seem to get it to repeat.



                #8 sdahlka

                sdahlka

                  Advanced Member

                • Members
                • PipPipPip
                • 324 posts

                  Posted 18 August 2015 - 02:21 PM

                  Hi, I think I have similar issue to momoka.  I am trying to repeat  input /item "Silt Pouch" <me> as my function but cant seem to get it to repeat.

                  here is a quick example of how to do it(will crash gearswap)

                  and it will never stop

                  ::a::
                  windower.send_command('input /item "Silt Pouch" <me>')
                  coroutine.sleep(1)
                  goto a
                   


                  #9 Iryoku

                  Iryoku

                    Advanced Member

                  • Windower Staff
                  • 488 posts

                    Posted 18 August 2015 - 10:27 PM

                    Don't use a goto for this... goto is evil*! In fact, I don't think Lua 5.1 even supports goto; it was added in Lua 5.2. This should work, instead.

                    while true do
                        windower.send_command('input /item "Silt Pouch" <me>')
                        coroutine.sleep(1)
                    end
                    

                    * Of course, goto has legitimate uses, but they are few and far between. Unless a goto adds a great amount of clarity or simplifies the code, you should avoid using it.



                    #10 Whiipyhot

                    Whiipyhot

                      Newbie

                    • Members
                    • Pip
                    • 3 posts

                      Posted 19 August 2015 - 02:32 AM

                      This is what I put together, how can I get this to work? think I did it right but maybe not
                       
                       silt = 'OFF' 
                           windower.send_command('unbind ^x') 
                           windower.send_command('unbind !x') 
                           windower.send_command('bind ^x lua c silt S1') 
                           windower.send_command('bind !x lua c silt S2') 
                           windower.send_command('alias sp lua c silt')      
                            
                           function self_command(command) 
                                if command == 'S1' then 
                                     if silt == 'OFF' 
                                     silt = 'ON' 
                                elseif command == 'S2' then 
                                     if silt == 'ON' 
                                     silt = 'OFF' 
                                end 
                           end 
                            
                           local function freemyinv() 
                                while silt = 'ON' do 
                                windower.send_command('input /item "Silt Pouch" <me>') 
                                coroutine.sleep(1) 
                           end
                       
                      says I have a string error, but I'm not sure where


                      #11 sdahlka

                      sdahlka

                        Advanced Member

                      • Members
                      • PipPipPip
                      • 324 posts

                        Posted 19 August 2015 - 02:26 PM

                        This is what I put together, how can I get this to work? think I did it right but maybe not
                         
                         silt = 'OFF' 
                             windower.send_command('unbind ^x') 
                             windower.send_command('unbind !x') 
                             windower.send_command('bind ^x lua c silt S1') 
                             windower.send_command('bind !x lua c silt S2') 
                             windower.send_command('alias sp lua c silt')      
                              
                             function self_command(command) 
                                  if command == 'S1' then 
                                       if silt == 'OFF' 
                                       silt = 'ON' 
                                  elseif command == 'S2' then 
                                       if silt == 'ON' 
                                       silt = 'OFF' 
                                  end 
                             end 
                              
                             local function freemyinv() 
                                  while silt = 'ON' do 
                                  windower.send_command('input /item "Silt Pouch" <me>') 
                                  coroutine.sleep(1) 
                             end
                         
                        says I have a string error, but I'm not sure where

                        @Iryoku
                        yes my bad it was added in 5.2.0-beta-rc1
                         
                        its because of several missing ends and you can not use coroutine.sleep(time) in gearswap and if your not doing it in gearswap you have your functions wrong
                        i fixed the ends below
                         
                            silt = 'OFF'
                            windower.send_command('unbind ^x')
                            windower.send_command('unbind !x')
                            windower.send_command('bind ^x lua c silt S1')
                            windower.send_command('bind !x lua c silt S2')
                            windower.send_command('alias sp lua c silt')      
                        
                        function self_command(command) 
                            if command == 'S1' then
                                if silt == 'OFF'
                                    silt = 'ON'
                                end
                            elseif command == 'S2' then
                                if silt == 'ON'
                                    silt = 'OFF'
                                end
                            end
                        
                            local function freemyinv()
                                while silt = 'ON' do
                                    windower.send_command('input /item "Silt Pouch" <me>')
                                    coroutine.sleep(1)
                                end
                            end
                        end
                         
                        here is a version thats should be just a little better as well as ready for its own lua
                         
                        silt = false
                        windower.send_command('unbind ^x')
                        windower.send_command('bind ^x lua silt Toggle')
                        windower.send_command('alias sp lua silt')
                        
                        function freemyinv()
                            while silt do
                                windower.send_command('input /item "Silt Pouch" <me>')
                                coroutine.sleep(1)
                            end
                        end
                            
                        windower.register_event('addon command', function(command)
                            if command == 'Toggle' then
                                silt = not silt
                                windower.add_to_chat(7, "Auto Silt "..(silt and "Enabled" or "Disabled"))
                                if silt then
                                    freemyinv()
                                end
                            end
                        end)
                         


                        #12 Whiipyhot

                        Whiipyhot

                          Newbie

                        • Members
                        • Pip
                        • 3 posts

                          Posted 20 August 2015 - 12:58 AM

                          Thanks so much! Its working now! :)  My next question now is Id like it to auto stop on its own when it finishes using the pouches but Im not sure the best way to go about it. This is what I have so far but it doesnt seem to stop it unless I hit the bind again.

                           

                          require('sets') 
                           
                          stop_filter = S{ 
                              '>> /item "Silt Pouch" <me>', 
                          windower.register_event('incoming text', function(text) 
                              if stop_filter:contains(text) then 
                                  windower.send_command('lua c silt Toggle') 
                              end 
                          end)


                          #13 momoka

                          momoka

                            Newbie

                          • Members
                          • Pip
                          • 6 posts

                            Posted 20 August 2015 - 02:38 AM

                            Thank you for all the wonderful help Iryoku and sdahlka. I haven't been around to reply sooner else I would have. I've managed to get this down now after reading all of your examples!

                             

                            I currently have my whm now set to spam cursna on my trust npc. Then quickly stop with a keybind which is truly a lifesaver. It's simple but this significantly reduces a lot of problems for me while I also try to play BRD. I have to manually target one of the trusts at the moment. If I use input /targetnpc of course it will randomly select the one closest to me. I'm wondering how to check for a set of names before being allowed to proceed.

                             

                            ie;

                            Valaineral, Trion. 

                             

                            I've found the following options that may be possible to use. However, I've found no real example of how to get this to work.

                             

                            windower.ffxi.get_mob_by_id(id) with is_npc

                            and

                            SelectNPCTargets in Mote's Utility lua

                             

                            I'd like to somehow check that the /target name matches a string of name(s) before being allowed to cast cursna. I'm not sure if this is possible. I prefer to avoid packets too. I will keep digging around in the meantime and if I figure it out I'll make a post. I really appreciate all the help and I'm having a lot of fun learning more about lua, it's so incredibly useful! :)



                            #14 sdahlka

                            sdahlka

                              Advanced Member

                            • Members
                            • PipPipPip
                            • 324 posts

                              Posted 20 August 2015 - 04:07 AM

                              Thanks so much! Its working now! :)  My next question now is Id like it to auto stop on its own when it finishes using the pouches but Im not sure the best way to go about it. This is what I have so far but it doesnt seem to stop it unless I hit the bind again.
                               
                              require('sets') 
                               
                              stop_filter = S{ 
                                  '>> /item "Silt Pouch" <me>', 

                              windower.register_event('incoming text', function(text) 
                                  if stop_filter:contains(text) then 
                                      windower.send_command('lua c silt Toggle') 
                                  end 
                              end)

                              something like this
                               
                              items = windower.ffxi.get_items("inventory")
                              silt = false
                              windower.send_command('unbind ^x')
                              windower.send_command('bind ^x lua silt Toggle')
                              windower.send_command('alias sp lua silt')
                              
                              function freemyinv()
                                  while silt do
                                      windower.send_command('input /item "Silt Pouch" <me>')
                                      coroutine.sleep(1)
                                      silt = false
                                      for _,v in ipairs(items) do
                                          if v.id == 6391 then
                                              silt = true
                                          end
                                      end
                                  end
                              end
                                  
                              windower.register_event('addon command', function(command)
                                  if command == 'Toggle' then
                                      silt = not silt
                                      windower.add_to_chat(7, "Auto Silt "..(silt and "Enabled" or "Disabled"))
                                      if silt then
                                          freemyinv()
                                      end
                                  end
                              end)
                               

                              Thank you for all the wonderful help Iryoku and sdahlka. I haven't been around to reply sooner else I would have. I've managed to get this down now after reading all of your examples!

                              I currently have my whm now set to spam cursna on my trust npc. Then quickly stop with a keybind which is truly a lifesaver. It's simple but this significantly reduces a lot of problems for me while I also try to play BRD. I have to manually target one of the trusts at the moment. If I use input /targetnpc of course it will randomly select the one closest to me. I'm wondering how to check for a set of names before being allowed to proceed.

                              ie;
                              Valaineral, Trion.

                              I've found the following options that may be possible to use. However, I've found no real example of how to get this to work.

                              windower.ffxi.get_mob_by_id(id) with is_npc
                              and
                              SelectNPCTargets in Mote's Utility lua

                              I'd like to somehow check that the /target name matches a string of name(s) before being allowed to cast cursna. I'm not sure if this is possible. I prefer to avoid packets too. I will keep digging around in the meantime and if I figure it out I'll make a post. I really appreciate all the help and I'm having a lot of fun learning more about lua, it's so incredibly useful! :)

                              like this
                              windower.ffxi.get_mob_by_target("t").name == <the name you want>

                              #15 momoka

                              momoka

                                Newbie

                              • Members
                              • Pip
                              • 6 posts

                                Posted 02 September 2015 - 05:04 PM

                                Hmm tried that but can't get the target by name to work still, if doing the name Trion or PlayerX it will just do nothing.



                                #16 sdahlka

                                sdahlka

                                  Advanced Member

                                • Members
                                • PipPipPip
                                • 324 posts

                                  Posted 03 September 2015 - 03:31 AM

                                  are you trying to set the target
                                  if so there is a way but its a drawn out process because windower has no direct way to set target

                                  #17 momoka

                                  momoka

                                    Newbie

                                  • Members
                                  • Pip
                                  • 6 posts

                                    Posted 03 September 2015 - 06:41 AM

                                    Yes, basically something like an accepted list of names (the tank or bard in party). I am forced to usually play both PLD and RDM or BRD. I'm able to multi-box decent enough and I do not want to bot I just want to make some things a little easier each time I kill an enemy. It's easy for me to accidentally target something else by accident every time I switch screens since I'm in a hurry! As a RDM or BRD I have no macro space and no time so assigning this kind of thing really helps me out a lot. So let me try to break this down...

                                     

                                    1. I am trying to make a list of names acceptable to target

                                    2. It will target either one of the names, then perform two actions

                                    3. If the selected target is neither of those then it will just stop. That way it doesn't accidentally begin to follow an enemy!

                                     

                                    I realize this is probably painful to look at but something like this... where "whitelist" is the accepted names I want it to always focus on. I realize the syntax is incorrect but I hope the idea is clear that it's trying to target one of the names in whitelist. I'm just coming up with an idea based on what I've been able to learn and play with from this thread so far hehe :)

                                    assist = false
                                    
                                    windower.send_command('unbind ^a')
                                    windower.send_command('bind ^a lua c assist Toggle')
                                    windower.send_command('alias sp lua c assist')
                                    
                                    whitelist = L{'PlayerName1','PlayerName2'}
                                    
                                    function gogo()
                                    	windower.send_command(';input /target .."whitelist"..;)
                                    	if player.target.type == 'PLAYER' and
                                    	if windower.ffxi.get_mob_by_target("t").name == 'whitelist' then
                                    		while assist do
                                    		windower.send_command(';input /ma "Phalanx II" '..whitelist..';pause 3;/follow '..whitelist..';)
                                    	elseif player.target.type == 'MONSTER' then
                                    		assist = not assist
                                    		return
                                        end
                                    end
                                    
                                    windower.register_event('addon command', function(command)
                                        if command == 'Toggle' then
                                            assist = not assist
                                            if assist then
                                                gogo()
                                            end
                                        end
                                    end)
                                    
                                     
                                    I hope I explained this clearly >_< I really appreciate the help and this has been a fun learning experience already. I'm getting the gist of things and wish there were a lot more examples out there to go off of


                                    #18 sdahlka

                                    sdahlka

                                      Advanced Member

                                    • Members
                                    • PipPipPip
                                    • 324 posts

                                      Posted 04 September 2015 - 05:14 AM

                                      try this but you have to put in your target selection name with 'lua assist Toggle "<playername>"' if you do it a second time it will stop
                                       

                                      assist = false
                                      
                                      whitelist = {'PlayerName1','PlayerName2'}
                                      
                                      function gogo(name)
                                          windower.send_command('input /target "'..name..'"')
                                          if assist then
                                              if whitelist:contains(name) then
                                                  while assist do
                                                      windower.send_command('input /ma "Phalanx II" '..name..';pause 3;/follow "'..name..'"')
                                                  end
                                              else
                                                  assist = not assist
                                              end
                                          end
                                      end
                                      
                                      windower.register_event('addon command', function(command)
                                          local comArgs = T(command:split(' '))
                                          if comArgs[1] == 'Toggle' then
                                              assist = not assist
                                              if assist and
                                                  if comArgs[2] then
                                                      gogo(comArgs[2])
                                                  else
                                                      print("You forgot to name your target")
                                                  end
                                              end
                                          end
                                      end)

                                      this code takes about 0.3 second to process

                                      but windower.send_command('input /ma "Phalanx II" '..name..';pause 3;/follow "'..name..'"') will run about 300 times a second so it will cause windower to stall



                                      #19 momoka

                                      momoka

                                        Newbie

                                      • Members
                                      • Pip
                                      • 6 posts

                                        Posted 07 December 2015 - 06:28 PM

                                        Thank you all again for taking the time to write all this code out and explain everything



                                        #20 alk

                                        alk

                                          Newbie

                                        • Members
                                        • Pip
                                        • 3 posts

                                          Posted 29 May 2016 - 12:51 PM

                                          here is a version thats should be just a little better as well as ready for its own lua
                                           

                                          silt = false
                                          windower.send_command('unbind ^x')
                                          windower.send_command('bind ^x lua silt Toggle')
                                          windower.send_command('alias sp lua silt')
                                          
                                          function freemyinv()
                                              while silt do
                                                  windower.send_command('input /item "Silt Pouch" <me>')
                                                  coroutine.sleep(1)
                                              end
                                          end
                                              
                                          windower.register_event('addon command', function(command)
                                              if command == 'Toggle' then
                                                  silt = not silt
                                                  windower.add_to_chat(7, "Auto Silt "..(silt and "Enabled" or "Disabled"))
                                                  if silt then
                                                      freemyinv()
                                                  end
                                              end
                                          end)
                                           

                                           

                                          Sorry for the necro post. I tried saving this as a lua and load it but I can't get the toggle to work. Am I doing something wrong or has there been changes making this code invalid?






                                          1 user(s) are reading this topic

                                          0 members, 1 guests, 0 anonymous users