Полезные сниппеты и функции

kizn

\ 0 _ 0 /
Всефорумный модератор
2,407
2,108
Описание: возвращает версию сампа
Lua:
local ffi = require("ffi")

function get_samp_version()
    local samp_base = getModuleHandle("samp.dll")

    if samp_dll ~= 0 then
        local e_lfanew = ffi.cast("long*", samp_base + 60)[0]
        local nt_header = samp_base + e_lfanew
        local entry_point_addr = ffi.cast("unsigned int*", nt_header + 40)[0]
        if entry_point_addr == 0x31DF13 then
            return "r1"
        elseif entry_point_addr == 0xCC4D0 then
            return "r3"
        end
    end

    return "unknown"
end

Пример использования:
Lua:
local ffi = require("ffi")

function get_samp_version()
    local samp_base = getModuleHandle("samp.dll")

    if samp_dll ~= 0 then
        local e_lfanew = ffi.cast("long*", samp_base + 60)[0]
        local nt_header = samp_base + e_lfanew
        local entry_point_addr = ffi.cast("unsigned int*", nt_header + 40)[0]
        if entry_point_addr == 0x31DF13 then
            return "r1"
        elseif entry_point_addr == 0xCC4D0 then
            return "r3"
        end
    end

    return "unknown"
end

function addChatMessage(color, message)
    local samp_base = getModuleHandle("samp.dll")
    local samp_version = get_samp_version()
   
    if samp_version ~= "unknown" then
        local CChat__AddMessage = ffi.cast(
            "void(__thiscall*)(void* pChat, unsigned int color, const char* text)",
            (samp_version == "r1") and (samp_base + 0x64450) or (samp_base + 0x679F0)
        )
       
        -- CChat* this
        local pChat = ffi.new("void*")
        pChat = ffi.cast(
            "void**",
            (samp_version == "r1") and (samp_base + 0x21A0E4) or (samp_base + 0x26E8C8)
        )[0]

        if pChat ~= 0 then
            return CChat__AddMessage(pChat, color, message)
        end
    end
end

function main()
    wait(500)

    -- msg color must be in RGBA fmt
    addChatMessage(-1, "Hello, BlastHack!")
    addChatMessage(0xFF000000, "{00FF00}hello {00FFFF}world")
end
определяешь переменную samp_base, проверяешь samp_dll
 
  • Нравится
Реакции: moreveal

PanSeek

t.me/dailypanseek
Всефорумный модератор
908
1,776
Lua:
local mem = require 'memory'
function setTireMarks(float)
    mem.setfloat(0x87111C, float) -- default: 0.35714287
end
sa-mp-165.png
Мелкая деталь которую мало кто замечал. Эта функция отключает или включает руление руля в транспорте.
Lua:
function turnRudder(bool)
    mem.setdouble(0x871FD0, bool and 0.278688609432124 or 0.0)
end
 
Последнее редактирование:

Gorskin

♥ Love Lua ♥
Проверенный
1,339
1,178
Изменяет размер прицела.
Lua:
local memory = require "memory"
local sizeX = 30
local sizeY = 30
memory.setfloat(8755780, sizeX, false)
memory.setfloat(8755804, sizeY, false)
memory.write(5825313, 8755804, 4, false)--разделение оси
 
  • Нравится
Реакции: rieder, xColorized и ARMOR

PanSeek

t.me/dailypanseek
Всефорумный модератор
908
1,776
Описание: Меняет назначения стандартных клавиш (не все, т.к. не знаю как разобрать switch case в IDA) для SA-MP'а. (Да, я знаю, что у @4el0ve4ik есть такой плагин, но мало ли кто хочет вшить в свой скрипт (Искал адреса сам ибо нужно было для себя, только потом увидел этот плагин))
Lua:
local mem = require 'memory'
local samp = getModuleHandle('samp.dll')

function setKeyScoreboard(key) -- TAB
    mem.setint8(samp + 0x5DC7F, key, true) -- R3: samp + 0x6101F
end

function setKeyNetworkStats(key) -- F5
    mem.setint8(samp + 0x71369, key, true) -- R3: samp + 0x75259
end

function setKeyHideInterface(key) -- F10
    mem.setint8(samp + 0x713A9, key, true) -- R3: samp + 0x75299
end

function setKeyHelpDialog(key) -- F1
    mem.setint8(samp + 0x713E0, key, true) -- R3: samp + 0x752D0
end

function setKeyReturnChangeClass(key) -- F4
    mem.setint8(samp + 0x797E, key, true) -- R3: samp + 0x79A4
end
Пример использования: Можно перебиндить эти клавиши под себя или вовсе их выключить.
Вот пример того, как я назначил клавишу 0x0, то есть ничего. Окно помощи (F1) показываться не будет:
Lua:
local mem = require 'memory'
local samp, defaultKey = 0x0, 0
function main()
    while not isSampAvailable() do wait(40) end
    samp = getModuleHandle('samp.dll')
    defaultKey = getKeyHelpDialog()
    setKeyHelpDialogNULL(true)
end
function onScriptTerminate(script, bQuitGame)
    if script == thisScript() then
        setKeyHelpDialogNULL(false)
    end
end
function setKeyHelpDialogNULL(bool)
    mem.setint8(samp + 0x713E0, bool and 0x00 or defaultKey, true)
end
function getKeyHelpDialog()
    return mem.getint8(samp + 0x713E0)
end
 

PanSeek

t.me/dailypanseek
Всефорумный модератор
908
1,776
Описание: Изменяет health bar.
Lua:
function setHealthBar(int)
    int = int > 248 and 248 or int -- "maximum" value is 1454.5
    local div = int > 100 and 5.86 or 5.69
    mem.setfloat(0xB793E0, int * div)
end
Пример использования: Если хотите health bar на 160 очков жизни, то setHealthBar(160).
P.S. С мелкими значениями, по типу 10 - работает плохо ибо обводка много места занимает.
КОД ПЛОХОЙ!!!
Постоянно перезаписывается число в память, когда можно изменить один раз, мне лень делать нормальные проверки, если нужно будет - сделаете.
Lua:
local mem = require 'memory'

function main()
    while true do wait(0)
        setHealthBar(getCharHealth(playerPed))
    end
end

function setHealthBar(int)
    int = int > 248 and 248 or int
    local div = int > 100 and 5.86 or 5.69
    mem.setfloat(0xB793E0, int * div)
end
 
Последнее редактирование:

pulemetov scripts

Участник
21
33
Описание: Таблица с id и названиями машин. стандарт + кастомные аризоны
Lua:
local NameCar = {
    --[ID CAR] = 'NAME CAR'
    --STANDART CAR
    [400] = 'Landstalker',
    [401] = 'Bravura',
    [402] = 'Buffalo',
    [403] = 'Linerunner',
    [404] = 'Perenniel',
    [405] = 'Sentinel',
    [406] = 'Dumper',
    [407] = 'Firetruck',
    [408] = 'Trashmaster',
    [409] = 'Stretch',
    [410] = 'Manana',
    [411] = 'Infernus',
    [412] = 'Voodoo',
    [413] = 'Pony',
    [414] = 'Mule',
    [415] = 'Cheetah',
    [416] = 'Ambulance',
    [417] = 'Leviathan',
    [418] = 'Moonbeam',
    [419] = 'Esperanto',
    [420] = 'Taxi',
    [421] = 'Washington',
    [422] = 'Bobcat',
    [423] = 'Mr Whoopee',
    [424] = 'BF Injection',
    [425] = 'Hunter',
    [426] = 'Premier',
    [427] = 'Enforcer',
    [428] = 'Securicar',
    [429] = 'Banshee',
    [430] = 'Predator',
    [431] = 'Bus',
    [432] = 'Rhino',
    [433] = 'Barracks',
    [434] = 'Hotknife',
    [435] = 'Article Trailer',
    [436] = 'Previon',
    [437] = 'Coach',
    [438] = 'Cabbie',
    [439] = 'Stallion',
    [440] = 'Rumpo',
    [441] = 'RC Bandit',
    [442] = 'Romero',
    [443] = 'Packer',
    [444] = 'Monster',
    [445] = 'Admiral',
    [446] = 'Squallo',
    [447] = 'Seasparrow',
    [448] = 'Pizzaboy',
    [449] = 'Tram',
    [450] = 'Article Trailer 2',
    [451] = 'Turismo',
    [452] = 'Speeder',
    [453] = 'Reefer',
    [454] = 'Tropic',
    [455] = 'Flatbed',
    [456] = 'Yankee',
    [457] = 'Caddy',
    [458] = 'Solair',
    [459] = "Berkley's RC",
    [460] = 'Skimmer',
    [461] = 'PCJ-600',
    [462] = 'Faggio',
    [463] = 'Freeway',
    [464] = 'RC Baron',
    [465] = 'RC Raider',
    [466] = 'Glendale',
    [467] = 'Oceanic',
    [468] = 'Sanchez',
    [469] = 'Sparrow',
    [470] = 'Patriot',
    [471] = 'Quad',
    [472] = 'Coastguard',
    [473] = 'Dinghy',
    [474] = 'Hermes',
    [475] = 'Sabre',
    [476] = 'Rustler',
    [477] = 'ZR-350',
    [478] = 'Walton',
    [479] = 'Regina',
    [480] = 'Comet',
    [481] = 'BMX',
    [482] = 'Burrito',
    [483] = 'Camper',
    [484] = 'Marquis',
    [485] = 'Baggage',
    [486] = 'Dozer',
    [487] = 'Maverick',
    [488] = 'SAN News Maverick',
    [489] = 'Rancher',
    [490] = 'FBI Rancher',
    [491] = 'Virgo',
    [492] = 'Greenwood',
    [493] = 'Jetmax',
    [494] = 'Hotring Racer',
    [495] = 'Sandking',
    [496] = 'Blista Compact',
    [497] = 'Police Maverick',
    [498] = 'Boxville',
    [499] = 'Benson',
    [500] = 'Mesa',
    [501] = 'RC Goblin',
    [502] = 'Hotring Racer A',
    [503] = 'Hotring Racer B',
    [504] = 'Bloodring Banger',
    [505] = 'Rancher',
    [506] = 'Super GT',
    [507] = 'Elegant',
    [508] = 'Journey',
    [509] = 'Bike',
    [510] = 'Mountain Bike',
    [511] = 'Beagle',
    [512] = 'Cropduster',
    [513] = 'Stuntplane',
    [514] = 'Tanker',
    [515] = 'Roadtrain',
    [516] = 'Nebula',
    [517] = 'Majestic',
    [518] = 'Buccaneer',
    [519] = 'Shamal',
    [520] = 'Hydra',
    [521] = 'FCR-900',
    [522] = 'NRG-500',
    [523] = 'HPV1000',
    [524] = 'Cement Truck',
    [525] = 'Towtruck',
    [526] = 'Fortune',
    [527] = 'Cadrona',
    [528] = 'FBI Truck',
    [529] = 'Willard',
    [530] = 'Forklift',
    [531] = 'Tractor',
    [532] = 'Combine Harvester',
    [533] = 'Feltzer',
    [534] = 'Remington',
    [535] = 'Slamvan',
    [536] = 'Blade',
    [537] = 'Freight (Train)',
    [538] = 'Brownstreak (Train)',
    [539] = 'Vortex',
    [540] = 'Vincent',
    [541] = 'Bullet',
    [542] = 'Clover',
    [543] = 'Sadler',
    [544] = 'Firetruck LA',
    [545] = 'Hustler',
    [546] = 'Intruder',
    [547] = 'Primo',
    [548] = 'Cargobob',
    [549] = 'Tampa',
    [550] = 'Sunrise',
    [551] = 'Merit',
    [552] = 'Utility Van',
    [553] = 'Nevada',
    [554] = 'Yosemite',
    [555] = 'Windsor',
    [556] = 'Monster A',
    [557] = 'Monster B',
    [558] = 'Uranus',
    [559] = 'Jester',
    [560] = 'Sultan',
    [561] = 'Stratum',
    [562] = 'Elegy',
    [563] = 'Raindance',
    [564] = 'RC Tiger',
    [565] = 'Flash',
    [566] = 'Tahoma',
    [567] = 'Savanna',
    [568] = 'Bandito',
    [569] = 'Freight Flat Trailer',
    [570] = 'Streak Trailer',
    [571] = 'Kart',
    [572] = 'Mower',
    [573] = 'Dune',
    [574] = 'Sweeper',
    [575] = 'Broadway',
    [576] = 'Tornado',
    [577] = 'AT400',
    [578] = 'DFT-30',
    [579] = 'Huntley',
    [580] = 'Stafford',
    [581] = 'BF-400',
    [582] = 'Newsvan',
    [583] = 'Tug',
    [584] = 'Petrol Trailer',
    [585] = 'Emperor',
    [586] = 'Wayfarer',
    [587] = 'Euros',
    [588] = 'Hotdog',
    [589] = 'Club',
    [590] = 'Freight Box Trailer',
    [591] = 'Article Trailer 3',
    [592] = 'Andromada',
    [593] = 'Dodo',
    [594] = 'RC Cam',
    [595] = 'Launch',
    [596] = 'Police Car (LSPD)',
    [597] = 'Police Car (SFPD)',
    [598] = 'Police Car (LVPD)',
    [599] = 'Police Ranger',
    [600] = 'Picador',
    [601] = 'S.W.A.T.',
    [602] = 'Alpha',
    [603] = 'Phoenix',
    [604] = 'Glendale Shit',
    [605] = 'Sadler Shit',
    [606] = 'Baggage Trailer A',
    [607] = 'Baggage Trailer B',
    [608] = 'Tug Stairs Trailer',
    [609] = 'Boxville',
    [610] = 'Farm Trailer',
    [611] = 'Utility Trailer',
    --CUSTOM CARS (ARIZONA)
    [612] = 'Mercedes-Benz GT63 AMG',
    [613] = 'Mercedes-Benz G63 AMG',
    [614] = 'Audi RS6',
    [662] = 'BMW X5',
    [663] = 'Chevrolet Corvette',
    [665] = 'Chevrolet Cruze',
    [666] = 'Lexus LX',
    [667] = 'Porsche 911',
    [668] = 'Porsche Cayenne',
    [699] = 'Bentley',
    [793] = 'BMW M8',
    [794] = 'Mercedes-Benz E63',
    [909] = 'Mercedes-Benz S63',
    [965] = 'Volksvagen Touareg',
    [1194] = 'Lamborghini Urus',
    [1195] = 'Audi Q8',
    [1196] = 'Dodge Challenger SRT',
    [1197] = 'Acura NSX',
    [1198] = 'Volvo V60',
    [1199] = 'Range Rover Evoque',
    [1200] = 'Honda Civic Type-R',
    [1201] = 'Lexus Sport-S',
    [1202] = 'Ford Mustang GT',
    [1203] = 'Volvo XC90',
    [1204] = 'Jaguar F-PACE',
    [1205] = 'Kia Optima',
    [3155] = 'BMW Z4 40i', 
    [3156] = 'Mercedes-Benz S600 W140',
    [3157] = 'BMW X5 e53', 
    [3158] = 'Nissan GTR R34',
    [3194] = 'Ducatti Diavel',
    [3195] = 'Ducatti Panigale',
    [3196] = 'Ducatti Ducnacker',
    [3197] = 'Kawasaki Ninja ZX-10RR',
    [3198] = 'Western',
    [3199] = 'Rolls Royce Cullinan',
    [3200] = 'Volkswagen Beetle',
    [3201] = 'Buggati Divo',
    [3202] = 'Buggati Chiron',
    [3203] = 'Fiat 500',
    [3204] = 'Mercedes-Benz GLS 2020',
    [3205] = 'Mercedes-AMG G65 AMG',
    [3206] = 'Lamborghini SVJ',
    [3207] = 'Range Rover SVA',
    [3208] = 'BMW 530i e39',
    [3209] = 'Mercedes-Benz S600 W220',
    [3210] = 'Tesla Model X',
    [3211] = 'Nissan Leaf',
    [3212] = 'Nissan Silvia S15',
    [3213] = 'Subary Forester XT',
    [3215] = 'Subary Legasy 1989',
    [3216] = 'Hyundai Sonata',
    [3217] = 'BMW 750i e38',
    [3218] = 'Mercedes-Benz E55 AMG',
    [3219] = 'Mercedes-Benz E500',
    [3220] = 'Jackson Storm',
    [3222] = 'Lightning McQueen',
    [3223] = 'Mater',
    [3224] = 'Buckingham',
    [3232] = 'Infinity FX50',
    [3233] = 'Lexus RX 450H',
    [3234] = 'Kia Sportage',
    [3235] = 'Volkswagen Golf R',
    [3236] = 'Audi R8',
    [3237] = 'Tayota Camry XV40',
    [3238] = 'Tayota Camry XV70',
    [3239] = 'BMW M5 e60',
    [3240] = 'BMW M5 F90',
    [3245] = 'Mercedes Maybach S650',
    [3247] = 'Mercedes-Benz AMG GT',
    [3248] = 'Porsche Panamera',
    [3251] = 'Volkswagen Passat',
    [3254] = 'Chevrolet Corvette 1980',
    [3266] = 'Dodge RAM',
    [3348] = 'Ford Mustang GT500',
    [3974] = 'Aston Martin BD5',
    [4542] = 'BMW M3 GTR',
    [4543] = 'Chevroler Camaro SS',
    [4544] = 'Mazda RX7',
    [4545] = 'Mazda RX8',
    [4546] = 'Mitsubishi Eclipse',
    [4547] = 'Ford Mustang 289',
    [4548] = 'Nissan 350Z',
    [4774] = 'BMW 760Li',
    [4775] = 'Aston Martin One-77',
    [4776] = 'Bentley Bacalar',
    [4777] = 'Bentley Bentayga',
    [4778] = 'BMW M4 Competition',
    [4779] = 'BMW i8',
    [4780] = 'Genesis G90',
    [4781] = 'Honda Integra Type-R',
    [4782] = 'BMW M3 G20',
    [4783] = 'Mercedes-Benz S500 W223',
    [4784] = 'Ford Raptor',
    [4785] = 'Ferrari J50',
    [4786] = 'Mercedes-Benz SLR McLaren',
    [4787] = 'Subary BRZ',
    [4788] = 'LADA Vesta SW Cross',
    [4789] = 'Porsche Taycan',
    [4790] = 'Ferrari Enzo',
    [4791] = 'UAZ Patriot',
    [4792] = 'Volga',
    [4793] = 'Mercedes-Benz X-Class',
    [4794] = 'Jaguar XF',
    [4795] = 'rcshutle',
    [4796] = 'Dodge Grand Caravan',
    [4797] = 'Dodge Charger',
    [4798] = 'Ford Explorer',
    [4799] = 'Ford F-150',
    [4800] = 'DeltaPlan',
    [4801] = 'Seashark',
    [4802] = 'Lambarghini Cantenario',
    [4803] = 'Ferrari 812 SuperFast',
    [6604] = 'Audi A6',
    [6605] = 'Audi Q7',
    [6606] = 'BMW M6 2020',
    [6606] = 'BMW M6 1990',
    [6608] = 'Mercedes-Benz CLA 45 AMG',
    [6609] = 'Mercedes-Benz CLS 63 AMG',
    [6610] = 'Haval H6',
    [6611] = 'Toyota Land Cruiser 200',
    [6612] = 'Lincoln Continental',
    [6613] = 'Porsche Macan',
    [6614] = 'Daewoo Matiz',
    [6615] = 'Mercedes-Benz 6x6',
    [6616] = 'Mercedes-Benz E63',
    [6617] = 'Monster Mutt',
    [6618] = 'Monster Indonesia',
    [6619] = 'Monster El Toro',
    [6620] = 'Monster Grave Digger',
    [6621] = 'Toyota Land Cruiser Prado',
    [6611] = 'Toyota Rav4',
    [6623] = 'Toyota Supra A90',
    [6624] = 'UAZ',
    [6625] = 'Volvo XC90 2012',
    [12713] = 'Mercedes-Benz GLE 63',
    [12714] = 'Renault Laguna',
    [12715] = 'Mercedes-Benz CLS 53',
    [12716] = 'Audi RS5',
    [12717] = 'Cadillac Escalade 2020',
    [12718] = 'Tesla CyberTrack',
    [12719] = 'Tesla Model S',
    [12720] = 'Ford GT',
    [12721] = 'Dodge Viper',
    [12722] = 'Volkswagen Polo',
    [12723] = 'Mitsibishi Evolution IX',
    [12724] = 'Audi TT RS',
    [12725] = 'Mercedes-Benz Actros',
    [12726] = 'Audi S4',
    [12727] = 'BMW 4-series',
    [12728] = 'Cadillac Escalade 2007',
    [12729] = 'Tayota Chaser',
    [12730] = 'Dacia 1300',
    [12731] = 'Mitsibishi Evolution X',
    [12732] = 'Chevrolet Impala 64',
    [12733] = 'Chevrolet Impala 67',
    [12734] = 'Kenwooth Track',
    [12735] = 'Kenwooth Trailer',
    [12736] = 'McLaren MP4',
    [12737] = 'Ford Mustang Mach 1',
    [12738] = 'Rolls-Royce Phantom',
    [12739] = 'Picup Track',
    [12740] = 'Volvo Track',
    [12741] = 'Subary WRX STI',
    [12742] = 'Sherp',
    [12743] = 'Sanki',
    [14119] = 'Audi A6',
    [14120] = 'Toyota Camry',
    [14121] = 'Kia',
    [14122] = 'Tesla Model X',
    [14123] = 'Toyota Rav4',
    [14124] = 'Nissan GTR 2017',
    [14767] = 'Mercedes-AMG Project One',
    [14768] = 'Aston Martin Valkyrie',
    [14769] = 'Chevrolet Aveo',
    [14857] = 'Bugatti Bolide',
    [14884] = 'Yacota K5',
    [14899] = 'Renault Duster',
    [14904] = 'Chevrolet Monza',
    [14905] = 'Mercedes-Benz G63 EQG',
    [14906] = 'HotWheels',
    [14907] = 'Hummer HX',
    [14908] = 'Ferrari LaFerrari',
    [14909] = 'BMW M5 CS',
    [14910] = 'LADA Priora',
    [14911] = 'quadra',
    [14912] = 'Mercedes-Benz GLE',
    [14913] = 'Mercedes-Benz Vision AVTR',
    [14914] = 'Specialize Stumpjumper',
    [14915] = 'Santa Cruz Tallboy',
    [14916] = 'Spooky Metalhead',
    [14917] = 'Turner Burner',
    [14918] = 'Holding Bus Company',
    [14919] = 'Los-Santos Inter Bus C.',
    [15085] = 'charger',
    [15098] = 'BMW M1 e26',
    [15099] = 'Lamborghini Countach',
    [15100] = 'Nagasaki',
    [15101] = 'Koenigsegg Gemera',
    [15102] = 'Kia K7',
    [15103] = 'toro',
    [15104] = 'Lexus LX600',
    [15105] = 'Nissan Qashqai',
    [15106] = 'predatorr',
    [15107] = 'Volkswagen Scirocco',
    [15108] = 'Longfin',
    [15109] = 'Toyota Land Cruser 300',
    [15110] = 'Wellcraft',
    [15111] = 'Yacht',
    [15112] = 'Boates',
    [15113] = 'Mercedes-Benz A45',
    [15114] = 'Toyota AE86',
    [15115] = 'Land Rover Defender',
    [15116] = 'mach',
    [15117] = 'Mazda 6',
    [15118] = 'Audi R8s',
    [15119] = 'Hyundai Santa Fe',
    [15295] = 'Range Rover Velar',
}
Пример использования:
Lua:
function getNameVehicleModel(id)
    local name
    if id then 
        name = NameCar[id]
    else
        name = 'Неизвестно'
    end
    return name
end
 

leekyrave

Известный
419
226
Функция получения расстояние между долготой и широтой, и долготой2 и широтой2.
Полезно использовать в определении расстояния между айпишниками.

Функция:
function getDistanceBetweenLatitude(latA, lonA, latB, lonB)
    latA, lonA = math.rad(latA), math.rad(lonA)
    latB, lonB = math.rad(latB), math.rad(lonB)

    local latDelta = math.sin((latB - latA) / 2) ^ 2
    local lonDelta = math.sin((lonB - lonA) / 2) ^ 2
    local angle = math.asin(math.sqrt(latDelta + math.cos(latA) * math.cos(latB) * lonDelta)) * 2
    return math.floor(angle * 6371 * 10) / 10
end
Спасибо @Cosmo за исправление недочета
 
Последнее редактирование модератором:

chapo

чопа сребдс // @moujeek
Модератор
8,934
11,702
Описание: создает простое окно (так же возможно добавить вкладки)
Код:
Lua:
local UI = { version = 1, font = {}, tab = 1 }
local Tabs = {'Tab 1', 'Tab 2', 'Tab 3', 'Tab 4'}

function UI.Init()
    local defGlyph = imgui.GetIO().Fonts.ConfigData.Data[0].GlyphRanges
    local font_config = imgui.ImFontConfig()
    font_config.SizePixels = 14.0
    font_config.GlyphExtraSpacing.x = 0.1
    UI.font = {}
    for Size = 10, 25 do
        UI.font[Size] = imgui.GetIO().Fonts:AddFontFromFileTTF(getFolderPath(0x14) .. '\\trebucbd.ttf', Size, font_config, defGlyph)
    end
end

function UI.End()
    imgui.EndChild()
    imgui.End()
    imgui.PopStyleVar()
end

function UI.Begin(title, var, flags, tab_data)
    for i = 10, 25 do
        if UI.font[i] == nil then
            error('Fonts not initialized! Add UI.Init() in imgui.OnInitialize()!')
        end
    end

    --==[ STYLE ]==--
    imgui.PushStyleVarFloat(imgui.StyleVar.WindowRounding, 5)
    local Begin = imgui.Begin(title, var, imgui.WindowFlags.NoTitleBar + (flags or 0))
    local size = imgui.GetWindowSize()
    local DL = imgui.GetWindowDrawList()
    local pos = imgui.GetWindowPos()

    --==[ LOGO ]==--
    imgui.PushFont(UI.font[18])
    local LogoSize = imgui.CalcTextSize(title)
    imgui.SetCursorPos(imgui.ImVec2(15, 15))
    imgui.BeginChild('UI_Window='..title..':Child=Logo', LogoSize, false)
    imgui.SetCursorPos(imgui.ImVec2(0, 0))
    imgui.Text(title)
    imgui.PopFont()
    imgui.EndChild()

    DL:AddLine(imgui.ImVec2(pos.x + 15, pos.y + 15 + LogoSize.y + 15), imgui.ImVec2(pos.x + size.x - 15, pos.y + 15 + LogoSize.y + 15), imgui.GetColorU32Vec4(imgui.GetStyle().Colors[imgui.Col.Button]), 2)

    --==[ TABS ]==--
    if tab_data then
        imgui.SetCursorPos(imgui.ImVec2(LogoSize.x + 30, 15))
        for i = 1, #tab_data do
            imgui.PushStyleVarFloat(imgui.StyleVar.FrameRounding, 5)
            imgui.PushStyleColor(imgui.Col.Button, UI.tab == i and imgui.GetStyle().Colors[imgui.Col.Button] or imgui.GetStyle().Colors[imgui.Col.WindowBg])
            if imgui.Button(tab_data[i]) then UI.tab = i end
            imgui.PopStyleVar()
            imgui.PopStyleColor()
            if i < #tab_data then
                imgui.SameLine()
            end
        end
    end

    --==[ CLOSE BUTTON ]==--
    if var ~= nil then
        imgui.SetCursorPos(imgui.ImVec2(size.x - 15 - 5, 15 + 5))
        if UI.CloseButton(10, 2) then
            var[0] = false
        end
    end

    --==[ CONTENT ]==--
    imgui.SetCursorPos(imgui.ImVec2(15, 15 + LogoSize.y + 15 + 5))
    local CSize = imgui.ImVec2(size.x - 15 - 15, size.y - LogoSize.y - 15 - 15 - 15 - 5)
    imgui.BeginChild('UI_Window='..title..':Child=Content', CSize, false)
    return Begin
end

function UI.CloseButton(size, thickness, textColor, color, colorHovered, colorClicked)
    local col = {
        text = textColor or imgui.GetStyle().Colors[imgui.Col.Button],
        color = color or imgui.GetStyle().Colors[imgui.Col.Button],
        colorHovered = colorHovered or imgui.GetStyle().Colors[imgui.Col.ButtonHovered],
        colorClicked = colorClicked or imgui.GetStyle().Colors[imgui.Col.ButtonActive],
    }
    local c = imgui.GetCursorPos()
    local p = imgui.GetCursorScreenPos()
    local DL = imgui.GetWindowDrawList()
    local tx = imgui.CalcTextSize('X')
    local curX, curY = getCursorPos()
    DL:AddCircle(p, size, imgui.GetColorU32Vec4(col.color), 100, thickness)
    if curX > p.x - size and curX < p.x + size then
        if curY > p.y - size and curY < p.y + size then
            DL:AddLine(imgui.ImVec2(p.x - size + thickness, p.y - size + thickness), imgui.ImVec2(p.x + size - thickness, p.y + size - thickness), imgui.GetColorU32Vec4(col.color), thickness)
            DL:AddLine(imgui.ImVec2(p.x + size - thickness, p.y - size + thickness), imgui.ImVec2(p.x - size + thickness, p.y + size - thickness), imgui.GetColorU32Vec4(col.color), thickness)
        end
    end
    imgui.SetCursorPos(imgui.ImVec2(c.x - size, c.y - size))
    return imgui.InvisibleButton('CLOSEBUTTONBEBRA', imgui.ImVec2(size * 2, size * 2))
end
Пример использования:
1655828321556.png

Lua:
-- ТУТ КОД ИЗ СПОЙЛЕРА ВЫШЕ

imgui.OnInitialize(function()
    imgui.GetIO().IniFilename = nil
    UI.Init()
end)


local WindowFrame = imgui.OnFrame(
    function() return Window[0] end,
    function(self)
        local resX, resY = getScreenResolution()
        local sizeX, sizeY = 300, 300
        imgui.SetNextWindowPos(imgui.ImVec2(resX / 2, resY / 2), imgui.Cond.FirstUseEver, imgui.ImVec2(0.5, 0.5))
        imgui.SetNextWindowSize(imgui.ImVec2(sizeX, sizeY), imgui.Cond.FirstUseEver)
        if UI.Begin('Title', Window, 0, Tabs) then

            if UI.tab == 1 then
                imgui.Text(u8'Первая вкладка!')
            end

            UI.End()
        end
    end
)

function main()
    while not isSampAvailable() do wait(0) end
    sampRegisterChatCommand('window', function()
        Window[0] = not Window[0]
    end)
    wait(-1)
end

При вызове UI.Begin вы можете использовать следующие параметры:
titleЗаголовок окна (обязательно)
varПеременная статуса окна (обязательно)
flagsФлаги имгуи окна (необязательно, можно nil)
tab_dataВкладки (пример есть в коде) (необязательно, можно nil)
 
Последнее редактирование:

ARMOR

011110000111100101101001
Модератор
4,969
6,890
Описание: Меняет цвет текста при зажатом F5 в формате ARGB
Lua:
local memory = require 'memory'

local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x5D6C7, COLOR, true) -- Это должно быть в начале кода, не в функции main

Описание: Меняет цвет текста при создании скриншота в формате ABGR
Lua:
local memory = require 'memory'

local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x64811, COLOR, true) -- Это должно быть в начале кода, не в функции main

Описание: Меняет цвет текста подключения к серверу в формате ABGR
Lua:
local memory = require 'memory'

local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x6481B, COLOR, true) -- Это должно быть в начале кода, не в функции main

Описание: Меняет цвет названия сервера в TAB в формате ARGB
Lua:
local memory = require 'memory'

-- Это должно быть в начале кода, не в функции main
local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x6AB39, COLOR, true) -- Цвет названия сервера
memory.setuint32(samp + 0x6ABA8, COLOR, true) -- Цвет количества игроков

Описание: Меняет цвет текста в TAB в формате ARGB
Lua:
local memory = require 'memory'

-- Это должно быть в начале кода, не в функции main
local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x6ACEF, COLOR, true) -- ID
memory.setuint32(samp + 0x6ACB0, COLOR, true) -- NICK
memory.setuint32(samp + 0x6AC6D, COLOR, true) -- SCORE
memory.setuint32(samp + 0x6AC32, COLOR, true) -- PING

Описание: Меняет цвет Названия и текста в диалоге в формате ARGB
Lua:
local memory = require 'memory'

-- Это должно быть в начале кода, не в функции main
local samp = getModuleHandle("samp.dll")
memory.setuint32(samp + 0x891B0, COLOR, true) -- Title
memory.setuint32(samp + 0x6B2D2, COLOR, true) -- Text

Это для версии сампа 0.3.7 R1

Внимание: цвета подгружаются только 1 раз после инициализации сампа, т.е для смены нужно будет перезаходить. Но некоторые цвета можно менять даже во время игры, вроде это цвет текста в диалоге, и цвет текста в TAB
 
Последнее редактирование:

Rice.

Известный
Модератор
1,756
1,622
Описание: Привязка элементов к краям, углам и центрам.
Юзеры фотошопа знают функцию, которая немного примагничивает объекты во время редактирования.
Я накидал небольшой костыль для реализации этой функции в imgui/mimgui.
В примере будет показана изменение положения окна с помощью привязки.
Lua:
local sw, sh = getScreenResolution() -- Получаем разрешение экрана
local pos_x = sw / 2, sh / 2 -- Сделаем изначальное положение окна посередине экрана
local px, py = getCursorPos() -- Получаем координаты нашего курсора
local size = imgui.GetWindowSize() -- Получаем размеры нашего экрана
local sizeX, sizeY = size.x / 8, size.y / 8 -- Получаем область привязки нашего окна (чем меньше делитель, тем больше радиус работы "магнита")

if px >= (sw / 2 - sizeX) and px <= (sw / 2 + sizeX) and py >= (sh / 2 - sizeY) and py <= (sh / 2 + sizeY) then -- Проверка на центр экрана
    pos_x, pos_y = sw / 2, sh / 2
elseif py >= (sh / 2 - sizeY) and py <= (sh / 2 + sizeY) then -- Проверка на центр по Y координат
    pos_y = sh / 2
elseif px >= (sw / 2 - sizeX) and px <= (sw / 2 + sizeX) then -- Проверка на центр по X координат
    pos_x = sw / 2
end

local sizeX, sizeY = size.x / 2, size.y / 2 -- Тут не нужно играться с делителем, потому что тут мы получаем половину от размера окна

if px >= sw - sizeX then -- Проверка на выход за стороны (справа)
    pos_x = sw - sizeX
elseif px <= sizeX then -- Проверка на выход за стороны (слева)
    pos_x = sizeX
elseif py >= sh - sizeY then -- Проверка на выход за стороны (снизу)
    pos_y = sh - sizeY
elseif py <= sizeY then -- Проверка на выход за стороны (сверху)
    pos_y = sizeY
end

if px >= sw - sizeX and py >= sh - sizeY then -- Проверка на выход за углы (справа снизу)
    pos_x, pos_y = sw - sizeX, sh - sizeY
elseif px <= sizeX and py <= sizeY then -- Проверка на выход за углы (слева сверху)
    pos_x, pos_y = sizeX, sizeY
elseif px >= sw - sizeX and py <= sizeY then -- Проверка на выход за углы (справа сверху)
    pos_x, pos_y = sw - sizeX, sizeY
elseif px <= sizeX and py >= sh - sizeY then -- Проверка на выход за углы (слева снизу)
    pos_x, pos_y = sizeX, sh - sizeY
end
Пример использования:
 

Вложения

  • window.lua
    3.1 KB · Просмотры: 19

ARMOR

011110000111100101101001
Модератор
4,969
6,890
Описание: Список адресов памяти из samp.dll отвечающих за цвет елементов для Версии сампа 0.3.7 R3-1.

Lua:
hpHight = 0x6CA7C -- Цвет передней части ХП у других игроков
hpLow = 0x6CAA3 -- Цвет задней части ХП у других игроков
ArmorHight = 0x6CD45 -- Цвет передней части брони у других игроков
ArmorLow = 0x6CD70 -- Цвет задней части брони у других игроков
hostname = 0x6EA89 -- Цвет названия сервера в TAB
players = 0x6EAF8 -- Цвет количества игроков в TAB
id = 0x6EB82 -- Цвет надписи id в TAB
name = 0x6EBBD -- Цвет надписи name в TAB
score = 0x6EC00 -- Цвет надписи score в TAB
ping = 0x6EC3F -- Цвет надписи ping в TAB
scrollbar = 0x8D197 -- Цвет ползунка в диалогах
stButtonText = 0x8D1BE -- Цвет текста в кнопках диалогов
hvButtonText = 0x8D1B6 -- Цвет текста в наведенных кнопках диалогов
stButtonOutline = 0x8D14E -- Цвет обводки кнопок диалогов
clButtonOutline = 0x8D1A7 -- Цвет обводки наведенных кнопок диалогов
dialogHeader = 0x8D0D0 -- Цвет стандартного названия диалогов
dialogText = 0x6F1D2 -- Цвет стандартного цвета
systemMsgs = 0x67C61 -- Цвет текста подключения к серверу
screenClass = 0x67C6B -- Цвет текста скриншота
netstats = 0x60A64 -- Цвет первой строчки в F5
dl3dtext = 0x751D1 -- Цвет текста /dl
sampStarted = 0xE596D -- Цвет текста старта сампа
sampStarted2 = 0xE598C -- Цвет текста старта сампа2
sampStartedVer = 0xE597B -- Цвет текста версии сампа при старте

Lua:
hpHigh: 0xFFB92228
hpLow: 0xFF4B0B14
armorHigh: 0xFFC8C8C8
armorLow: 0xFF282828
hostname: 0xFFBEBEBE
players: 0xFFBEBEBE
id: 0xFF95B0D0
name: 0xFF95B0D0
score: 0xFF95B0D0
ping: 0xFF95B0D0
scrollbar: 0xFFB92228
stButtonText: 0xFFC8C8C8
hvButtonText: 0xFFFFFFFF
stButtonOutline: 0xFFC8C8C8
clButtonOutline: 0xFF95B0D0
dialogHeader: 0xFFD2D2D2
dialogText: 0xFFA9C4E4
systemMsgs: 0xFFA9C4E4
screenClass: 0xFF88AA62
netstats: 0xFF8888EE
dl3dtext: 0xFF358BD4
sampStarted: 0x46464646
sampStarted2: 0x46464646
sampStartedVer: 0x39433942
connectedTo: 0x39433942

Способ применения:
Lua:
local memory = require 'memory'

local samp = getModuleHandle("samp.dll")
memory.setuint(samp + 0x6CA7C, 0xFF00FF00, true)

sa-mp-016.png

Внимание: Большинство цветов подгружаются только 1 раз после инициализации сампа, т.е для смены нужно будет перезаходить. Но некоторые цвета можно менять даже во время игры, вроде это цвет текста в диалоге, и цвет текста в TAB
 
Последнее редактирование:

Gorskin

♥ Love Lua ♥
Проверенный
1,339
1,178
Описание: Убирает лимит на отображение денег в худе, теперь вы сможете увидеть миллиарды. Украл у Black Jesus.
Screenshot_1.png

Lua:
writeMemory(0x571784, 4, 0x57C7FFF, false)
writeMemory(0x57179C, 4, 0x57C7FFF, false)

-- 0x57C7FFF - патч
-- 0x57C3B9A - стандарт значение
 
Последнее редактирование:

ARMOR

011110000111100101101001
Модератор
4,969
6,890
Описание функции: Отключает\Включает только чат, а не: чат, худ, миникарту как при нажатии F7

function HideChat()
local samp = getModuleHandle("samp.dll")
memory.fill(samp + 0x63DA0, 0x90909090, 4, true)
sampAddChatMessage("Чат отключен!", -1)
end

function UnHideChat()
local samp = getModuleHandle("samp.dll")
memory.fill(samp + 0x63DA0, 0x0A000000, 4, true)
sampAddChatMessage("Чат включен!", -1)
end

function HideChat()
local samp = getModuleHandle("samp.dll")
memory.fill(samp + 0x671F0, 0x90909090, 4, true)
sampAddChatMessage("Чат отключен!", -1)
end

function UnHideChat()
local samp = getModuleHandle("samp.dll")
memory.fill(samp + 0x671F0, 0x0A000000, 4, true)
sampAddChatMessage("Чат включен!", -1)
end

Пример использования:
Lua:
local memory = require 'memory'
local enable = false
local samp = getModuleHandle("samp.dll")

function main()
    if not isSampfuncsLoaded() or not isSampLoaded() then return end
    while not isSampAvailable() do wait(100) end

    while true do
        wait(0)
        if testCheat("OO") then
            HideChat()
        end
    end
end

function HideChat()
    enable = not enable
    if enable then
        memory.fill(samp + 0x671F0, 0x90909090, 4, true)
        sampAddChatMessage("Чат отключен!", -1)
    else
        memory.fill(samp + 0x671F0, 0x0A000000, 4, true)
        sampAddChatMessage("Чат включен!", -1)
    end
end

Внимание: Что-бы чат включился/отключился нужно что-бы появилось новое сообщение в чате( Типо чат обновился )
 
  • Нравится
Реакции: Gorskin

Smeruxa

Известный
1,371
726
Описание функции: Преобразовывает тёмный цвет в оттенок белого.
(Принимает 2 аргумента. Цвет и порог срабатывания преобразования, например, если цвет достаточно светлый, то у него яркость ~ >70, а значит порог срабатывания будет 70, чтобы преобразовать в светлый оттенок. Чем выше значение - тем темнее будет оттенок, возможно станет уже не белым. Не рекомендуется повышать значение больше чем 100. )

Lua:
function to_white(color, limit_br)

    local function join_argb(a, r, g, b)
        local argb = b
        argb = bit.bor(argb, bit.lshift(g, 8))
        argb = bit.bor(argb, bit.lshift(r, 16))
        argb = bit.bor(argb, bit.lshift(a, 24))
        return argb
    end
 
    local function explode_argb(argb)
        local a = bit.band(bit.rshift(argb, 24), 0xFF)
        local r = bit.band(bit.rshift(argb, 16), 0xFF)
        local g = bit.band(bit.rshift(argb, 8), 0xFF)
        local b = bit.band(argb, 0xFF)
        return a, r, g, b
    end

    -- LOGIC

    local a, r, g, b = explode_argb(color)
    local brightness = r + g + b
    local result = join_argb(255, r, g, b)
    if brightness < limit_br then
        result = join_argb(255, 255 - r, 255 - g, 255 - b)
    end

    -- LOGIC

    return result
end

Пример использования:
Lua:
require 'lib.moonloader'

function to_white(color, limit_br)
    local function join_argb(a, r, g, b)
        local argb = b
        argb = bit.bor(argb, bit.lshift(g, 8))
        argb = bit.bor(argb, bit.lshift(r, 16))
        argb = bit.bor(argb, bit.lshift(a, 24))
        return argb
    end
 
    local function explode_argb(argb)
        local a = bit.band(bit.rshift(argb, 24), 0xFF)
        local r = bit.band(bit.rshift(argb, 16), 0xFF)
        local g = bit.band(bit.rshift(argb, 8), 0xFF)
        local b = bit.band(argb, 0xFF)
        return a, r, g, b
    end

    -- LOGIC

    local a, r, g, b = explode_argb(color)
    local brightness = r + g + b
    local result = join_argb(255, r, g, b)
    if brightness < limit_br then
        result = join_argb(255, 255 - r, 255 - g, 255 - b)
    end

    -- LOGIC

    return result
end

function main()
    while not isSampLoaded() do wait(100) end
    sampRegisterChatCommand("beg", function()
        sampAddChatMessage("test", 0xFF291818)
    end)
    sampRegisterChatCommand("res", function()
        sampAddChatMessage("test", to_white(0xFF291818, 85))
    end)
    wait(-1)
end
 
Последнее редактирование:
  • Влюблен
Реакции: Double Tap Inside

qrlk

Известный
Друг
411
928
Описание: Простенькая интеграция для сбора ошибок lua скрипта в sentry.

Подгружает фоновой скрипт (из создаваемого временного файла), который ловит ошибки родителя через onSystemMessage и отправляет их в Sentry через https://develop.sentry.dev/sdk/event-payloads/ в формате, близком к тому, как это было задумано разработчиками.
Сильно я не углублялся, но основные вещи вроде парсинга stack traceback из лога, идентификации юзеров (можно смотреть сколько пользователей затронула ошибка) и тегов есть. Ошибки группируются по stack traceback, а если есть сампфункс, то отправит ещё и gamestate, ip и name сервера, дополнит юзера ником.

После завершения работы родителя, фоновый скрипт выгружается через 60 секунд.

Это версия 0.0.0 и обновлять я её не планирую (так как всё что мне было нужно я уже сделал).

Особых зависимостей нет: из библиотек используется encoding для кодирования в utf-8 (распространяется с moonloader).

после script_name и script_version:
-- https://github.com/qrlk/qrlk.lua.moonloader
local enable_sentry = true -- false to disable error reports to sentry.io
if enable_sentry then
  local sentry_loaded, Sentry = pcall(loadstring, [=[return{init=function(a)local b,c,d=string.match(a.dsn,"https://(.+)@(.+)/(%d+)")local e=string.format("https://%s/api/%d/store/?sentry_key=%s&sentry_version=7&sentry_data=",c,d,b)local f=string.format("local target_id = %d local target_name = \"%s\" local target_path = \"%s\" local sentry_url = \"%s\"\n",thisScript().id,thisScript().name,thisScript().path:gsub("\\","\\\\"),e)..[[require"lib.moonloader"script_name("sentry-error-reporter-for: "..target_name.." (ID: "..target_id..")")script_description("Этот скрипт перехватывает вылеты скрипта '"..target_name.." (ID: "..target_id..")".."' и отправляет их в систему мониторинга ошибок Sentry.")local a=require"encoding"a.default='CP1251'local b=a.UTF8;local c="moonloader"function getVolumeSerial()local d=require"ffi"d.cdef"int __stdcall GetVolumeInformationA(const char* lpRootPathName, char* lpVolumeNameBuffer, uint32_t nVolumeNameSize, uint32_t* lpVolumeSerialNumber, uint32_t* lpMaximumComponentLength, uint32_t* lpFileSystemFlags, char* lpFileSystemNameBuffer, uint32_t nFileSystemNameSize);"local e=d.new("unsigned long[1]",0)d.C.GetVolumeInformationA(nil,nil,0,e,nil,nil,nil,0)e=e[0]return e end;function getNick()local f,g=pcall(function()local f,h=sampGetPlayerIdByCharHandle(PLAYER_PED)return sampGetPlayerNickname(h)end)if f then return g else return"unknown"end end;function getRealPath(i)if doesFileExist(i)then return i end;local j=-1;local k=getWorkingDirectory()while j*-1~=string.len(i)+1 do local l=string.sub(i,0,j)local m,n=string.find(string.sub(k,-string.len(l),-1),l)if m and n then return k:sub(0,-1*(m+string.len(l)))..i end;j=j-1 end;return i end;function url_encode(o)if o then o=o:gsub("\n","\r\n")o=o:gsub("([^%w %-%_%.%~])",function(p)return("%%%02X"):format(string.byte(p))end)o=o:gsub(" ","+")end;return o end;function parseType(q)local r=q:match("([^\n]*)\n?")local s=r:match('^.+:%d+: (.+)')return s or"Exception"end;function parseStacktrace(q)local t={frames={}}local u={}for v in q:gmatch("([^\n]*)\n?")do local w,x=v:match("^    *(.:.-):(%d+):")if not w then w,x=v:match("^    *%.%.%.(.-):(%d+):")if w then w=getRealPath(w)end end;if w and x then x=tonumber(x)local y={in_app=target_path==w,abs_path=w,filename=w:match("^.+\\(.+)$"),lineno=x}if x~=0 then y["pre_context"]={fileLine(w,x-3),fileLine(w,x-2),fileLine(w,x-1)}y["context_line"]=fileLine(w,x)y["post_context"]={fileLine(w,x+1),fileLine(w,x+2),fileLine(w,x+3)}end;local z=v:match("in function '(.-)'")if z then y["function"]=z else local A,B=v:match("in function <%.* *(.-):(%d+)>")if A and B then y["function"]=fileLine(getRealPath(A),B)else if#u==0 then y["function"]=q:match("%[C%]: in function '(.-)'\n")end end end;table.insert(u,y)end end;for j=#u,1,-1 do table.insert(t.frames,u[j])end;if#t.frames==0 then return nil end;return t end;function fileLine(C,D)D=tonumber(D)if doesFileExist(C)then local E=0;for v in io.lines(C)do E=E+1;if E==D then return v end end;return nil else return C..D end end;function onSystemMessage(q,F,i)if i and F==3 and i.id==target_id and i.name==target_name and i.path==target_path and not q:find("Script died due to an error.")then local G={tags={moonloader_version=getMoonloaderVersion(),sborka=string.match(getGameDirectory(),".+\\(.-)$")},level="error",exception={values={{type=parseType(q),value=q,mechanism={type="generic",handled=false},stacktrace=parseStacktrace(q)}}},environment="production",logger=c.." (no sampfuncs)",release=i.name.."@"..i.version,extra={uptime=os.clock()},user={id=getVolumeSerial()},sdk={name="qrlk.lua.moonloader",version="0.0.0"}}if isSampAvailable()and isSampfuncsLoaded()then G.logger=c;G.user.username=getNick().."@"..sampGetCurrentServerAddress()G.tags.game_state=sampGetGamestate()G.tags.server=sampGetCurrentServerAddress()G.tags.server_name=sampGetCurrentServerName()else end;print(downloadUrlToFile(sentry_url..url_encode(b:encode(encodeJson(G)))))end end;function onScriptTerminate(i,H)if not H and i.id==target_id then lua_thread.create(function()print("скрипт "..target_name.." (ID: "..target_id..")".."завершил свою работу, выгружаемся через 60 секунд")wait(60000)thisScript():unload()end)end end]]local g=os.tmpname()local h=io.open(g,"w+")h:write(f)h:close()script.load(g)os.remove(g)end}]=])
  if sentry_loaded and Sentry then
    --replace "https://public@sentry.example.com/1" with your DSN obtained from sentry.io after you create project
    --https://docs.sentry.io/product/sentry-basics/dsn-explainer/#where-to-find-your-dsn
    pcall(Sentry().init, { dsn = "https://public@sentry.example.com/1" })
  end
end


Пример использования:
  • Копируем код в начало вашего скрипта (после script_name и script_version).
  • Регистрируемся на sentry.io (5К ошибок/месяц бесплатно) или используем self-hosted версию.
    • Кривую русификацию интерфейса можно включить тут.
  • Создаём проект.
  • Копируем DSN и заменяем им "https://public@sentry.example.com/1" в коде.
  • Запускаем, вызываем ошибку через функцию error().
  • Изучаем ошибку на сайте.
пример вызова ошибки, нужно поменять DSN:
require 'lib.moonloader'

script_name("moonloader sentry sdk example")
script_version("0.0.0")
script_url("https://github.com/qrlk/qrlk.lua.moonloader")

-- https://github.com/qrlk/qrlk.lua.moonloader
local enable_sentry = true -- false to disable error reports to sentry.io
if enable_sentry then
  local sentry_loaded, Sentry = pcall(loadstring, [=[return{init=function(a)local b,c,d=string.match(a.dsn,"https://(.+)@(.+)/(%d+)")local e=string.format("https://%s/api/%d/store/?sentry_key=%s&sentry_version=7&sentry_data=",c,d,b)local f=string.format("local target_id = %d local target_name = \"%s\" local target_path = \"%s\" local sentry_url = \"%s\"\n",thisScript().id,thisScript().name,thisScript().path:gsub("\\","\\\\"),e)..[[require"lib.moonloader"script_name("sentry-error-reporter-for: "..target_name.." (ID: "..target_id..")")script_description("Этот скрипт перехватывает вылеты скрипта '"..target_name.." (ID: "..target_id..")".."' и отправляет их в систему мониторинга ошибок Sentry.")local a=require"encoding"a.default='CP1251'local b=a.UTF8;local c="moonloader"function getVolumeSerial()local d=require"ffi"d.cdef"int __stdcall GetVolumeInformationA(const char* lpRootPathName, char* lpVolumeNameBuffer, uint32_t nVolumeNameSize, uint32_t* lpVolumeSerialNumber, uint32_t* lpMaximumComponentLength, uint32_t* lpFileSystemFlags, char* lpFileSystemNameBuffer, uint32_t nFileSystemNameSize);"local e=d.new("unsigned long[1]",0)d.C.GetVolumeInformationA(nil,nil,0,e,nil,nil,nil,0)e=e[0]return e end;function getNick()local f,g=pcall(function()local f,h=sampGetPlayerIdByCharHandle(PLAYER_PED)return sampGetPlayerNickname(h)end)if f then return g else return"unknown"end end;function getRealPath(i)if doesFileExist(i)then return i end;local j=-1;local k=getWorkingDirectory()while j*-1~=string.len(i)+1 do local l=string.sub(i,0,j)local m,n=string.find(string.sub(k,-string.len(l),-1),l)if m and n then return k:sub(0,-1*(m+string.len(l)))..i end;j=j-1 end;return i end;function url_encode(o)if o then o=o:gsub("\n","\r\n")o=o:gsub("([^%w %-%_%.%~])",function(p)return("%%%02X"):format(string.byte(p))end)o=o:gsub(" ","+")end;return o end;function parseType(q)local r=q:match("([^\n]*)\n?")local s=r:match('^.+:%d+: (.+)')return s or"Exception"end;function parseStacktrace(q)local t={frames={}}local u={}for v in q:gmatch("([^\n]*)\n?")do local w,x=v:match("^ *(.:.-):(%d+):")if not w then w,x=v:match("^ *%.%.%.(.-):(%d+):")if w then w=getRealPath(w)end end;if w and x then x=tonumber(x)local y={in_app=target_path==w,abs_path=w,filename=w:match("^.+\\(.+)$"),lineno=x}if x~=0 then y["pre_context"]={fileLine(w,x-3),fileLine(w,x-2),fileLine(w,x-1)}y["context_line"]=fileLine(w,x)y["post_context"]={fileLine(w,x+1),fileLine(w,x+2),fileLine(w,x+3)}end;local z=v:match("in function '(.-)'")if z then y["function"]=z else local A,B=v:match("in function <%.* *(.-):(%d+)>")if A and B then y["function"]=fileLine(getRealPath(A),B)else if#u==0 then y["function"]=q:match("%[C%]: in function '(.-)'\n")end end end;table.insert(u,y)end end;for j=#u,1,-1 do table.insert(t.frames,u[j])end;if#t.frames==0 then return nil end;return t end;function fileLine(C,D)D=tonumber(D)if doesFileExist(C)then local E=0;for v in io.lines(C)do E=E+1;if E==D then return v end end;return nil else return C..D end end;function onSystemMessage(q,F,i)if i and F==3 and i.id==target_id and i.name==target_name and i.path==target_path and not q:find("Script died due to an error.")then local G={tags={moonloader_version=getMoonloaderVersion(),sborka=string.match(getGameDirectory(),".+\\(.-)$")},level="error",exception={values={{type=parseType(q),value=q,mechanism={type="generic",handled=false},stacktrace=parseStacktrace(q)}}},environment="production",logger=c.." (no sampfuncs)",release=i.name.."@"..i.version,extra={uptime=os.clock()},user={id=getVolumeSerial()},sdk={name="qrlk.lua.moonloader",version="0.0.0"}}if isSampAvailable()and isSampfuncsLoaded()then G.logger=c;G.user.username=getNick().."@"..sampGetCurrentServerAddress()G.tags.game_state=sampGetGamestate()G.tags.server=sampGetCurrentServerAddress()G.tags.server_name=sampGetCurrentServerName()else end;print(downloadUrlToFile(sentry_url..url_encode(b:encode(encodeJson(G)))))end end;function onScriptTerminate(i,H)if not H and i.id==target_id then lua_thread.create(function()print("скрипт "..target_name.." (ID: "..target_id..")".."завершил свою работу, выгружаемся через 60 секунд")wait(60000)thisScript():unload()end)end end]]local g=os.tmpname()local h=io.open(g,"w+")h:write(f)h:close()script.load(g)os.remove(g)end}]=])
  if sentry_loaded and Sentry then
 --replace "https://public@sentry.example.com/1" with your DSN obtained from sentry.io after you create project
 --https://docs.sentry.io/product/sentry-basics/dsn-explainer/#where-to-find-your-dsn
    pcall(Sentry().init, { dsn = "https://public@sentry.example.com/1" })
  end
end

error("ERROR")
1656325050784.png

1656325751638.png

1656325802206.png

1656325965561.png

1656325851163.png

1656325877855.png

1656325896806.png