revenge-bundle-next/lib/patcher/README.md
2025-06-09 22:47:21 +07:00

6.7 KiB

@revenge-mod/patcher

This is a JavaScript library that allows to monkey patch functions in a way that is easy to use and understand.

It is made by ryan-0324 (source uploaded on Discord) and licensed under CC0.

This source code is polished and adapted to work with Revenge. We do not claim any rights to this library. You may use it granted you follow the license.

How does this work?

Think of hooks as separate functions that are called before, after, or instead of the original function. You can use them to modify the behavior of the original function, or to add new functionality.

Before hooks

Before hooks allow you to modify the arguments passed to the original function, or to perform some action before the original function is called.

import { before } from '@revenge-mod/patcher'

const obj = {
    method: (a) => console.log('Original method called with:', a)
}

before(obj, 'method', ([a]) => {
    console.log('Before method called with:', a)
    // Modify arguments by returning new array
    return [a + 1]
})

obj.method(2)
// CONSOLE OUTPUT:
// Before method called with: 2
// Original method called with: 3

After hooks

After hooks allow you to modify the return value of the original function, or to perform some action after the original function is called.

import { after } from '@revenge-mod/patcher'

const obj = {
    method: (a) => a * 2
}

after(obj, 'method', (result) => {
    console.log('After method called with result:', result)
    // Modify return value
    return result + 1
})

console.log(obj.method(2)) // 5
// CONSOLE OUTPUT:
// After method called with result: 4
// 5

Instead hooks

Instead hooks allow you to completely replace the original function with a new one, while still being able to call the original function if needed.

import { instead } from '@revenge-mod/patcher'

const obj = {
    method: (a) => {
        console.log('Original method called with:', a)
        return 'original result'
    }
}

instead(obj, 'method', ([a], original) => {
    console.log('Instead method called with:', a)
    // Call the original function if needed
    const originalResult = original('modified')
    console.log('Original method was called')
    
    // Return a new value
    return 'new value'
})

console.log(obj.method('test')) // 'new value'
// CONSOLE OUTPUT:
// Instead method called with: test
// Original method called with: modified
// Original method was called
// new value

Combining hooks

You can combine multiple hooks on the same function.

import { before, after, instead } from '@revenge-mod/patcher'

const obj = {
    method: (a) => a * 2
}

before(obj, 'method', ([a]) => {
    console.log('Before method called with:', a)
    return [a + 1] // 2 becomes 3
})

instead(obj, 'method', ([a], original) => {
    console.log('Instead method called with:', a)
    const result = original(a) // Call original with modified arg (3)
    console.log('Original method returned:', result)
    return result + 1 // 6 becomes 7  
})

after(obj, 'method', (result) => {
    console.log('After method called with result:', result)
    return result + 1 // 7 becomes 8
})

console.log(obj.method(2)) // 8
// CONSOLE OUTPUT:
// Before method called with: 2
// Instead method called with: 3
// Original method returned: 6
// After method called with result: 7
// 8

Stacking hooks

Stacking multiple hooks of same type will execute them in a specific order:

  1. Before hooks (execute in reverse order - last added runs first)
  2. Instead hooks (only the last added executes, unless calling original)
  3. After hooks (execute in order - first added runs first)

Using multiple before hooks will execute them in reverse order (LIFO - Last In, First Out):

import { before } from '@revenge-mod/patcher'

const obj = {
    method: (a) => console.log('Original method called with:', a)
}

before(obj, 'method', ([a]) => {
    console.log('First before hook called with:', a)
    return [a + 1]
})

before(obj, 'method', ([a]) => {
    console.log('Second before hook called with:', a)
    return [a + 2]
})

obj.method(2)
// CONSOLE OUTPUT:
// Second before hook called with: 2
// First before hook called with: 4
// Original method called with: 5

Multiple after hooks execute in normal order (FIFO - First In, First Out):

import { after } from '@revenge-mod/patcher'

const obj = {
    method: (a) => a * 2
}

after(obj, 'method', (result) => {
    console.log('First after hook called with result:', result)
    return result + 1
})

after(obj, 'method', (result) => {
    console.log('Second after hook called with result:', result)
    return result + 2
})

console.log(obj.method(2)) // 7
// CONSOLE OUTPUT:
// First after hook called with result: 4
// Second after hook called with result: 5
// 7

Multiple instead hooks will only execute the last one added. To chain them, you must call the original function:

import { instead } from '@revenge-mod/patcher'

const obj = {
    method: (a) => `original-${a}`
}

instead(obj, 'method', ([a], original) => {
    console.log('First instead hook called with:', a)
    return `first-${original(a)}`
})

instead(obj, 'method', ([a], original) => {
    console.log('Second instead hook called with:', a)
    // Calling original will execute the previous instead hook
    return `second-${original(a)}`
})

console.log(obj.method('test')) // 'second-first-original-test'
// CONSOLE OUTPUT:
// Second instead hook called with: test
// First instead hook called with: test
// second-first-original-test

Removing hooks

Each hook returns an unpatch function that removes the hook:

import { before } from '@revenge-mod/patcher'

const obj = {
    method: (a) => console.log('Original method called with:', a)
}

const unpatch = before(obj, 'method', ([a]) => {
    console.log('Before method called with:', a)
    return [a + 1]
})

obj.method(2)
// CONSOLE OUTPUT:
// Before method called with: 2
// Original method called with: 3

unpatch() // Remove the hook

obj.method(2)
// CONSOLE OUTPUT:
// Original method called with: 2

Unpatch functions are safe to call multiple times and won't throw errors.

Constructor patching

You can also patch constructor functions:

import { before } from '@revenge-mod/patcher'

class MyClass {
    constructor(value) {
        this.value = value
    }
}

const exports = { MyClass }

before(exports, 'MyClass', ([value]) => {
    console.log('Constructor called with:', value)
    return [`modified-${value}`]
})

const instance = new exports.MyClass('test')
console.log(instance.value) // 'modified-test'
// CONSOLE OUTPUT:
// Constructor called with: test
// modified-test