[ML] (error) SobelOperator.lua: ...eaned originalSA by kichiro\moonloader\SobelOperator.lua:205: attempt to call field 'GetDevice' (a nil value)
stack traceback:
	...eaned originalSA by kichiro\moonloader\SobelOperator.lua:205: in function 'process_sobel'
	...eaned originalSA by kichiro\moonloader\SobelOperator.lua:267: in function 'OnDrawFrame'
	...s\cleaned originalSA by kichiro\moonloader\lib\imgui.lua:1378: in function <...s\cleaned originalSA by kichiro\moonloader\lib\imgui.lua:1367>

```lua
-- SobelOperator.lua
-- Применяет оператор Собеля как шейдер-оверлей в SA-MP с использованием MoonLoader, mimgui и LuaJIT FFI

local ffi = require 'ffi'
local dx9 = require 'mimgui.dx9'       -- Исправленное подключение модуля dx9
local imgui = require 'imgui'          -- Библиотека для создания интерфейса
local bit = require 'bit'
local band = bit.band
local bor = bit.bor
local lshift = bit.lshift
local rshift = bit.rshift

-- Определяем необходимые структуры и методы через FFI
ffi.cdef[[
typedef long HRESULT;
typedef unsigned long DWORD;
typedef unsigned int UINT;
typedef unsigned char BYTE;

typedef struct IDirect3DDevice9 IDirect3DDevice9;
typedef struct IDirect3DSurface9 IDirect3DSurface9;
typedef struct IDirect3DTexture9 IDirect3DTexture9;

typedef struct D3DLOCKED_RECT {
    long Pitch;
    void* pBits;
} D3DLOCKED_RECT;

typedef struct RECT {
    long left;
    long top;
    long right;
    long bottom;
} RECT;

// Перечисление форматов пикселей
typedef enum D3DFORMAT {
    D3DFMT_A8R8G8B8 = 20
    /* Добавьте другие форматы при необходимости */
} D3DFORMAT;

// Методы устройства Direct3D
typedef HRESULT (__stdcall *GetBackBuffer_t)(IDirect3DDevice9* self, UINT iBackBuffer, UINT iDest, IDirect3DSurface9** ppD3DSurface9);
typedef HRESULT (__stdcall *LockRect_t)(IDirect3DSurface9* self, D3DLOCKED_RECT* pLockedRect, const RECT* pRect, DWORD Flags);
typedef HRESULT (__stdcall *UnlockRect_t)(IDirect3DSurface9* self);

// Методы IDirect3DDevice9 vTable
typedef struct IDirect3DDevice9_Methods {
    void* QueryInterface;
    void* AddRef;
    void* Release;
    void* TestCooperativeLevel;
    void* GetAvailableTextureMem;
    void* EvictManagedResources;
    void* GetDirect3D;
    void* SetSoftwareVertexProcessing;
    void* GetSoftwareVertexProcessing;
    void* SetTexture;
    void* GetTexture;
    void* SetTextureStageState;
    void* GetTextureStageState;
    void* SetSamplerState;
    void* GetSamplerState;
    void* SetRenderState;
    void* GetRenderState;
    void* CreateTexture;
    void* GetRenderTarget;
    GetBackBuffer_t GetBackBuffer;
    void* BeginScene;
    void* EndScene;
    void* Clear;
    void* SetTransform;
    void* GetTransform;
    void* CreateVertexBuffer;
    void* SetViewport;
    void* GetViewport;
    void* SetMaterial;
    void* GetMaterial;
    void* SetLight;
    void* GetLight;
    void* LightEnable;
    void* GetLightEnable;
    void* SetFVF;
    void* GetFVF;
    void* CreateVertexShader;
    void* SetVertexShader;
    void* GetVertexShader;
    // Остальные методы опущены
} IDirect3DDevice9_Methods;

// Методы IDirect3DSurface9 vTable
typedef struct IDirect3DSurface9_Methods {
    void* QueryInterface;
    void* AddRef;
    void* Release;
    void* GetDesc;
    LockRect_t LockRect;
    UnlockRect_t UnlockRect;
    void* GetDC;
    void* ReleaseDC;
    // Остальные методы опущены
} IDirect3DSurface9_Methods;

// Структура IDirect3DDevice9
typedef struct IDirect3DDevice9 {
    IDirect3DDevice9_Methods* lpVtbl;
    // Остальные поля опущены
} IDirect3DDevice9;

// Структура IDirect3DSurface9
typedef struct IDirect3DSurface9 {
    IDirect3DSurface9_Methods* lpVtbl;
    // Остальные поля опущены
} IDirect3DSurface9;
]]

-- Константы
local D3D_OK = 0

-- Определение операторов Собеля
local Gx = ffi.new('int[3][3]', {
    {-1, 0, 1},
    {-2, 0, 2},
    {-1, 0, 1}
})

local Gy = ffi.new('int[3][3]', {
    { 1,  2,  1},
    { 0,  0,  0},
    {-1, -2, -1}
})

-- Функция для преобразования ARGB в оттенки серого
local function rgbToGrayscale(r, g, b)
    return math.floor(0.3 * r + 0.59 * g + 0.11 * b)
end

-- Функция для преобразования буфера пикселей в оттенки серого
local function to_grayscale(width, height, pixels_ptr, pitch, grayscale)
    local ptr = ffi.cast('unsigned int*', pixels_ptr)
    for y = 0, height - 1 do
        for x = 0, width - 1 do
            local pixel = ptr[y * (pitch / 4) + x]
            local b = band(pixel, 0xFF)
            local g = band(rshift(pixel, 8), 0xFF)
            local r = band(rshift(pixel, 16), 0xFF)
            grayscale[y * width + x] = rgbToGrayscale(r, g, b)
        end
    end
end

-- Функция для применения оператора Собеля
local function apply_sobel(width, height, grayscale, edge)
    for y = 1, height - 2 do
        for x = 1, width - 2 do
            local sumX = 0
            local sumY = 0
            for ky = 0, 2 do
                for kx = 0, 2 do
                    local pixel = grayscale[(y + ky - 1) * width + (x + kx - 1)]
                    sumX = sumX + Gx[ky][kx] * pixel
                    sumY = sumY + Gy[ky][kx] * pixel
                end
            end
            local mag = math.sqrt(sumX * sumX + sumY * sumY)
            if mag > 255 then mag = 255 end
            edge[y * width + x] = math.floor(mag)
        end
    end
end

-- Функция для создания текстуры из данных границ
local function create_edge_texture(device, width, height, edge_data)
    local texture = dx9.CreateTexture(width, height, 1, 0, dx9.D3DFMT_A8R8G8B8, dx9.D3DPOOL_MANAGED)
    if not texture then return nil end

    local rect = ffi.new('RECT', {left = 0, top = 0, right = width, bottom = height})

    local locked_rect = dx9.LockRect(texture, rect, 0)  -- Предполагается, что флаги = 0
    if not locked_rect.pBits then
        dx9.UnlockRect(texture)
        dx9.Release(texture)
        return nil
    end

    local pixels = ffi.cast('unsigned int*', locked_rect.pBits)
    for y = 0, height - 1 do
        for x = 0, width - 1 do
            local intensity = edge_data[y * width + x]
            -- Создаем белый цвет при обнаружении границы, черный иначе
            local color = bor(lshift(intensity, 16), lshift(intensity, 8), intensity)
            pixels[y * width + x] = bor(lshift(255, 24), color)  -- A8R8G8B8
        end
    end

    dx9.UnlockRect(texture)
    return texture
end

-- Переменные для хранения текстуры границ
local edge_texture = nil

-- Функция обработки кадра и применения оператора Собеля
local function process_sobel()
    local device = dx9.GetDevice()
    if not device then return end

    -- Получаем Back Buffer
    local back_buffer = dx9.GetBackBuffer(device, 0, 0, dx9.D3DBACKBUFFER_TYPE_MONO)
    if not back_buffer then return end

    -- Захватываем размеры экрана
    local screen_width, screen_height = dx9.GetScreenSize()
    if screen_width == 0 or screen_height == 0 then
        dx9.Release(back_buffer)
        return
    end

    -- Блокируем Back Buffer для доступа к пикселям
    local locked_rect = dx9.LockRect(back_buffer, ffi.new('RECT', {left = 0, top = 0, right = screen_width, bottom = screen_height}), 0)
    if locked_rect.hresult ~= D3D_OK or not locked_rect.pBits then
        dx9.Release(back_buffer)
        return
    end

    -- Выделяем память для оттенков серого и границ
    local grayscale = ffi.new('unsigned char[?]', screen_width * screen_height)
    local edge = ffi.new('unsigned char[?]', screen_width * screen_height)

    -- Преобразуем изображение в оттенки серого
    to_grayscale(screen_width, screen_height, locked_rect.pBits, locked_rect.Pitch, grayscale)

    -- Разблокируем Back Buffer
    dx9.UnlockRect(back_buffer)
    dx9.Release(back_buffer)

    -- Применяем оператор Собеля
    apply_sobel(screen_width, screen_height, grayscale, edge)

    -- Создаем текстуру из полученных данных
    local new_texture = create_edge_texture(device, screen_width, screen_height, edge)

    -- Освобождаем предыдущую текстуру, если она существует
    if edge_texture then
        dx9.Release(edge_texture)
    end

    edge_texture = new_texture
end

-- Основная функция
function main()
    while not isSampAvailable() do
        wait(200)
    end
    imgui.Process = false
    local window = imgui.ImBool(true)  -- Показать окно при старте
    while true do
        wait(0)
        imgui.Process = window.v
    end
end

-- Функция отрисовки окна ImGui
function imgui.OnDrawFrame()
    -- Обработка оператора Собеля при каждом кадре
    process_sobel()

    if edge_texture then
        local resX, resY = getScreenResolution()
        local sizeX, sizeY = 300, 300 -- Размер окна

        imgui.SetNextWindowPos(imgui.ImVec2(resX / 2 - sizeX / 2, resY / 2 - sizeY / 2), imgui.Cond.FirstUseEver)
        imgui.SetNextWindowSize(imgui.ImVec2(sizeX, sizeY), imgui.Cond.FirstUseEver)
        if imgui.Begin('Sobel Operator Shader', window) then
            -- Отображаем текстуру границ
            imgui.Image(edge_texture, imgui.ImVec2(sizeX - 20, sizeY - 40))
        end
        imgui.End()
    end
end
```
Почини код