Fix lag, FPS drops and crashes caused by obfuscation using LPH_NO_VIRTUALIZE
Luarmor uses Luraph as its obfuscation provider, so if you’re experiencing lag, FPS drops or even crashes, you need to use certain macros in your script. This page explains how to use the LPH_NO_VIRTUALIZE macro.
If your code runs fine unobfuscated but lags or crashes once obfuscated, this page explains why and how to fix it.
Luraph is a VM-based obfuscator: it turns your code into its own custom instructions, which are run by Luraph’s VM, which in turn is run by the Lua interpreter. This greatly increases the number of instruction cycles.
How obfuscation works
Normally, your script gets compiled into a set of Lua instructions. See the example below:
As you can see, your simple print statement gets compiled into 4 instructions. Each instruction is a cycle, so it will take 4 cycles to execute your code.When you obfuscate your script, the main goal is to make the original code unreadable. Luraph does this by generating its own custom instruction set that only the Luraph VM can understand.See the example below:
How Luraph obfuscates Lua code
Notice how your simple print statement turned into 40 instructions, which will take 40 cycles to execute. Also keep in mind that Luraph does a lot of things to obfuscate the code flow, which will ultimately lead to even more instructions.
Normally, Lua is extremely fast, so you will not notice any performance impact. But when your code runs hundreds of times per second, there will be obvious lag, FPS drops or even freezes while those instructions run.A good example is RenderStepped connections, which scripts often use to run their wallhack rendering every frame.
RenderStepped example
Example of a RenderStepped connection
This code will be slow when obfuscated, because the number of instructions generated by Luraph will be more than 2000. This means Lua will have to run 2000+ instruction cycles every frame, which is around 60 * 2000 = 120k instructions every second.This loop also calls local functions (e.g. CalculateParameters), which run every frame as well, adding even more instructions.
Here is a second example, a metamethod hook on __index:
__index example
Example of a metamethod hook
__index runs very often. For example, each of these invokes it:
game.Workspace
game.Players
and so on
Each one runs the hook code above, which is extremely resource intensive because the hook is probably called thousands of times per second.On top of that, if you obfuscate this part, you will end up with more than a million instructions to run every second, which will crash your game.
Do not wrap your entire script in LPH_NO_VIRTUALIZE, as it defeats the purpose of virtualization (obfuscation). Only use this macro if a function runs more than 30 times per second.
You need to exclude these frequently run functions from obfuscation, using either loadstring() or LPH_NO_VIRTUALIZE.We recommend LPH_NO_VIRTUALIZE over loadstring, because code excluded with LPH_NO_VIRTUALIZE is generally harder to view.
LPH_NO_VIRTUALIZE takes one argument, which must be a constant function (written inline, not passed as a variable), and returns a function you can call.
Add this at the top of your script, so the original (unobfuscated) code still runs:
getfenv().LPH_NO_VIRTUALIZE = function(f) return f end;
This pattern works for any LPH macro. See Macros.Here are examples of good and bad usage of LPH_NO_VIRTUALIZE:
Good usage
RunService.RenderStepped(LPH_NO_VIRTUALIZE(function(s) -- regular function bodyend))
old = hookmetamethod(game, "__index", LPH_NO_VIRTUALIZE(function(t, k) -- regular function bodyend))
local generateTracers = LPH_NO_VIRTUALIZE(function(pos, pos2) -- function bodyend)RunService.Heartbeat:Connect(generateTracers)
LPH_NO_VIRTUALIZE(function() for i,v in pairs(getgc()) do if type(v) == 'table' then f = v end endend)()
Bad usage
local function doSomething()-- function bodyendLPH_NO_VIRTUALIZE(doSomething) -- you can't pass reference argumentshookfunction(print, doSomething)
LPH_NO_VIRTUALIZE( -- this is a syntax error local old old = hookmetamethod(game, "__namecall", function(...) end))
LPH_NO_VIRTUALIZE(function() -- somethingend) -- This part will not run because you are not calling it at the end.-- You should add an extra () at the end in order to call itprint('done')