Cheat Engine Forum Index Cheat Engine
The Official Site of Cheat Engine
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 


How to increase performance of my lua code?

 
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting
View previous topic :: View next topic  
Author Message
dancewithdead
How do I cheat?
Reputation: 0

Joined: 29 Mar 2018
Posts: 1

PostPosted: Thu Mar 29, 2018 9:17 am    Post subject: How to increase performance of my lua code? Reply with quote

for loop with 4000 offsets is very slow..
how can i improve it?

what it do?
I want read data from many addresses and save in file.
Then read in other software - but i want fast updates

Code:
[ENABLE]
{$lua}
filename = "C:/data.txt"
base = 0x00AA2B60
timer = createTimer()
timer.setInterval(1000)
timer.onTimer = function()

    local f = io.open(filename, "w")
    f:write(tostring('return {\n'))
    offset = 0
    check = readInteger('[[[test.exe+base]+74]+0]+F4')
    isAlive = readInteger('[[[test.exe+base]+74]+0]+158')
     if check and check > 0 and check < 100000 then
        name = string.match(readString('[[[[[test.exe+base]+74]+0]+128]+0]+0'),'(%w+)%.')
        d1 = readFloat('[[[test.exe+base]+74]+0]+F8')
        d2 = readFloat('[[[test.exe+base]+74]+0]+FC')
        d3 = readFloat('[[[test.exe+base]+74]+0]+100')
        d4 = readInteger('[[[test.exe+base]+74]+0]+160')
     f:write(tostring('[1] = {name='..'"'..name..'"'..',d1='..d1..',d2='..d2..',d3='..d3..',d4='..d4..'},\n'))
     end
    for n=1,4000 do
        offset = offset + 4
        check = readInteger('[[[test.exe+base]+74]+offset]+F4')
        d4 = readInteger('[[[test.exe+base]+74]+offset]+160')
        isAlive = readInteger('[[[test.exe+base]+74]+offset]+158')
        name = readString('[[[[[test.exe+base]+74]+offset]+128]+0]+0')
        if name and name~="" and isAlive==2049 then
           if d4 and d4 < 32 then
              if  check > 0 and check < 100000 then
              name = string.match(name,'(%w+)%.')
              d1 = readFloat('[[[test.exe+base]+74]+offset]+F8')
              d2 = readFloat('[[[test.exe+base]+74]+offset]+FC')
              d3 = readFloat('[[[test.exe+base]+74]+offset]+100')
              d4 = readInteger('[[[test.exe+base]+74]+offset]+160')
              f:write(tostring('['..(n+1)..'] = {name='..'"'..name..'"'..',d1='..d1..',d2='..d2..',d3='..d3..',d4='..d4..'},\n'))
              end
           end
        end
        end
    f:write(tostring('}'))
    f:close()
 end

{$asm}
[DISABLE]

{$lua}
timer.destroy()


Is it possible write it in other language in cheatengine?
looks like readInteger, read"anything" in lua is slow
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 138

Joined: 06 Jul 2014
Posts: 4275

PostPosted: Thu Mar 29, 2018 11:48 am    Post subject: Reply with quote

When parsing the address string, CE will dereference every node in the pointer path. You're asking CE to do a ridiculous number of calls to ReadProcessMemory. Mitigate that and it'll run faster.

To mitigate calls to RPM, cache nodes in the pointer path (see this topic for an example), read blocks of memory at a time (e.g. d1/d2/d3 and the array of pointers), and check conditions as they're read.

For lower-level optimizations, use local variables and use table.concat instead of several sequential string concatenation operations (or just write it to the file and let Lua buffer it).

_________________
I don't know where I'm going, but I'll figure it out when I get there.
Back to top
View user's profile Send private message
panraven
Grandmaster Cheater
Reputation: 54

Joined: 01 Oct 2008
Posts: 941

PostPosted: Sat Mar 31, 2018 4:04 am    Post subject: Reply with quote

This is an alternative to cache the pointer node. usage save the script as *.lua into autorun directory, or include in an initializing memory record,
Code:

--
local fmt = string.format

local debugNow = nil--true

local function dp(...)
  if debugNow then print(...)end
end

local function call(fn,...)return fn(...)end
local function tApply(t)return call(table.unpack(t,1,t.n or #t))end

function wrapAccess(oriAccess, memoaddr, istable)
  return function(sAddr,...)
    if type(sAddr)=='table' then sAddr = tApply(sAddr) end
    local addr = type(sAddr)=='string' and (istable and memoaddr[sAddr] or not istable and memoaddr(sAddr))
    if not addr then dp('* * * raw getaddr',sAddr)addr = GetAddressSafe(sAddr)end
    if addr then return oriAccess(addr,...)  end
  end
end
local memoAddr_mt = {
  __index = function(me,s)
    local c,e,r,addr = 1,0,s
    if r:find"%b[]"then
      while c+e >0 and r:find"%b[]" do
        r,c = r:gsub("%[[^%[%]]+%]",function(ptr)-- search inner most of [...] form
          dp("==>",ptr)
          local pAddr = rawget(me,ptr) -- cached?
          if not pAddr then -- no, try readPointer/GetAddress and save as cache
            pAddr = readPointer(ptr:sub(2,-2)) --or GetAddressSafe(ptr)
            dp("* * * GetPtr:",ptr)
            if pAddr then rawset(me,ptr,pAddr) dp(fmt('new: %08X : %s',pAddr, ptr))end
          else
            dp("cached inner:",ptr)
          end
          if pAddr then return fmt("%X",pAddr)else e=e-1 end
        end)
      end--while
      r = (r..' '):gsub("%f[_%w](%x+)(%f[^_%w].?)",function(x, tl)return tonumber(x,16)..tl end)-- convert hex to decimal
      local ok,eval = pcall(load,'return '..r ) -- ... and eval it as numeric address
      if ok then ok,eval = pcall(eval)end
      if ok and eval then addr = eval end
    end
    if not addr then -- fall back to parse original symbolic address
      addr = GetAddressSafe(s) dp("* * * GetAddr:",s)
    else
      dp("cached:",s)
    end
    if addr then rawset(me,s,addr)end
    return addr
  end,
  __call = function(me,s)return me[s]end
}
function memoAddr()  return setmetatable({},memoAddr_mt)end

then for example in your code, in each timer loop add this code:
Code:

...
timer.onTimer = function()

  local commonCache = memoAddr()
  local readInteger = wrapAccess(readInteger, commonCache)
  local readFloat = wrapAccess(readFloat, commonCache)
  local readString = wrapAccess(readString, commonCache)

    local f = io.open(filename, "w")
...

Then each part of the symbolic address used by readInter/readFloat/readString (local version) of the form '[...]' will be cached after 1st time encountered.
It should minimize CE internal parsing that require RPM, with minimal change of your code.

---

I think it will be more manageable for future maintenance if you refactor pointer node manually as ParkourPenguin suggestion.

---

It seems there is some bug on 'offset'.

Code:

...
    for n=1,4000 do
        offset = offset + 4   -- this 'offset' is a Lua variable
        check = readInteger('[[[test.exe+base]+74]+offset]+F4') -- this 'offset' is an AA symbol, this will not changed with the Lua 'offset' given it is already defined as AA symbol.
...

may be something like this can fix:
Code:

...
    for n=1,4000 do
        offset = offset + 4   
        local partial = string.format("[[[test.exe+base]+74]+%X]", offset)
        check = readInteger(partial..'+F4')
-- and replace other "[[[test.exe+base]+74]+offset]" with partial
... 


There is also an optimizing opportunity for the 4000x offset parts.
Instead of reading each offset by RPM, we can read all 4000x4bytes outside the loop as a byteTable.
Then extract each pointer of the form "[[[test.exe+base]+74]+offset]" locally.
ie.
Code:

...
    local Offset = readBytes("[[test.exe+base]+74]",4000*4,true)
    for n=1,4000 do
        local offset = string.char(table.unpack(Offset, n*4-3, n*4))
        local partial = string.format("%X", string.unpack("<i4",offset))-- i4 for 32bit
        check = readInteger(partial..'+F4')
...

Not test, hopeful there is no typo or bug on my code.
bye~

_________________
- Retarded.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Cheat Engine Forum Index -> Cheat Engine Lua Scripting All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Powered by phpBB © 2001, 2005 phpBB Group

CE Wiki   IRC (#CEF)   Twitter
Third party websites