Jump to content

UpvalueHacker and component


Recommended Posts

How can I use rezecib's UpvalueHacker util to modify this onAttacked event?
 

local function onAttacked(player, data)
	...
end

...

local SomeComponent = Class(function(self, player)
	...

	player:ListenForEvent("attacked", onAttacked) -- <- event listener I wanna modify
end)

I just can't figure out the root for the component.. I've tried randomly guessing self.class, self.Class, self.fn, self._ctor but none have worked.

Maybe the author can help me on this? @rezecib

Link to comment
Share on other sites

@ptr

I just tried this

AddClassPostConstruct("components/age", function(self, ...)
	local fn = UpvalueHacker.FindUpvalue(self._ctor, "OnSetOwner")
	print("This is what I found:", fn)
end)

And the result I get is "This is what I found: nil".

Can you point out what's wrong?

Link to comment
Share on other sites

Well, it is due to how AddClassPostConstruct works. It actually overrides the original constructor, adding your function behind the original one.

So, in order to protect the original constructor, you have to use AddComponentPostInit instead.

I don't know about the UpvalueHacker and how it works, so here's my code...

Quote

AddComponentPostInit("age",function(inst)
    for i = 1,math.huge do
        local n, v = GLOBAL.debug.getupvalue(inst._ctor,i)
        if n == nil then 
            break
        elseif n == "OnSetOwner" then
            print(v)
        end
    end
end)

 

Link to comment
Share on other sites

@ptr

Well, on a first look it worked. It did find the reference to the function.. but when I tried to modify it I realized it it was the one outside the constructor. How can I access the one inside of it?

This is what I have now

AddComponentPostInit("age", function(self, ...)
	local fn = UpvalueHacker.FindUpvalue(self._ctor, "OnSetOwner")
	print("FindUpvalue", fn) -- This prints correctly
	
	local function OnSetOwner(...)
		print("OnSetOwner") -- This doesn't prints
		return fn(...)
	end
	
	UpvalueHacker.SetUpvalue(self._ctor, OnSetOwner, "OnSetOwner") -- It's doing nothing since it's the one outside the constructor </3
end)

 

Link to comment
Share on other sites

Again, I don't know how that function works, but my code work well...

AddComponentPostInit("age",function(inst)
    for i = 1,math.huge do
        local n, v = GLOBAL.debug.getupvalue(inst._ctor,i)
        if n == nil then 
            break
        elseif n == "OnSetOwner" then
            GLOBAL.debug.setupvalue(inst._ctor,i,function(inst)
                v(inst)
                print("Hooked!")
            end)
        end
    end
end)

 

Link to comment
Share on other sites

The upvalue hacker edits upvalues.

What's an upvalue?

local something

local function fn()
    return something + 2
end

something is an upvalue of fn.

In this case, OnSetOwner is an upvalue of the constructor function.

Why doesn't it work?

Because the constructor function runs before you edit the upvalue from where it will take the value to use for ListenForEvent.

local ac = GLOBAL.require("components/age")
local fn = UpvalueHacker.FindUpvalue(ac._ctor, "OnSetOwner")

local function OnSetOwner(...)
    print("OnSetOwner")
    return fn(...)
end

UpvalueHacker.SetUpvalue(ac._ctor, OnSetOwner, "OnSetOwner")

 

Link to comment
Share on other sites

@DarkXero

Theoretically, we cannot change the function address assigned to eventlistener table in AddComponentPostInit through assignment, without touching the table itself, as the class has already been constructed. But in DST, the code actually works somehow? It seems the setupvalue has replaced the original function with a pointer to the new function.

Quote

[00:00:25]: Telling Client our new session identifier: 2A712D96A8427ED7
[00:00:25]: ModIndex: Load sequence finished successfully.    
[00:00:25]: Reset() returning
[00:00:26]: Attempting to send resume request
[00:00:26]: Resuming user: session/2A712D96A8427ED7/A7JGRTPS5EPJ/0000000041
[00:00:26]: Hooked!    
[00:00:26]: ResumeExistingUserSession    111200    
[00:00:26]: Spawning player at: [Load] (-340.00, 0.00, -124.00)    
[00:00:26]: ReceiveResumeNotification
[00:00:26]: ResumeRequestLoadComplete    true    
[00:00:26]: Deserializing tile data (350 x 350)

 

Link to comment
Share on other sites

36 minutes ago, ptr said:

But in DST, the code actually works somehow?

You will have to provide the code and what did you do.

AddComponentPostInit("age",function(inst)
    for i = 1,math.huge do
        local n, v = GLOBAL.debug.getupvalue(inst._ctor,i)
        if n == nil then 
            break
        elseif n == "OnSetOwner" then
            GLOBAL.debug.setupvalue(inst._ctor,i,function(inst)
                v(inst)
                print("Hooked!")
            end)
        end
    end
end)

This prints nothing. Unless you had something different or another player join you. Clean the logs and try again.

Link to comment
Share on other sites

4 minutes ago, DarkXero said:

Still can't replicate.

It's really weird, that's all the code in modmain...

And here's the modinfo

api_version = 6
api_version_dst = 10

dont_starve_compatible = true
reign_of_giants_compatible = true
shipwrecked_compatible = true
dst_compatible = true

server_only_mod = false
all_clients_require_mod = true
client_only_mod = false

configuration_options = 
{}

The rest name things won't make a magic right?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...