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 


Any suggestions to make this GUI code more efficient?

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

Joined: 25 Jul 2021
Posts: 9
Location: Earth

PostPosted: Tue Jul 11, 2023 12:38 am    Post subject: Any suggestions to make this GUI code more efficient? Reply with quote

I have a Lua script that controls two forms containing GUI elements, it gets called every frame at 60 FPS.
Here is a part of it, for clarity (the whole thing's huge--534 lines in total):
Code:

if healthLayer >= damageLayer then
 HealingTopBarVisible = true
 HealthBar.HealingBottom.Visible = true
 HealthBar.HealingBottom.Width = 800
elseif healthLayer == damageLayer - 1 then
 HealingTopBarVisible = false
 HealthBar.HealingBottom.Visible = true
 HealthBar.HealingBottom.Width = 4*healthTop
else
 HealingTopBarVisible = false
 HealthBar.HealingBottom.Visible = false
end

for selection,bar in ipairs(HealingTopBars) do
 if selection == HealingTopBar and healthLayer > 0 then
  bar.Visible = true and HealingTopBarVisible
  bar.Width = 4*healthTop
 else
  bar.Visible = false
 end
end
--------------------------------------------------------------------------
for layer,indicator in ipairs(HPLayerIndicators) do
 if damageLayer > layer then
  indicator.Visible = true
  if layer > 1 then
   firstOffset = 2
  else
   firstOffset = 1
  end
  if healingLayer > layer then
   secondOffset = 3
   if dangerHealthLayer >= layer then
    thirdOffset = 0
   elseif warningHealthLayer >= layer then
    thirdOffset = 1
   else
    thirdOffset = 2
   end
  else
   if healthLayer > layer then
    secondOffset = 2
   else
    secondOffset = 1
   end
   thirdOffset = 0
  end
  indicator.Picture = HPLayerColors[firstOffset][secondOffset+thirdOffset]
 else
  indicator.Visible = false
 end
end

(the full code is attached below)
The code uses 20% of my CPU on average, and I want to lower that.
Any way how to do this?[/code]



SonicGUI.lua
 Description:
534 lines long

Download
 Filename:  SonicGUI.lua
 Filesize:  15.44 KB
 Downloaded:  226 Time(s)


_________________
A health bar is useless if it cannot protect its owner from death
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4709

PostPosted: Tue Jul 11, 2023 10:51 am    Post subject: Reply with quote

That script has a bunch of syntax errors with both superfluous and missing ending parenthesis.

Some code is completely missing. e.g. what's `checkFPS`?

That's one small snippet from a ridiculously long function. You really need to refactor it.

You're only using global variables in that function?? The `local` keyword exists and should really be the default.

`al["whatever"]` - indexing into the address list every time is unnecessary. Get the memory records once and reuse them.

I'm not sure what happens when assigning to various properties of GUI elements, but reassigning the same value is unnecessary. Remember what was assigned, and change it only if necessary.

Are you sure you want to update that at 60 fps? Timers typically have trouble going that high, and it's not like all the values exist in your process. Every individual call to ReadProcessMemory (i.e. indexing MemoryRecord.NumericalValue) is a significant performance hit.
If you really want better performance, do a bunch of code injections in the target process to write important values to some predetermined area of memory, and use a single call to ReadProcessMemory to get that (e.g. call `copyMemory` into a MemoryStream, then read values).

_________________
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
S-nonymous027
How do I cheat?
Reputation: 0

Joined: 25 Jul 2021
Posts: 9
Location: Earth

PostPosted: Tue Jul 11, 2023 8:32 pm    Post subject: Reply with quote

ParkourPenguin wrote:
That script has a bunch of syntax errors with both superfluous and missing ending parenthesis.

Some code is completely missing. e.g. what's `checkFPS`?

That's one small snippet from a ridiculously long function. You really need to refactor it.

You're only using global variables in that function?? The `local` keyword exists and should really be the default.

`al["whatever"]` - indexing into the address list every time is unnecessary. Get the memory records once and reuse them.

I'm not sure what happens when assigning to various properties of GUI elements, but reassigning the same value is unnecessary. Remember what was assigned, and change it only if necessary.

Are you sure you want to update that at 60 fps? Timers typically have trouble going that high, and it's not like all the values exist in your process. Every individual call to ReadProcessMemory (i.e. indexing MemoryRecord.NumericalValue) is a significant performance hit.
If you really want better performance, do a bunch of code injections in the target process to write important values to some predetermined area of memory, and use a single call to ReadProcessMemory to get that (e.g. call `copyMemory` into a MemoryStream, then read values).


Whoopsie, seems like I've uploaded the wrong one then... that one must've been my prototype (the one I'm using has none of them syntax errors).

All the values does exist within the (edit 2: game's) process, save for calculated variables like "speed" and "eta" (est. time of arrival)
60 fps is necessary since I want the forms to display values in real time.
As for the memoryrecord access and local variables, I'm working on it.
I also found a bug that wrongly colors the bottom layer of the health bar.

Edit: Is this what you mean by remembering the assigned values?
Code:

address = 0x01234567
oldValue = 0

function displayValue()
 local value = readInteger(0x01234567)
 
 if value ~= oldValue then
  --do something
  oldValue = value
 end
end

_________________
A health bar is useless if it cannot protect its owner from death


Last edited by S-nonymous027 on Tue Jul 11, 2023 11:02 pm; edited 1 time in total
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4709

PostPosted: Tue Jul 11, 2023 10:53 pm    Post subject: Reply with quote

S-nonymous027 wrote:
All the values does exist within the process...
There are two separate processes involved here: CE and the game. Every time you index `MemoryRecord.NumericalValue`, CE must call ReadProcessMemory to read the value in the game's process. The time required to do this isn't negligible when you're doing it several dozen times per second.

Yes, that's more or less what I mean by remembering the assigned value. Importantly, you should only modify the GUI when something has actually changed. e.g. if `indicator.Picture` is currently `picture1`, and the next loop `indicator.Picture` should still be `picture1`, there's no need to assign anything to `indicator.Picture`: just leave it alone. Doing nothing is always the fastest thing you can possibly do.

_________________
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
S-nonymous027
How do I cheat?
Reputation: 0

Joined: 25 Jul 2021
Posts: 9
Location: Earth

PostPosted: Tue Jul 11, 2023 10:58 pm    Post subject: Reply with quote

The only thing left for me to do now is figuring out how to read all the values in one call of ReadProcessMemory. I never did that before, any clues on how to do it?
_________________
A health bar is useless if it cannot protect its owner from death
Back to top
View user's profile Send private message
ParkourPenguin
I post too much
Reputation: 152

Joined: 06 Jul 2014
Posts: 4709

PostPosted: Wed Jul 12, 2023 12:14 am    Post subject: Reply with quote

It'll take some effort. Make sure ReadProcessMemory is actually a bottleneck. Try replacing everything that calls ReadProcessMemory (i.e. every index using `NumericalValue`) with a sensible random number. If there are no calls to ReadProcessMemory and it doesn't help performance at all, then it's probably the GUI stuff that's expensive and the following would be a waste of time.

Separate this problem into two parts: in the target process, copy values into some common area of memory. In CE's process, read from this common area of memory and assign it to values.

The first part largely depends on how the address list reads values. More specifically, in the target process, you want to read the values the same way as CE does in the address list. If it's a pointer, traverse the pointer path; if it's an injection copy, use the registered symbol as a base address.
I guess the most general way is to create a thread in the target process, but you could also use a code injection (or several). Just make sure the code injection(s) are being run at at least 60 Hz.
Code:
[ENABLE]
globalalloc(ValuePool,4096)
label(value1)
label(value2)
label(value3)
label(valCopyTerminate)
label(code)
label(loop)

registersymbol(valCopyTerminate)

ValuePool:
value1:
  resd 1  // 4-byte
value2:
  resd 1  // float
value3:
  resq 1  // double

valCopyTerminate:
  dd 0

db CC
code:
  push rbp
  mov rbp,rsp
  sub rsp,20
loop:
{$try}
  // note: can't directly use `mov rdx,[game.exe+1234]` (RIP-relative addressing)
  mov rdx,game.exe+1234
  mov rdx,[rdx]
  mov rdx,[rdx+53C]  // 1st offset 53C
  mov eax,[rdx+3C]  // 2nd offset 3C
  mov [value1],eax  // 4-byte value

  mov eax,[rdx+78]  // 2nd offset 78
  mov [value2],eax  // float value (floats are 4 bytes)

// registered symbol (again watch for RIP-relative addressing):
  mov rdx,PlayerAddr
  mov rdx,[rdx]  // might want to check if `rdx` is 0 before the next instruction
  mov rax,[rdx+40]  // original instruction might be `movsd xmm0,[rdx+40]`, and `PlayerAddr` holds rdx
  mov [value3],rax  // double value (doubles are 8 bytes)
{$except}
  mov ecx,#15
  call Sleep
  mov ecx,[valCopyTerminate]
  test ecx,ecx
  jz loop
// exit:
  mov rsp,rbp
  pop rbp
  ret

createthread(code)

[DISABLE]
valCopyTerminate:
  dd 1
Edit: RSI is a nonvolatile register; use RDX instead

The second part is a little easier if you're familiar with CE's Lua API. Something like this:
Code:
// allocate enough memory to hold all the values (4+4+8=16)
local buffer = createMemoryStream()
buffer.Size = 16

// read from other process's memory into this process
local addr = getAddressSafe'ValuePool'
if not addr then return end  // only run if the memory exists

copyMemory(addr, buffer.Size, buffer.Memory, 1)

// start reading values. 4-byte value:
local value1 = buffer.readDword()

// unfortunately, there is no "readFloat"; workaround:
local value2 = byteTableToFloat(buffer.read(4))

// same for double
local value3 = byteTableToDouble(buffer.read(8))

buffer.destroy()
buffer = nil
See celua.txt for documentation on CE's Lua API. The wiki might be helpful too.
_________________
I don't know where I'm going, but I'll figure it out when I get there.


Last edited by ParkourPenguin on Wed Jul 12, 2023 12:36 pm; edited 1 time in total
Back to top
View user's profile Send private message
S-nonymous027
How do I cheat?
Reputation: 0

Joined: 25 Jul 2021
Posts: 9
Location: Earth

PostPosted: Wed Jul 12, 2023 2:50 am    Post subject: Reply with quote

After I fixed both ReadProcessMemory and GUI code, it now runs at 15% CPU on average and it's much smoother than before.

Turns out both were the culprit.

Thanks a lot!

Edit:
Someone should make a tutorial based on this. It's kinda irritating not to know how much load each function you call puts on your CPU.



GUI works.png
 Description:
Behold the entirety of the GUI!

Green = HP (healed portion shown in celeste/light blue)
Blue gradient = energy, consumed by boosting
Light blue = speed
Marked bar (lowermost) = progress
 Filesize:  724.53 KB
 Viewed:  1932 Time(s)

GUI works.png



_________________
A health bar is useless if it cannot protect its owner from death
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