Changelog

Co se změnilo

Kompletní seznam změn po verzích, generovaný přímo z vývojářského changelogu. Je psaný technicky a jen česky — pro rychlý přehled použij filtr.

v1.0.0

ve vývoji
234 položek

Bezpečnost

fixupdatemanifest bez sha256 se už neinstaluje

Chybějící digest byl dosud jen SP_LOG_WARN a instalace pokračovala - takže z ověření zbylo „je to 32bit DLL nad 64 KB". Nebyla to teorie: publikovaná web/version.json měla sha256: "" a size: 0, takže auto-download (default ON) běžel v produkci bez kontroly obsahu. Teď se fail-closed odmítne a do logu jde, že jde o chybu na straně toho, kdo manifest publikuje.

fixupdateupdate_url musí být https

HTTPS se vynucovalo jen u .asi, jenže manifest určuje jak digest, tak URL souboru - kdo ho přepíše na cestě, vybere instalované bajty, a kontrola https na downloadu pak znamená jen to, že útočníkův vlastní host potřebuje certifikát. Platí i pro /spupdate, který ini čte znovu.

fixupdatepřetečení v kontrole PE hlavičky staženého buildu

peOff + 24u > b.size() se na 32bitovém buildu zabalilo: pro e_lfanew = 0xFFFFFFF0 je součet 8, kontrola projde a čte se ~4 GB za bufferem. Nově se porovnává odečtením.

featnuiURL politika pro stránky, které určuje server

NUI vrstvy (SP_UiOpen) i world screens dosud přijaly cokoliv, co nebylo prázdné, a vyrenderovaly to v procesu hry. Nově https-only (včetně interního https://sp.local/), file:///data:/blob:/ javascript: vždy odmítnuto, http:// jen s nui_allow_insecure = 1 (LAN testy) a volitelný nui_allowed_hosts. Klientovy VLASTNÍ stránky (loadscreen z ini) se needitují - hráč volící si vlastní file:/// je jiné rozhodnutí než server volící ho za něj.

featcefzablokované popupy a stahování

OnBeforePopup vrací true (off-screen browser nemá kam popup dát, takže by to bylo jen neviditelné okno) a OnBeforeDownload nevolá callback, čímž download ruší - jedna z mála cest, kterou se stránka dostane na disk.

featassetskvóta a LRU eviction na cache

(asset_cache_max_mb, default 2 GB, 0 = bez limitu). Cache je sdílená přes všechny servery a dosud z ní nikdy nic nemizelo; server, který verzuje assety, zaplnil hráči disk. Vyhazuje se nejdéle nepoužívané (cache hit se počítá jako použití), plus nové CacheManager::Clear().

chorecipermissions: contents: read

workflow jen čte, ale dědil výchozí (často read/write) práva tokenu.

Přidáno

featclothesserver obleče hlavní postavu (SP_SetPlayerClothes, EventCmd::Clothes, SP_CAP_CLOTHES)

CJ je jediný ped v GTA:SA, jehož tělo není hotový model: model 0 hra skládá za běhu z kusů oblečení podle CPedClothesDesc (18 texture keys + 10 model keys) zavěšeného na CPlayerPed. SA:MP ten deskriptor nikdy neodhalil, takže „hráč má uniformu" dosud znamenalo spálit celé skin ID. Plugin do něj zapíše (CPedClothesDesc::SetTextureAndModel, 0x5A8050) a nechá hru tělo složit znovu (CClothes::RebuildPlayer, 0x5A82C0) - stejný pár volání jako šatník v singleplayeru, tedy opcody 087B + 070D. 18 částí včetně devíti tetovacích slotů, čepic, brýlí a hodinek. Nic se nepřenáší: "VEST" je jméno v hráčově vlastním models\player.img, takže klient s modovaným oblečením vidí svou verzi - žádný asset transfer. Klient si ohoz drží. Ohoz se v SA:MP rozpadá ze tří důvodů, a všechny tři jsou úplně běžné: respawn (SA:MP zahodí a znovu vytvoří lokálního CPlayerPed a ten nový má výchozí deskriptor), změna skinu, a cokoli dalšího, co do deskriptoru sáhne. Kdyby se jen jednou zapsalo, spadl by ohoz z hráče při prvním umření. Proto je žádaný stav klientský: Tick() 4x za sekundu porovná živý deskriptor s tím, co má být, a při neshodě přeobleče - stejný tvar, jakým HandlingManager přežívá re-stream vozidla. Server posílá ohoz jednou a OnPlayerSpawn se ho netýká. Přestavba se slučuje. ConstructPedModel končí v CStreaming::LoadAllRequestedModels, takže rebuild blokuje, dokud se kusy nenatáhnou; šest částí poslaných v jednom ticku je proto JEDNA přestavba a mezi dvěma je podlaha 250 ms - server, který Set spamuje, stojí hráče přestavbu za čtvrt sekundy, ne jednu na paket. Sundání není díra v těle. Klient si deskriptor odfotí před prvním zápisem, takže SP_ClearPlayerClothes vrací hráči jeho vlastní výchozí stav - sundání čepice vrátí vlasy. Ze stejného snímku se skládá i každý apply (snapshot → přepsat overrides → rebuild), takže Set i Clear jsou jedna cesta kódem bez mezistavů. Ohoz poslaný hráči, který zrovna nemá skin 0, se nezahazuje - klient si ho nechá a obleče ho, jakmile hráč zase nosí CJ; skript se to dozví jako SP_CLOTHES_NOT_CJ v OnPlayerClothesSP. Natives SP_SetPlayerClothes / SP_SetPlayerTattoo / SP_SetPlayerOutfit / SP_ClearPlayerClothes / SP_RebuildPlayerClothes + SP_IsPlayerWearing / SP_GetPlayerClothesMask, vypínač clothes = 0. Viz docs/clothing.md.

featclothestloušťka a síla postavy (SP_SetPlayerBodyStats)

Poslední dvě pole téhož deskriptoru, a skládačka mezi nimi prolíná tělo i každý kus oblečení - tričko na tlusté postavě je tlustá varianta toho trička, ne roztažená. Nejde je u nositele zapsat do deskriptoru, a to je celý vtip: CPlayerPed::ProcessControl volá CClothes::RebuildPlayerIfNeeded (0x5A8390), ta obě pole porovná s CStats a při rozdílu tělo přestaví - ze statů. Zapsaná hodnota by se tedy příští snímek přepsala zpátky a hra by se navíc přestavovala pořád dokola. Držící páka jsou samotné staty: klient píše rovnou do CStats::StatTypesFloat (0xB79380, [21] fat, [23] muscle), ne přes CStats::SetStatValue - ta končí v CheckForStatsMessage, tedy v singleplayerové HUD hlášce „stat ti stoupl", která nemá co dělat s tím, že server řekl, jak postava vypadá. Zápisem statu se hra přestaví sama a v souladu se svým stavem; klient hodnotu znovu prosazuje z Tick(), takže ji gymový skript ani herní jedení nepřebijí. U ostatních hráčů je to naopak: jejich tělo se skládá ConstructPedModel do privátního model slotu a nikdy neprojde RebuildPlayer, takže ho nemá co resyncovat - tam jdou obě hodnoty rovnou do deskriptoru. A dává to smysl, CStats na mém stroji jsou o mně, ne o tom, koho vidím. Rozsah 0..1000 (SP_BODY_STAT_MIN/MAX), mimo něj ořez. Demo: /fat /ripped /skinny.

featclothesoblečení vidí i ostatní hráči (SP_OnPlayerStreamIn, ClothesOp::SetPeer)

Oblékání výš řeší JEDNOHO hráče na JEHO obrazovce. Aby ho tak viděli ostatní, nemůže to jít přes deskriptor: skládačka zapisuje do model slotu, takže mi->SetClump() na modelu 0 znamená, že model 0 drží v jednu chvíli jedno tělo a každý ped z něj vzniklý si ho jen naklonuje (CClumpModelInfo::CreateInstanceRpClumpClone). Dva různě oblečení CJ z něj vyjít nemůžou. A tím se opravuje bug, který zavlekla předchozí položka: obléknout lokálního hráče zapsalo jeho ohoz do modelu 0, takže každý další CJ, co se streamnul, si ho naklonoval - cizí člověk na ulici v tričku, které sis právě koupil. Klient teď po každém lokálním apply vrátí šablonu modelu 0 na default (RestoreCjTemplateBody), za cenu jedné assembly navíc. Cizí ohoz se místo toho staví do privátního ped model slotu (klon modelu 0) navázaného na player id, a substituci dělá ta samá mechanika jako per-instance ped skiny - AssetManager přepíše model id v RPC 32 (stream-in) a 153 (SetPlayerSkin). Klony jsou označené jako self-managed, aby jim DFF recovery cesta nepřehrála oblečení stock CJčkem. Sloty jsou globálně vzácné, takže strop 16 naráz a LRU recyklace - a recyklace, ne uvolnění: uvolnit slot, ze kterého je instancovaný živý ped, nechá jeho m_nModelIndex ukazovat na smazaný CPedModelInfo, do kterého hra čte každý snímek. Sestavení blokuje na LoadAllRequestedModels, takže max jedno cizí tělo za tick - autobus s dvanácti oblečenými hráči nesmí být dvanáct assembly v jednom snímku. Server ohoz zrcadlí (packed jména, SP_CLOTHES_SYNC_SLOTS částí na hráče), rozesílá změny streamnutým plugin peerům a přes SP_OnPlayerStreamIn / SP_OnPlayerStreamOut řeší příchozí i odchozí. SP_RefreshPlayerClothes udělá změnu viditelnou na už postaveném pedovi přenastavením téhož skinu - klientský rebuild peda tu schválně není, holé DeleteRwObject + CreateRwObject nechá clothing geometrii rozestavěnou a spadne na 0x5E5815 (viz poznámka v renderware.cpp).

featcapturečervená tečka, když se natáčí

Klávesa na video je přepínač a na obrazovce nebylo nic, co by řeklo, ve kterém stavu je - takže se v rohu rozsvítí tepající červená tečka. Fade-in i fade-out (0,35 / 0,45 s) a dýchání s podlahou na 45 %, aby to nikdy neproblikávalo do prázdna; pod tečkou je měkké tmavé halo, protože samotná červená zmizí na bílé obloze. Škáluje s rozlišením - fixních 7 px je na 3440x1440 skoro neviditelných. Kreslí se AŽ ZA záznamem, schválně: Present jede PostFX → NUI → overlay → capture, a capture bere backbuffer jako poslední právě proto, aby v klipu bylo to, co hráč vidí. Kdyby tečka šla s HUDem, byla by vypálená v každém snímku videa - tedy na jediném místě, kde být nesmí. Proto má vlastní průchod a vlastní save/restore stavu (ne CreateStateBlock, ten D3D9 mezi BeginScene a EndScene zakazuje - stejná past, kterou dokumentuje PostFX).

featactorsauto se dá po trase PŘEHRÁT, ne jen odřídit (SP_ActorDriveVehiclePath, ActorCmd::DrivePath, SP_CAP_VEHICLE_PATH)

Zpráva zněla „to auto se v těch move kamerách viditelně seká" a odpověď na ni není lepší řízení - SP_ActorDriveVehicleTo předá auto jízdní AI GTA, a ta umí jediné: dojet. Nikdy neslíbí, že dojede včas, protože jede po path nodech, brzdí do zatáček, objíždí provoz, občas se prohlásí za zaseknutou a couvne, a příjezd hlásí s rádiusem 8 m. Scéna, kde kamera střihne na křižovatku ve 23,3 s, potřebuje přesně ten druhý slib. Skripty si proto vypomáhaly serverovým timerem, který nad tou AI píše SetVehiclePos + SetVehicleZAngle desetkrát za sekundu - jenže to nejsou hodinky, to je druhý vlastník téže matice, a souboj dvou vlastníků o jednu pozici je právě to, co na obrazovce vypadá jako trhání. Nově se trasa pošle jednou a klient po ní auto přehraje snímek po snímku jako čistou funkci uplynulého času: hladké při jakémkoli FPS, na každém klientovi stejné bez jediného sync paketu (pozice je deterministická, takže se trasa broadcastuje a póza se během ní ignoruje), a na křižovatce ve chvíli, kdy tam kamera střihne. Volitelný per-waypoint rozvrh (SP_ActorDriveVehiclePathTimed) je ten nástroj na svázání auta s kamerou; interpoluje se monotónní kubikou, takže mezi dvěma blízkými klíči nemůže vzniknout přestřel, po kterém auto uprostřed záběru couvne. Auto je po dobu jízdy ve stejném statusu, do kterého ho dává sama hra, když přehrává .rrr dráhy z carrec.img (STATUS_PLAYER_PLAYBACK) - takže ho fyzika netahá dolů, nekopne s ním kolize a nesebere mu volant provozní AI; původní status se po dojetí vrací. Detaily, wire i limity v docs/vehicle-paths.md, matematika samostatně a otestovaná mimo hru (src/network/vehicle_path.h, tests/test_vehicle_path.cpp).

  • Zatáčky se projíždějí zevnitř (Bézierovo zaoblení rohu), ne splinem skrz waypointy. Spline byl první verze a je to obvyklá volba: opouští waypoint ve směru jeho sousedů, takže pravoúhlou křižovatku projede pod 45° a musí se vyhoupnout ven. Naměřeno na trase z INTRO to bylo až 8,7 m mimo spojnici waypointů - ulice v San Andreas je asi 12 m široká, takže policejní auto zatáčelo skrz protější barák. Se zaoblením je to na téže trase pod 3 m, což je chyba, kterou dělá i skutečné auto.
  • feat(missions): missions/sp_intro.inc - „In the Beginning", tedy PROLOG1 → PROLOG3 → projížďka policejního auta Jeffersonem (588 m za 83 s, klíčovaná na 19 záběrů kamery) → vysazení u BMX. První uživatel nové trasy; zapojená do sp_crash_test.pwn jako /intro, aby ji něco překládalo a dalo se to spustit.
featoverlaylimit snímků jde nastavit posuvníkem (ALT+S → Opravy, pod „Odemknout FPS i v SA:MP")

Návrh od hráče. /fpslimit existuje odjakživa a plugin si hodnotu už pamatoval mezi sezeními, chybělo jen ji vidět a hýbat s ní bez psaní příkazu. Pod tím je stejný setter, který volá samotný SA:MP, takže posuvník a příkaz jsou jedno nastavení - hodnota napsaná do chatu se v dialogu objeví sama a naopak. Rozsah 20–144: návrh mluvil o 20–100, ale plugin schválně sundává vlastní strop SA:MP na 90 FPS (oprava „Odemknout FPS v SA:MP"), takže dial končící na stovce by ten strop potichu vrátil zrovna těm, kvůli kterým ta oprava je. Do ini se zapisuje až po puštění posuvníku, ne během tažení. Sedí schválně pod tou opravou a dokud není zapnutá, je zašedlý: posuvník píše přes vlastní setter SA:MP a plugin ho umí najít jen díky té opravě (hledá ho podle signatury). Bez ní není kam zapisovat, takže živě vypadající dial v jiné záložce nabízel ovládání, které potichu nedělalo nic. Takhle je ta závislost vidět z rozložení, ne z věty. Dialog navíc přečte volbu Frame Limiter z nastavení hry a když je vypnutá, řekne to a nabídne ji zapnout: s vypnutou SA:MP tenhle limit úplně ignoruje a jede si po vlastní zadrátované větvi - takže by posuvník zdánlivě nedělal nic.

Odebráno

chorediagpryč /spveh a /spcol

Obojí byly dočasné sondy k lovu na mizející kolo a na kolize po reskinu; obě otázky jsou zodpovězené (viz opravy odpružení a lagcompu níž), takže tu zůstávaly jen jako příkazy, které nikdo nemá důvod psát. Zjištění, kvůli kterým vznikly, zůstávají v komentářích v renderware.cpp.

Opraveno

fixactorsSetVehicleZAngle (RPC 160) se konečně taky pozoruje

Plugin dlouho hlídal jen SetVehiclePos (159), a to hlídání bylo tím pádem asymetrické - přesně v tom případě, kvůli kterému existuje. Serverová evidence pózy (SP_HandleActorPos) posílá při příjezdu obojí najednou: pozice se pečlivě spolkla a natočení prošlo, takže synchronizované auto dostávalo vteřinu staré natočení, zatímco tělem jelo dál. Skript, který si natočení psal sám - interpolační timer volající SetVehicleZAngle desetkrát za sekundu nad klientem řízeným autem, což je způsob, jakým byla poprvé napsaná projížďka z INTRO - z toho udělal tvrdý zásek desetkrát za sekundu. Konzumuje se podle stejného pravidla jako pozice; u úhlu se nedělá test na „to je zjevně teleport", protože na rozdíl od souřadnice je každý úhel legální úhel.

fixactorsSetVehiclePos s nekonečnou souřadnicí se zahodí a zaloguje

NaN dělal všechna porovnání v testu na echo nepravdivá, takže packet propadl do větve „je blízko" a potichu se spolkl - tedy se schoval místo aby se nahlásil. Serverová strana to kontrolovala už dřív.

fixdiagcrash-probe hlásil jako pád něco, co je záměr

V logu se objevilo [ERR] CRASH-PROBE: AV read @ data=0x00000300 s celým stackem - a hra přitom nespadla a spadnout nemohla. RawProbeReadable zjišťuje čitelnost ukazatele jediným způsobem, který si per-frame cesta může dovolit: přečte ho uvnitř __try. Když je ukazatel špatný, vznikne opravdový access violation, __except ho chytí a vrátí false - přesně to, k čemu ta funkce je. Jenže crash-probe je first-chance vektorový handler, takže tenhle AV viděl taky a napsal o něm červený ERR. O řádek níž ve stejném logu přitom stálo colshadow_stale_crash - ... m_pShadowVertices=0x00000300 ... skipped, tedy že ochrana zabrala a jedna entita jen na jeden snímek nevrhla stín. Nově se AV a in-page error během úmyslné sondy nehlásí (core/expected_fault.h, per vlákno, zvednuto těsně kolem toho čtení) - illegal/priv instrukce a stack overflow se hlásí dál, ty si sonda nikdy nevyžádala. Zbytek diagnostiky beze změny: skutečný pád se pořád vypíše celý.

perfgtapatchessonda na čitelnost nesahá na spodních 64 kB

Ty ve Win32 procesu nejsou namapované z principu, takže „ukazatel" 0x300 nebo 0xB4 se dá zamítnout bez jediného dotyku - a přesně takové hodnoty v tom mrtvém shadow meshi byly obakrát, co to v terénu spadlo do logu. Dřív každá stála skutečný page fault. Plus zamítnutí rozsahu, který přeteče adresní prostor.

fixlagcompvzdálená vozidla se umisťovala na pozici ŘIDIČE, ne vozidla

peer_sync hlásí PedGetPosition bezpodmínečně - tedy pozici peda včetně sedačky - a LagCompClient ji předával rovnou VehicleSetPosition. Tím se počátek vozidla položil tam, kde SEDÍ řidič, takže každé vzdálené vozidlo bylo nadzvednuté o vlastní výšku sedu. Na motorce, kde jezdec sedí vysoko nad rámem, to byly ty „motorky lítají": stroj visel zhruba 0,7 m nad zemí se stínem na silnici. Potvrzeno vypnutím smoothingu - motorky okamžitě dosedly. Opraveno na straně PŘÍJEMCE záměrně: hlášená pozice je zároveň to, proti čemu server rewinduje zásahy (sp_lagcomp.inc), a tiché přesunutí sedícího hráče ze sedačky do počátku vozidla by posunulo každý hit test o tentýž offset. Drát si tedy nechává svůj význam a příjemce si offset sedačky změří z živého páru (oba objekty má po ruce) a odečte ho. Je to samoopravné: po umístění vozidla hra peda zase posadí, takže ped skončí přesně v hlášeném bodě a další snímek naměří tentýž offset. Offset je omezen na 3 m, aby smetí nemohlo mrštit autem přes mapu.

fixlagcomplagcomp_smoothing = 0 server už nepřebije

OnSetMode přepisoval hodnotu ze serveru bezpodmínečně, takže hráč, který si smoothing vypnul, ho měl po připojení zase zapnutý - a nedalo se to ani otestovat („zkus to vypnout" se vracelo jako „žádná změna", přitom se to nikdy nevyplo). Featura zapisuje paměť cizích entit, takže odmítnout ji musí být hráčovo rozhodnutí. Chybějící klíč dál znamená „rozhoduje server"; výslovná 0 zamyká.

fixcutscenesploché černé / cyanové postavy ve filmečcích byl FOG

Postavy se kreslily jako jeden plochý obrys bez vnitřního detailu, cyanový venku a černý v hale letiště, zatímco auta, propy i svět kolem měly textury normálně. Naměřená barva to uzavírá: FOGCOLOR venku FF35A2E3 = RGB(53,162,227), ten cyan, a uvnitř FF050505, ta čerň. Na objektu ani na draw callu přitom nebylo špatně nic - navázaná vlastní textura, COLOROP=MODULATE nad TEXTURE x DIFFUSE, plný colour write, vertex deklarace shodná s korektně kresleným pedem bajt za bajtem a sedící na svůj stride, reálná měnící se UV data, rozumné světelné konstanty. Fog je jediná etapa, která umí být celou odpovědí a všechno tohle nechat v pořádku, protože nahradí hotový pixel až za texturováním a osvětlením - a je to i důvod, proč tři dřívější „vylučovací" testy (prohozená textura, magenta, rpGEOMETRYLIGHT) nemohly nic ukázat. Proč jen postavy: fog je vertex fog (FOGVERTEXMODE=3, FOGTABLEMODE=0), a při navázaném vertex shaderu bere D3D faktor z jeho výstupu oFog místo aby ho spočítal z FOGSTART/FOGEND. RenderWarův skin shader oFog nepíše, takže skinované postavy fogovala nedefinovaná hodnota vycházející jako plný fog; všechno neskinované vedle nich jde fixed-function cestou, dostane skutečný faktor ze 100..800 a je v pořádku. Proč je to oprava a ne obcházení: ped kreslený v běžné hře, protažený týmž kódem, má FOG(on=0) - hra kolem skinovaného draw callu fog sama vypíná, právě protože ho shader neumí spočítat, a obsazení to nikdy nedostalo. cutscene_fog_fix (default 1) tedy dělá to samé co hra dělá každému jinému pedovi: obalí skinovaným atomikům obsazení render callback, na dobu jednoho draw callu shodí D3DRS_FOGENABLE a vrátí ho přesně jak byl. Rozbor v docs/cutscenes.md.

chorecutscenesdiagnostika, kterou se to našlo, je zase venku

~1700 řádek sondy (stav zařízení per draw, deklarace proti stride, bajty za UV prvkem, konstantní registry shaderu, kontrolní vzorek z živého ped poolu) a šest opt-in experimentů (cutscene_tex_experiment, _reinstance, _unlit_test, _paint_test, _move_test, _hide_skinned, _paint_at_draw, _probe_ms, _draw_probe). Co naměřily, zůstává zapsané v komentáři u opravy a v docs; kód po nich ne.

fixcapturecapture_video_loop zakládal při každém přetočení NOVÝ soubor

Přetočení je StopRecording() + StartRecording(), a VideoBegin si pokaždé řekla MakeOutputPath(), což do jména vtiskne aktuální sekundu - takže z „pokračuje do stejného souboru" byla řada přírůstků a dlouhá session tiše zaplnila disk. Přesně to, čemu má strop na délku zabránit, a komentář u přetočení i ini přitom tvrdily opak. Jméno se teď určí jednou, při spuštění natáčení, a přetočení ho použije znovu; ruční start vždycky dostane soubor vlastní. Log navíc jméno vypíše a řekne, kolik sekund tím zaniklo. Vlaječka přetočení se shodí na každé cestě, která se do VideoBegin nedostane - jinak by refusal mezi tím způsobil, že následující ruční nahrávání přepíše smyčkový klip.

fixcutscenescutscene_txd_parent nikdy nemohl na filmečky sáhnout - a přitom běžel defaultně na každém autě ve hře

Patch vznikl na čtení, že 0x5372EAovo == 6 je PEDÍ větev a že obsazení, které je ped třídy, jí prochází. 6 je VOZIDLO. Není to spor dvou disassemblerů: renderware.cpp si GetModelType každou session kalibruje proti pravdě (model 400 musí být vozidlo, model 0 ped) a loguje „vehicle=6, ped=7"; volaná funkce se odjakživa jmenuje CVehicleModelInfo::UseCommonVehicleTexDicationary; a callback, který instaluje, dělá remap laku (remap* s #). Cutscene sloty jsou typ 7, takže tu větev nepotkají - ani herci, ani auta ve scéně. Co patch reálně měnil, byla vazba textur u všech běžných vozidel. Nově default OFF, přejmenovaný a popsaný podle toho, co dělá; id zůstává, aby stávající sp-plugin.ini znamenal totéž.

fixcutscenestím padá i důkaz, o který se opíralo „ped třída rozbíjí textury"

cast #9 'cstaxi92' … mats=67 textured=57 se četlo tak, že 57 vyřešených jmen dokazuje běh vozidlového callbacku, protože jen ten vidí vehicle.txd. Jenže rodičem cstaxi92 je taxi.txd a jeho rodičem vehicle.txd, takže na stejných 57 jmen dosáhne i obyčejná procházka rodičů, kterou dělá standardní callback. Měření nerozlišuje nic. Vanilla slot (typ 5) i náš (typ 7) čtou DFF pod týmž bootovním callbackem 0x731720 - vyhledávání textur pro obsazení je na třídě nezávislé a od vanilly se neliší. Poznámky v gta_funcs.h i gta_patches.cpp jsou přepsané, ať to nikdo neodvozuje potřetí; ze stejného důvodu je opravená i poznámka u RepairCastBoundSpheres (0x7336F0 je vozidlový clump callback, naše obsazení ho nedostává).

fixpostfxRenderBody nulovalo oba shadery a nikdy je nevracelo

Jediný neobnovený stav zařízení v celé Present cestě. Není to neškodné jen proto, že poslední kresby snímku jsou fixed-function: RenderWare si pamatuje, který shader podle něj visí, a co považuje za nastavené, znovu nepošle - takže vyčištění zařízení za zády té cache je přesně stav, kdy první kresba dalšího snímku běží přes špatný program. NuiManager::Render si tenhle risk ukládá a vrací celý; PostFX ne. Doplněno GetVertexShader/GetPixelShader k ukládanému stavu (a Release(), protože Get* přidává referenci). Base grade nemá ini klíč a zapíná ho SERVER přes SP_SetPostFX, takže cesta byla dosažitelná právě ve chvíli, kdy gamemode pustí barevný grade k filmečku.

fixcutscenesadoptovaný cutscene slot dědil kreslicí vzdálenost speciální postavy

Blok se zakládá bajtovou kopií živého model infa slotu 290 a čistí se sedm polí; +0x18 (m_fDrawDistance) mezi nimi nebylo, takže dvacet slotů, které jsou cutscene OBJEKTY, jelo s číslem popisujícím chodce v provozu. Vanilla je má z hier sekce v data\default.ide a ta se nekrotí (1, csplay, player, null, 2000.0) - a přesně 2000 si cutscene_manager už ručně psal na slot hráčova těla; dvacet slotů obsazení se toho nikdy nedočkalo. Na tom poli visí všechno, co rozhoduje, jestli se entita kreslí, kreslí prolnutá, nebo vypadne.

fixcutscenesadoptovaný slot tvrdil „kolize načtena" s nulovým col modelem

Čistil se jen bit 5 (bDoWeOwnTheColModel), ne bit 7 (bIsColLoaded), zatímco +0x14 se nuluje hned o dva řádky níž. renderware.cpp ty dva bity jmenuje spolu jako „dva bity, které musí col swap shodit" - a tohle col swap je.

fixoverlayImGui dostával vstup jen když bylo otevřené ALT+S menu - hráč uvízl za náhledem

Podmínka na předávání zpráv do ImGui zněla if (m_visible), jenže vstup hry overlay zmrazí i kvůli 3D náhledu (SetGameInputFrozen OR-uje WantsInput). Náhled tak neviděl ani tlačítka myši, ani klávesy: Koupit/Zavřit nešlo kliknout, tažení a zoom nedělaly nic a ESC v DrawUi nemohl zafungovat, protože ImGui se o stisku nikdy nedozvědělo. Zmrazený vstup držel dialog, který se ze své podstaty neuměl zavřít - ven vedl jen Alt-Tab. ImGui teď dostává zprávy, kdykoliv o vstup stojí cokoliv našeho, a ESC navíc zavírá náhled přímo z WndProc (RequestClose), tedy i kdyby ImGui vrstva selhala.

fixtextstringy ze serveru, které nejsou UTF-8, se překládají z Windows-1250

Pawn posílá bajty tak, jak je má ve scriptu, takže „500 kreditů" dorazilo s ů jako jediný bajt 0xF9 - pro ImGui neplatné UTF-8, tedy čtvereček (Cena: 500 kredit�). ReadU8String teď string zkontroluje: co je platné UTF-8 (včetně čistého ASCII) projde bajt po bajtu beze změny, jen to, co UTF-8 být nemůže, se přečte znovu jako CP1250. Server posílající UTF-8 tím pádem funguje přesně jako dosud. Platí pro všechny serverové texty (notifikace, HUD, statebagy, náhled).

featpreviewneprůhledné pozadí náhledu + jednorázový report renderu

Model vykreslený bez světel je skoro černý a na průhledném podkladu nad tmavým ImGui oknem to vypadá identicky jako „nevykreslilo se nic". Břidlicové pozadí ty dva případy rozliší pohledem a log k tomu přidá počet atomiků (celkem / s render flagem), střed a poloměr bounding sphere, vzdálenost a pozici kamery.

fixpreviewnáhled si embedded kolizi z DFF vůbec nenechá přečíst

Navázání R* hooku na vynulovaný stand-in modelu (viz níže) posunulo pád jen o jednu instrukci: AV read @ 0x14 eip=0x004C4BDD - herní čtečka kolizí si z „model info" vytáhne ukazatel, který má reálné model info naplněné a náš zeroed sink nulový. Předstírat model info přesvědčivěji je závod, který proti nereversované rutině nejde vyhrát, a náhled kolizi stejně nepotřebuje (renderuje geometrii do offscreen kamery). DFF se proto načte do paměti, vyprázdní se extension chunk clumpu (tam GTA embedded kolizi ukládá) a čte se z memory streamu. Jména framů (FrameList), geometry/material plugin data i per-atomic extensions zůstávají bajt po bajtu netknuté. Když se soubor přepsat nepodaří, jede se původní cestou ze souboru.

fixvehiclesre-derive marker se přeskakoval právě u settle passu (chybějící přední kolo NRG-500)

StampModelColForRederive končila hned na if (HasPendingColCheck), jenže „check běží" neznamená „marker je nasazený" - první SetupSuspensionLines ho spotřebuje a druhý rebuild (settle pass), jehož jediný úkol je dopočítat kolo a zavěšení, pak jel bez markeru, tedy přes zkratku „už inicializováno". V logu hráče to vypadalo čistě (obě pasáže Refreshed, všech 13 atomiků drawn, nody OK) a kolo přesto chybělo. Nově se u existujícího checku marker přerazítkuje na místě a restartuje se jeho watchdog - bez druhého zápisu do fronty; zálohou pro restore se navíc stanou aktuální (už clone-derived) hodnoty místo stock.

featdiagživý probe umístění kol motorky 2 s po applyi

Apply-time dump ukazuje framy tak, jak je nese DFF - jenže CBike::PreRender matice kol přepisuje každý frame, takže o chybějícím kole strukturálně nemůže nic říct (log hráče: všech 13 atomiků drawn, nody OK, suspension lines správně odvozené z custom clumpu - a kolo pořád nikde). Probe proto vzorkuje stav, až když ho pár set PreRenderů nastavilo: lokální pozici, délky os matice (kolo zmenšené na ~0 je neviditelné, i když je všechno ostatní platné) a světovou pozici z LTM. Přední a zadní kolo vedle sebe - zadní se vykresluje správně, takže rozdíl mezi těmi dvěma řádky je ta chyba. Jede automaticky po instance applyi na bike a taky přes /spveh.

featdiagapply-time dump vozidla vypíše i suspension lines

Doteď to uměl jen /spveh spuštěný ručně. Dump geometrie odpovídá na „je ten díl tam", tohle na „odkud se počítá jeho pozice" - a řádek s p0.z ≈ 99999.99 je označený explicitně jako nespotřebovaný re-derive marker (kolo se staví ~100 km nad mapou).

fixpreview3D náhled odmítal každý DFF s embedded kolizí (AV @ 0x14)

ReadDFF nastavoval R* embedded-collision hook (RpPrtStdGlobalDataSetStreamEmbedded) jen když měl model info; náhled ho volá s modelId -1, takže hook zůstal null. Jakmile DFF nesl collision chunk, herní čtečka kolizí bez null-checku zavolala CBaseModelInfo::SetColModel a zapsala pColModel (+0x14) přes nulové this - v logu hráče AV write @ data=0x00000014 eip=0x004C4BD2 (= SetColModel+0x12), SEH to chytil a asset byl reportován jako malformed or unsupported DFFModelPreview: FAILED - DFF did not load as a clump, reprodukovatelně při každém otevření. Asset byl v pořádku. Hook teď dostane cíl vždy: bez model slotu je to privátní vynulovaný CBaseModelInfo-shaped sink, takže kolize skončí v naší paměti a nikdy se nedostane k reálnému modelu (za cenu pár kB leaku herní CColModel na otevření - ověřený game-side free pro ni tenhle modul nemá). kolo).** Marker vynucující přepočet geometrie kol z custom clumpu se razítkoval jen při úplně prvním applyi klonu. Druhé vozidlo se stejným skinem (garážová auta sdílejí jeden klon) nebo tentýž vůz po respawnu/restreamu už marker nedostaly - bike zkratka „už inicializováno" pak přeskočila přepočet per-instance fork/wheel-base polí a kolo se skládalo ze směsi stock polí a clone lines → „kolo se někdy neobjeví". Marker se teď razítkuje před suspension setupem při každém RefreshVehicleInPlace na klon (spotřebuje se synchronně hned dalším voláním; watchdog s restore zůstává jako pojistka).

fixreviewdávka oprav z trojitého code-review (COL / download / release)

Z hloubkového review COL cesty, download pipeline a čerstvého kódu: (1) Download „exactly-once": dokončený CHUNK přenos šel na server přímou zprávou mimo dedupe - když se HTTP a chunk cesta sešly na jednom assetu (okno = celá doba HTTP fetch e), server dostal DVĚ dokončení a gamemode počítající completions označil hráče za připraveného předčasně. Úspěch teď jde přes ReportAssetDone (dedupe per asset); selhání zůstává per-transfer, aby server mohl přenos poslat znovu. (2) HTTP worker už nemůže shodit hru: fetch až 128MB souboru alokuje jeden souvislý buffer bez memory-gate a bez try/catch - bad_alloc na worker vlákně = std::terminate. Teď stejná memwatch brána jako chunk cesta + catch. (3) COL poctivost: col na VEHICLE modelu se četl z drátu a mlčky zahazoval s ok=1 (a nejde ho zapojit naivně - .col nenese suspension lines, živé vozidlo by četlo null → crash třídy 0x6AABE8) - col-only na vozidlo teď vrací ok=0, col+art hlásí zahození do logu; nezacachovaný col u GLOBAL apply celý apply poctivě odmítne (ok=0) místo tichého „ok bez kolize"; a výsledek kolize se propisuje do ApplyDone i když jede spolu s artem. (4) Memory-safety: FreeCloneModel ruší i čekající suspension-watchdog záznamy slotu (stale záznam by po reuse slotu memcpy-nul starou zálohu do menšího line arraye nového klonu); restore ruší i naplánovaný settle pass (jinak by vozidlo vrátil na klon, který restore právě odstranil); obě refresh fronty si ověřují, že vazba vozidlo→klon pořád platí (SA:MP id se recyklují). (5) Drobné: preview okno nemělo potlačený HW kurzor (dvojitý kurzor v okně), timeout přes absolutní GetTickCount (padal by na 49denním wrapu), asset_preview klíč chyběl v ini šabloně, watchdog log pro globální marker hlásil špatnou větev.

fixassetssuspension klonu se přepočítá z CUSTOM clumpu (kola „mimo" hned po aplikaci)

Privátní kopie kolizí (níže) ochránila lines před pozdějším přepsáním, jenže zkopírovala i jejich HODNOTY - a bike setup má (po vzoru VC předchůdce) zkratku „už inicializováno": inicializované lines čte zpátky místo odvození z framů entity. Kola custom modelu tak seděla podle STOCK geometrie od prvního framu (NRG přední kolo v kapotáži, SlamTruck s koly mimo blatníky). Privátní lines se teď razítkují markerem FAKESUSPENSION, který setup donutí odvodit geometrii kol z clone clumpu - a protože SA bike rutina (0x6B89B0) je nereverzovaná, hlídá to watchdog: nesežere-li hra marker do 3 s, vrátí se záloha stock hodnot (nejhorší případ = dosavadní chování, nikdy rozbitý suspension ray). Log řekne, která větev nastala. /spveh nově tiskne i suspension lines modelu, takže špatně usazené kolo je vidět v číslech. Ověřeno na reálném klientovi: binárka marker konzumuje (NRG po fixu sedí). Stejné razítko dostává nově i GLOBÁLNÍ výměna vozidla (celý model) - tam žádný klon není, ale lines modelu jsou pořád inicializované ze stock clumpu a bike zkratka je držela; globální varianta bez timeout-restore (instance smí přijít klidně za minuty a marker čte jen setup). Z živého testu na Faggiu navíc: PRVNÍ parent→clone rebuild nechá (přední) kolo mimo a ruční druhý apply - clone→clone rebuild o pár vteřin později - to spolehlivě spraví. Odpovědná rutina je nereverzovaná, takže místo hádání interních detailů se po prvním úspěšném přechodu na klon automaticky plánuje settle pass: jedno opakování téhož in-place rebuildu hned na dalším herním framu (~16 ms, okem neviditelné) - jeden celý průchod herní smyčky mezi oběma rebuildy zůstává (to měl ruční druhý pokus taky), jen bez půlvteřinové latence retry fronty. Jen při skutečné změně modelu - settle pass sám je clone→clone a nesmí se řetězit; pokud zrovna neprojde (obsazeno/model venku), spadne do pomalé retry fronty místo ztracení.

fixassetsklon vozidla má PRIVÁTNÍ suspension kolize - kolo NRG už neujíždí

Klon byte-copy sdílel s rodičem ukazatel na CColModel, jenže suspension lines v něm nejsou read-only data: každé SetupSuspensionLines (auto i motorka) je přepisuje podle clumpu toho modelu, který zrovna běží. A motorka si PŘEDNÍ kolo každý frame skládá právě z těch lines (CBike::PreRender - kolo se neskrývá, kolo se přesouvá). Takže: skinutá NRG vypadala dobře, dokud se kdekoli nestreamla stock 522 - ta sdílené lines přepsala podle stock geometrie a přední kolo custom modelu ujelo do kapotáže/pod zem; respawn to zase přepsal zpátky, přesně jak hlásilo testování. Nově dostane vehicle klon vlastní kopii hlavičky kolize + line arraye (mesh/spheres/boxes zůstávají sdílené s pinnutým rodičem, layouty ověřené proti gta-reversed VALIDATE_SIZE/OFFSET), takže si klon a stock instance už nikdy nepíšou do stejných dat. Stejný trik používá MTA pro engineRequestModel modely.

fixassetsRhino věž, Hydra zbraně/podvozek/světlice, tažné rameno - tři další místa, kde GTA větví na model id

(1) CAutomobile::PreRender (0x6AAB50) je ten switch, který komponenty hýbe - věž Rhina, rameno odtahovky, vidle Forkliftu; stav (míření, úhel ramene) se počítal správně v bracketovaném ProcessControl, ale matice nodů se točí až tady. Audit zdrojů ukázal, že je to null-safe (vše jde přes SetComponentRotation, které začíná if (!component) return), takže bracket je bezpečný i pro dff bez daného nodu. (2) Letadla ProcessControl overridují - CPlane::ProcessControl (0x6C9260) volá CVehicle::ProcessWeapons až PO vnořeném CAutomobile průchodu, a všechny jeho helpery (GetPlaneNumGuns, FirePlaneGuns, SelectPlaneWeapon, …) switchují na id - na clone id vyjde počet kanónů 0 a Hydra létá jako Shamal. Bracket na override to pokryje celé. (3) CPlane::PreRender (0x6C94A0) - animace podvozku, trysek a afterburner efekty. Navíc CVehicle::SetModelIndex zbraňuje sloty jen pro skutečná id, takže vozidlo POSTAVENÉ pod clone id nemělo vybranou zbraň vůbec - klon se teď po rebuildu/při prvním bracketovaném framu douzbrojí podle rodiče (jen dokud je slot prázdný, volbu hráče to nikdy nepřepíše; offsety m_nVehicleWeaponInUse +0x513 a m_wMiscComponentAngle +0x86C jsou VALIDATE_OFFSET z gta-reversed). Do seznamu hardcoded modelů přibyla i ozbrojená letadla Rustler/Beagle/Stunt (stejná cesta jako Hydra). Pozn. k odtahovce: hák se i ve vanille zapíná až se SKLOPENÝM ramenem (m_wMiscComponentAngle startuje na 0 = nahoře) - dosud ale rameno nebylo vidět se hýbat, takže to vypadalo jako mrtvé tažné; s PreRender bracketem je pohyb vidět.

fixdiagdump atomiků četl špatný bit

„drawn" se testovalo přes bit 0x01 (rpATOMICCOLLISIONTEST), který má nastavený prakticky každý atomik - správně je rpATOMICRENDER = 0x04. Každý dosavadní dump tedy hlásil „drawn" bez ohledu na realitu a skryté kolo by nepoznal; nově se testuje 0x04 a tiskne se i syrový flags byte.

fixassetsčásti modelu pojmenované extraN už nemizí

Z testování: motorce se ztrácelo kolo a po respawnu bylo zpátky. Vehicle dff může mít volitelné varianty a hra si je hledá podle jména framu - extra1..extra6. CVehicleModelInfo::SetClump je z master clumpu vyjme do m_apExtras a CreateInstance jich pak každé instanci vrátí maximálně dvě, vybrané náhodně (ChooseComponent, váženo i počasím). Konkrétně: dff mělo 13 atomiků, master si nechal 8, instance dostala 10 - pět dílů se tedy stalo volitelnými a viděly se vždycky jen dva z nich. U stock modelů (bedny na Sadleru) je to záměr, u per-instance skinu ne. Nově se ty framy před SetClump přejmenují, geometrie zůstane v clumpu jako každá jiná a každá instance vyrenderuje model tak, jak je vymodelovaný. Frame se hledá herním CClumpModelInfo::GetFrameFromName, umístění jména v RwFrame se dohledává za běhu, ne natvrdo.

fixassetsskinutý tow truck padal na prvním vykreslení

AV read @ 0x00000008, eip=0x006AABE8, ve stejnou vteřinu jako apply. Z dumpu vytažený kód crash site to řekl přesně: call CEntity::GetColModel()edi = colModel->m_pColDataeax = colData->m_pLines (NULL) → fld [eax+8]. Suspension linky klonu nebyly alokované, protože SetupSuspensionLines běžel pod bracketem, tedy jako model 525 - GetColModel() mu dal colmodel rodiče, linky se naplnily tam a klon, na kterém entita reálně je, zůstal prázdný. Motorka nepadala jen proto, že CBike::SetupSuspensionLines je jiná adresa a v tabulce nikdy nebyla. Z toho plyne pravidlo, které je teď u tabulky napsané: bracketovat jen funkce, které se na model id větví, nikdy ty, které podle něj dohledávají data. Vypadly proto i ProcessSuspension a ProcessEntityCollision (obojí přes GetColModel()), a s nimi extra suspension linky Rhina.

fixassetsskinutý tow truck zase táhne

Bracket kolem CAutomobile::ProcessControl na to nestačil - odtah se totiž z poloviny řeší jinde: link se navazuje a ruší v SetTowLink/BreakTowLink a pozice háku a oje se počítají v GetTowHitchPos/GetTowBarPos, což všechno volá kolizní kód přímo. Výsledkem byl tow truck, který vypadal správně, řidiče měl na místě a nic nezvedl. Bracket teď pokrývá i ProcessEntityCollision, SetTowLink, BreakTowLink, GetTowHitchPos, GetTowBarPos, UpdateMovingCollision, ProcessSuspension, SetupSuspensionLines a PreRender (tj. i pohyblivé díly: hák, korba dumperu, vidle forkliftu). Adresa PreRender se nikde nepublikuje, takže se čte z vtable skutečného special clonu, ne z hádané konstanty.

fixaudioprvní zásah už nezasekne frame

Z testování: první hitmarker i zvuk dorazily se znatelným zpožděním, další už okamžitě. Příčina: ReloadActive() se volal jen z OnHitSounds a z menu. Když asset v tu chvíli ještě nebyl docachovaný, tiše se vzdal a nic to nikdy nezkusilo znovu - takže Media Foundation dekód plus waveOutOpen spadly na první Play(), synchronně na herním vlákně. To jsou stovky ms, které zablokují celý frame, proto se opozdil i ten vizuální hitmarker. Nový HitSound::Tick() (throttled na 1 s, po načtení no-op) to zahřeje dřív, než první rána padne; Play()nikdy nedekóduje - když zvuk není připravený, radši jednu ránu mlčí, než aby zasekl obraz. Soubor, který se nepodaří dekódovat, se zapamatuje a nezkouší se každou vteřinu znovu.

featassetsper-instance skin funguje i na Rhinu, Hydře, Towtrucku a Linerunneru — bez ztráty jejich funkce

Z testování: Rhino se naklání do zatáček a nestřílí, Hydra se chová jako Shamal (bez zbraní a světlic), tažné u Towtrucku nereaguje, Linerunner nezahákne návěs - a to vše s korektně aplikovaným skinem. Příčina není ve swapu: per-instance skin musí vozidlo přesunout na klon model id, a CAutomobile::ProcessControl větví na syrovém id (case MODEL_RHINO: TankControl(), case MODEL_TOWTRUCK: TowTruckControl(), case MODEL_PACKER/DOZER/…: UpdateMovingCollision(), if (m_nModelIndex == MODEL_RHINO) pro osm kol). Proti klonu každé takové porovnání selže, takže si vozidlo nechá vzhled a přijde o funkci. Není to opravitelné obnovením přepisu ModelID - ten entitu nechá na klonu taky. ⚠️ První řešení bylo špatně a je nahrazené. Nechat entitu na skutečném model id (půjčit jí clump rodiče na dobu přestavby) sice vrátilo tažné/kanón, ale rozbilo sedačku: CVehicleStructure je per model info, ne per entita (GetFrontSeatPosn() je doslova m_pVehicleStruct->m_avDummyPos[4]), takže vozidlo na skutečném id může číst jen stock dummies - ped_frontseat i ped_arm se braly z origo Towtrucku místo z custom .dff. Z testování: hráč seděl mimo sedačku. Nové řešení: vozidlo zůstane na klonu (odtud jde art i CVehicleStructure, tedy sedačka a loket z custom modelu) a chování se vrátí bracketem kolem CAutomobile::ProcessControl (0x6B1880) - po dobu toho jednoho volání se vozidlu podstrčí rodičovské model id, takže switch (m_nModelIndex) uvnitř pořád trefí TankControl / TowTruckControl / UpdateMovingCollision. Zbytek času je to klon. Cena: co běží uvnitř ProcessControl si model info dohledá podle rodiče, takže TowTruckControl čte pozici háku ze stock struktury - hák je ale hardpoint na rameni, ne detail karoserie, takže na reskinu sedí; sedačka a loket, které jsou vidět, jdou z klonu. Původní varianta (RefreshVehicleKeepingModelId) je odstraněná, ne zakomentovaná - nechávat po sobě mrtvý mechanismus je přesně to, co tenhle bug způsobilo.

Detail k původní (odstraněné) variantě: entita nikdy nezměnila model id. Klon se vyrobí jen jako nosič artu, jeho clump se na dobu přestavby půjčí rodičovskému modelu, proběhne beze změny existující (a na klientovi ověřená) sekvence a stock clump se vrátí přímým zápisem ukazatelů - ne druhým SetClump, který by z 50-místného poolu bral novou CVehicleStructure na každý apply. Stojí to na tom, že entita renderuje vlastní m_pRwObject, klonovaný z clumpu při CreateRwObject, takže instance postavená během půjčky si art nechá napořád a ostatní vozidla toho modelu si ničeho nevšimnou. Restore je i v __except: nechat náš clump na stock id by neznamenalo „chybí skin", ale že se každé vozidlo toho modelu při streamnutí promění. Cena: CVehicleStructure je per model, takže po vrácení clumpu čte vozidlo stock pozice dummies (sedačky, výfuky, hák) - pro reskin se stejným rozložením správně, pro přemodelovaný hák mírně vedle. Seznam modelů (HasHardcodedModelBehaviour) je best-effort, ne úplný.

featassetsper-instance skin funguje na DEVÍTI třídách vozidel místo jedné

RefreshVehicleInPlace byl omezený na VEHICLE_CLASS_AUTOMOBILE, protože každý krok jeho sekvence je per-třídní implementace (Fix, SetupModelNodes, SetupSuspensionLines). Nově je z toho tabulka (ResolveVehClassOps) pokrývající automobil, monster truck, quad, heli, letadlo, loď, přívěs a motorku/BMX - tedy 9 z 11 tříd. Klíčové zjištění: monster truck, quad, heli, plane i trailer dědí z CAutomobile a SetModelIndex/SetupModelNodes nepřepisují, takže se liší jen v Fix (0x6CE2B0 quad, 0x6C4530 heli, 0x6CABB0 plane; zbytek dědí 0x6A3440) a v suspension, kterou už měl projekt celou v handling_data.h - těch pět hodnot sedí s gta-reversed na bit, takže se reusují místo duplikace. Motorka je jediná výjimka: Fix 0x6B7050, SetupSuspensionLines 0x6B89B0 a vlastní SetModelIndex 0x6B8970. Adresa CBike::SetupModelNodes není potřeba - CBike::SetModelIndex je override, jehož celé tělo je CVehicle::SetModelIndex(index); SetupModelNodes();, takže jedno volání udělá oba kroky. CBmx dědí z CBike a nic z toho nepřepisuje → jedna větev pokrývá obojí. Adresy z gta-reversed; ten samý zdroj nezávisle reprodukuje šest hodnot, které už projekt měl ověřené jinou cestou (0x6A0770, 0x6A3440, 0x6A65D0, CVehicle::SetModelIndex 0x6D6A40 a všech pět ADDR_*_SetupSuspLines) - proto se zbytku věří. Lodě (CVehicle::Fix nemá publikovanou adresu) jdou taky: CVehicle::Fix (0x6D6390) i CVehicle::SetupSuspensionLines jsou reálné, ale prázdné implementace, které CBoat nepřepisuje - loď tedy oba kroky prostě nemá a vynechají se, a CBoat::SetModelIndex (0x6F1140) si stejně jako CBike relinkuje nody sám. Nepodporované zůstávají jen vlaky a flying class id 7/8. Neověřeno na reálném klientovi.

docsvelikosti model-info tříd dohledány, kompletně

(CPedModelInfo 0x44, CWeaponModelInfo 0x28, CClumpModelInfo 0x24, CAtomicModelInfo 0x20, CTimeModelInfo / CDamageAtomicModelInfo / CLodAtomicModelInfo 0x24) a zapsány jako SIZEOF_*_MODELINFO. Past: GetModelType hlásí ATOMIC pro CAtomicModelInfo (0x20) i CDamageAtomicModelInfo (0x24), takže se pro tu rodinu bere ta větší - přečíst 4 bajty navíc uvnitř poolu je neškodné, kdežto zkopírovat míň by nechalo m_pDamagedAtomic neinicializovaný v klonu, jehož vtable si na něj sáhne. Záměrně nezapojené do alokátoru klonů: bez vazby (půlka B) by rozdával sloty, které nemá co použít. Viz docs/instance-apply.md §4.1.

fixassetsINSTANCE apply na třídu bez ověřené sekvence už nevyhazuje hráče z vozidla pro nic

Klon se vyrobí a art se načte správně (VERIFY … ourClump=yes), ale navázat ho na entitu umí jen RefreshVehicleInPlace, což je CAutomobile cesta → NotAutomobile. Klient to hlásil jako ok=1, refreshedLive=0, což skript čte jako „restreamni to" → SP_Restream VehicleReseateject + restream + reseat. To dávalo smysl, dokud OnWorldVehicleAdd přepisoval ModelID v paketu a vozidlo se vrátilo jako klon; ten přepis je ale pryč (smazán jako mrtvý kód), takže se vozidlo postaví na stock modelu a jediný pozorovatelný efekt je, že jezdec sletí z motorky. Nově se hlásí ok=0 — což je i věcně správně, art leží na klonu, který nikdo nikdy neinstancuje — plus konkrétní WARN se jménem třídy a odkazem na docs/instance-apply.md. Klon se nezahazuje: může být přes m_cloneCache sdílený s vozidlem, které ho nosí. Z terénu: Faggio (462) a NRG-500 (522).

fixhttphttp::Get zahazoval query string

WinHttpCrackUrl plní jen komponenty, na které dostane buffer, a lpszExtraInfo zůstávalo NULL - request tedy šel na holou cestu. Podepsané/expirující CDN odkazy (?token=, ?Expires=&Signature=) tím stahovaly špatnou věc nebo 403; horší je cache-busting (?v=2), který selhal úspěchem - server vrátil 200 se starým souborem.

choreciunit testy se konečně staví a spouští

tests/ existovaly od začátku s korektním add_test(), ale CI konfigurovalo SP_BUILD_TESTS=OFF a ctest nikdy neběžel - regrese v config parseru, pattern scanneru nebo interpolační matematice se tedy dostala do releasu se zeleným zaškrtnutím. Zároveň z tests/CMakeLists.txt zmizelo explicitní /MD, které si odporovalo s globálním statickým CRT.

Dokumentace

dokumentacedocs/code-review-2026-08.md

review celého repa: nedodělky, drift dokumentace vůči kódu, konkrétní chyby, bezpečnostní nálezy a doporučené pořadí prací.

dokumentacedocs/protocol.md

: RPC_SP_CHALLENGE (207) doplněn do mapy id (volné je 208–255, ne 207–255) a nová §4.1 popisuje challenge-response a co auth-tag neumí - dosud to bylo jen v hlavičce sp_rpc_ids.h.

dokumentaceROADMAP.md

: challenge nonce označen jako hotový (byl implementovaný, ale veden jako [ ]), auto-update přepsán z „jen notifikace" na skutečný stav, opraven výchozí toggle (ALT+S, ne CTRL+SHIFT+S) a popsán rozpor mezi kódovým defaultem lagcomp_smoothing (OFF) a shipovanou ini (ON). Nový Milestone 14 (bezpečnost & release hygiena).

dokumentaceREADME.md

: sekce Dokumentace odkazovala na 7 z 23 dokumentů - teď na všechny, tematicky rozdělené.

dokumentaceLICENSE

doplněna (badge na ni odkazoval, soubor neexistoval) včetně sekce o third-party komponentách v external/.

dokumentaceOba AV dokumenty dostaly v hlavičce jasnou roli (playbook vs

snímek měření). Sloučit je nešlo: na false-positives.md se z šesti zdrojáků odkazuje číslem sekce.

Přidáno

featpreview3D náhled assetu - „prohlédni si, než koupíš"

Server otevře hráči rotovatelný 3D náhled streamovaného DFF/TXD assetu v ImGui okně (tažení = otočit, kolečko = zoom, tlačítka Koupit/Zavřít), takže asset shop může ukázat skutečný model před zaplacením. Render jde přes offscreen RenderWare kameru do camera-texture rasteru - stejná technika, kterou hra sama používá pro real-time stíny (CShadowCamera) a kterou má SA:MP mobile pro UI snapshoty modelů; D3D9 textura za rasterem jde rovnou do ImGui::Image, bez kopie. Assety se odkazují manifest ID (dědí se SHA-256 ověření i čekání na download), DFF se čte jako samostatný clump s privátním, znovupoužívaným TXD slotem - na model sloty se nesahá, procházení obchodu nespálí žádný slot. Nový kanálový příkaz SP_EVT_ASSET_PREVIEW (25), odpověď SP_RPT_ASSET_PREVIEW (8), capability SP_CAP_ASSET_PREVIEW (bit 15, jen buildy s overlayem); pawn SP_ShowAssetPreview / SP_CloseAssetPreview + OnAssetPreviewSP(playerid, dffAssetId, result) - include odpověď páruje na náhled, který server skutečně otevřel, takže podvržený BUY na cizí asset id se zahodí (a FAILED/DISABLED znamená „hráč model neviděl - neúčtovat"). Adresy kamerové cesty jsou z plugin-sdk/mtasa-blue (stejná 1.0 US sada jako zbytek renderware.cpp), zatím neověřené na živém klientovi - stejný režim jako kdysi RefreshVehicleInPlace: všechno SEH-guarded, první fault vypne náhledy pro session a shop dostane Failed, nikdy nespadne hra. Vypnutelné asset_preview=0. Viz docs/asset-preview.md.

docsprůzkum podobných platforem

(docs/similar-platforms.md) - co v téhle oblasti umí MTA:SA (dxDrawModel3D, render targety, DGS preview), FiveM (content-addressed streaming, DUI), open.mp/0.3.DL a SA:MP addony, plus seřazené nápady co převzít (content-addressed cache, dxDrawModel3D ekvivalent, new-id registrace modelů, ped headshoty).

feathitsoundsjméno ke každému zvuku ze serveru

Menu klienta umělo jen „Zvuk 1 / Zvuk 2 / …“, což hráči neřekne nic o tom, co si vybírá. SP_EVT_HIT_SOUNDS proto veze volitelný trailing blok labelů (u8 count, count × u32 assetId [, count × str label]) a menu ukáže jméno od serveru („Click“, „Pop“), když dorazí. Nové natives SP_SetHitSoundListEx (pole id + pole jmen) a SP_SetHitSoundsNamed(playerid, id1, "Click", id2, "Pop", …); SP_SetHitSoundList / SP_SetHitSounds zůstávají beze změny. Blok je volitelný a na konci, takže server bez labelů i klient, který je nezná, se chovají přesně jako dřív - a prázdný label u jedné položky spadne zpátky na číslo jen na tom jednom řádku. Delší než 24 znaků klient ořízne (šířka řádku menu). Popis ve sp_rpc_ids.h byl u tohohle příkazu ještě od staré verze (tři pevná id bez počtu) a je teď opravený.

Opraveno

fixcutscenescutscéna se na některých instalacích vůbec nespustila a shodila hru v herním loaderu

CCutsceneMgr::LoadCutsceneData_preload začíná průchodem slotů cutscene objektů (for i = CUTOBJ01..CUTOBJ19: ms_modelInfoPtrs[i]->m_nRefCount) bez null checku. Když slot chybí, hra si sáhne na [NULL+8] dřív, než se cutscéna začne načítat - z terénu: AV read @ 0x00000008, eip=0x005B0617, esi=0x138 (= 312). Kde blok CUTOBJ začíná, není spolehlivě dané (seznamy model id říkají 300, ale SA:MP nabízí skiny 300–311 a skiny SA:MP jsou herní ped id, takže blok může začínat až na 312 - pak pád trefil první iteraci a chyběl celý blok), takže guard pokrývá sjednocení 300–331. Klient sloty ověří dřív, než hře podá jméno (jednou za sezení, líně - při startu pluginu hra .ide ještě nemusí mít načtené), odmítne SP_CUTR_NO_SLOTS a předtím zaloguje census slotů 288–340 (id[-id]:typ, - = chybí, plus stav ms_pCutsceneDir), takže z jednoho běhu na reálném klientovi je vidět, kde ped blok končí a jestli cutscene blok chybí celý nebo jen z části. Odchytit pád místo toho nejde: SA:MP si first-chance výjimky počítá a umí kvůli nim hru ukončit, a přerušený load nechá ms_cutsceneProcessing = true, což v CGame::Process vypne front-end - hráči nefunguje ESC menu až do restartu hry. Pro pády, které kontrola slotů nepokryje, přibyl ForceCleanupAfterAbortedLoad (nechá proběhnout herní DeleteCutsceneData, dorovná CTimer::Resume) a report SP_CUTR_LOAD_FAULT, takže hra i tak skončí v čistém stavu. Viz docs/cutscenes.md §3b.

Přidáno

featcutscenesspouštění skutečných GTA:SA cutscén ze serveru

Nový EventCmd::Cutscene (24) na kanálu 203 + report ReportCmd::CutsceneState (7) na 206; natives SP_PlayCutscene / SP_StopCutscene / SP_IsPlayerInCutscene / SP_GetPlayerCutscene a callback OnPlayerCutsceneSP. Server pošle jméno položky z hráčova anim\cuts.img (CRASH1A, MOB_01, …) a klient přehraje tu samou cutscénu, kterou hraje singleplayer - přes herní CCutsceneMgr, sekvencí, kterou používá main.scm (LoadCutsceneData 0x4D5E80 → čekání na ms_cutsceneLoadStatus == LOADEDms_cutsceneOffset + StartCutscene 0x5B1460HasCutsceneFinished 0x5B0570DeleteCutsceneData 0x4D5ED0). Čtyři věci, které nejsou mechanické: (1) load je asynchronní - LoadCutsceneData jen vyžádá modely a přechod LOADING → LOADED dojede sama hra v CGame::Process, takže klientská část je stavový automat s watchdogem (30 s load, 10 min přehrávání), ne jedno volání. (2) Teardown je povinný na každé cestě ven - cutscéna nechává hráče neviditelného, nesmrtelného a bez herní kamery a vrátí to teprve DeleteCutsceneData; proto přes něj míří i skip, timeout, SP_StopCutscene i Shutdown() pluginu. (3) Jméno se ověřuje proti anim\cuts.img (index IMG v2 při prvním použití), aby se automat nezasekl čekáním na modely, které nikdy nedorazí - u neparsovatelného archivu se kontrola vypne a rozhodne hra. (4) Cutscéna bez .DAT nemá dráhu kamery a HasCutsceneFinished() je u ní pravda hned na prvním snímku - jde přehrát jen s maxMs, jinak ji klient odmítne místo hádání délky. Navíc SP_CUT_NO_SKIP (vanilla skip reaguje i na ztrátu focusu okna, takže alt-tab jinak cutscénu přeskočí) a SP_CUT_KEEP_ZONE. Server drží stav per hráč, sám ho nechává vypršet (spadlý klient neuvězní hráče „v cutscéně“) a SP_CUT_FAILED je pro skript stejný signál jako konec. Rozbor v docs/cutscenes.md. Capability SP_CAP_CUTSCENE (1<<14), klientský vypínač cutscenes = 0. Vyžaduje nový klient i rekompilaci filterscriptu.

featvehiclehandling na konkrétní instanci vozidla, ze serveru

Nový EventCmd::VehicleHandling (23) na kanálu 203 + natives SP_SetVehicleHandling / SP_SetVehicleHandlingInt / SP_ResetVehicleHandling (a model varianty SP_SetModelHandling* / SP_ResetModelHandling, SP_ResetAllHandling). Nastavitelný je celý řádek handling.cfg - mass, top speed, akcelerace motoru, převodovka, trakce, brzdy, odpružení, model/handling flagy - per vehicleid, ne per model. GTA přitom drží handling jednou na model (cHandlingDataMgr::m_aVehicleHandling, 0xC2B9C8+0x14) a každé CVehicle na něj má jen ukazatel (+0x384), takže "handling vozidla 42" znamená dát tomu jednomu CVehicle vlastní kopii bloku a přesměrovat ho - stejný postup jako MTA CVehicleSA::SetHandlingData. Ostatní instance modelu i globální tabulka zůstávají nedotčené. Tři věci, které nejsou mechanické: (1) hodnoty jdou po drátě v jednotkách handling.cfg a klient je konvertuje herní rutinou ConvertDataToGameUnits (0x6F5080) - ta není idempotentní, takže se vždycky staví z inverze stock bloku, nikdy se nedopisuje do už zkonvertovaného; inverze maxVelocity je schválně vedená přes maxFlatVelocity, protože drag smyčka je ztrátová a jen tahle varianta je pevný bod (round-trip nemění výsledek). Klient si to při prvním override ověří na živé binárce a neshodu napíše do logu. (2) Konstruktor vozidla si z handlingu kešuje m_fMass, m_fTurnMass, buoyancy, těžiště a handling flagy do samotného CVehicle a fyzika čte tyhle kopie - přepsat jen ukazatel je klasické "posuvník hmotnosti nic nedělá", takže se přepisují spolu s ním. (3) SA:MP vozidlo při streamování zničí a znovu vytvoří na stock bloku, takže override drží per-frame tick (jeden snapshot vehicle poolu jednou za 8 snímků, ne 2000× GetVehiclePtr s VirtualQuery) - díky tomu přežije odjetí a návrat. Limity odpružení (jediná dvě pole, která hra zapéká při stavbě vozidla) mají opt-in flag SP_HANDLING_REBUILD_SUSP, protože herní SetupSuspensionLines píše kolizní čáry do sdílené kolize modelu. Všechny offsety jsou ověřené proti plugin-sdk (VALIDATE_*) a gta-reversed a znovu potvrzené static_assertem na layoutu struktur. Rozbor v docs/vehicle-handling.md. Capability SP_CAP_VEH_HANDLING (1<<13); klient bez ní se nemění. Vyžaduje nový klient i rekompilaci filterscriptu.

featdevin-game handling editor

Nová podkarta Developer → Handling (jen build s SP_ENABLE_DEV): sleduje auto, ve kterém sedíš, ukazuje jeho handling v cfg jednotkách, každý posuvník jde do fyziky okamžitě stejnou cestou jako serverový příkaz a tlačítko vygeneruje hotová SP_SetVehicleHandling* volání do gamemode. V release buildu není - handling posuvník pro hráče je cheat, ne feature.

featdemo/rocket + /rocketoff

v sp_demo.pwn - per-instance handling na autě, ve kterém sedíš (320 km/h, silnější motor, AWD, víc trakce a brzd), a návrat na stock.

fixpatchespád radaru 0x0058370A po zničení/teleportu vozidla

Nová položka radar_camtarget_crash v GUI (kategorie Pád, defaultně zapnutá). CRadar::CalculateCachedSinCos (0x583670) dereferencuje m_pCamTargetEntity bez null-checku. Kamera míří na vozidlo, ve kterém hráč sedí, takže když ho skript zničí nebo hráče teleportuje pryč, ukazuje na mrtvou entitu ještě jeden snímek radaru - odtud pády v cílovém checkpointu závodu a při warpu. Rozebráno na reálném 1.0 US image (všech devět expect sekvencí ze stávající tabulky na něm sedělo bajt po bajtu), potvrzeny tři deref sity téhož ukazatele: 0x5836C5 / 0x58370A / 0x583778.

featpatchespatch může být podložený hookem, ne jen byte editem

Patch má nově volitelné applyFn/revertFn; když jsou vyplněné, ApplyPatch/RevertPatch je zavolají místo zápisu bajtů, jinak se nemění nic - stejný ini klíč, stejný řádek v overlayi, stejný živý přepínač. Bylo to nutné právě kvůli radar_camtarget_crash: faulting site má 21 bajtů a nejmenší korektní null-check 25, nejbližší padding (9 NOPů na 0x583817) je mimo dosah rel8 a nejbližší použitelný code cave v okolí má 16 bajtů proti potřebným 26 - a půjčovat si cizí padding by znamenalo prát se o stejné bajty s CLEO/SilentPatch. Oprava tedy sedí v detouru ve stylu Hook_SetupDummy (radarcam::Install): guard je úzký, reprodukuje původní větvení (fullscreen mapa a LOOKING_FORWARD jdou beze změny do originálu) a přeskočí jen ten snímek, který by spadl - cachedSin/cachedCos si podrží hodnotu z minulého snímku, takže radar se o jeden snímek nepootočí a nic jiného se nemění.

featmissionsmission packages .spm - data mise mimo binárku

Nový pawn/include/sp_mission_pkg.inc čte misi z textového souboru ve scriptfiles (props, prop moves, pickupy, skupiny actorů se zbraní/HP/behave presetem, trigger volumes, kamerové beaty, dialogy, waypoint routy, pojmenované body) a vystaví je jako tabulky + pár apply helperů; volumes latchují a hlásí se přes OnMissionVolumeSP. Kompletní ukázka pawn/scriptfiles/missions/crash3.spm = statická polovina sp_crash3.inc (~520 z 2002 řádků). Není to mission runtime a záměrně nemá být - podmínky, časovače a stavové automaty zůstávají v Pawnu; rozbor v docs/mission-packages.md říká, proč vyjde jen 15-25 % reálné mise jako data. Klient, wire protokol ani asset pipeline se NEMĚNÍ: balíček čte server přes fopen (stejně jako SP_RadioPlayPls čte .pls), na klienty jde jen art přes stávající manifest/HTTP cestu. Jen rekompilace filterscriptu (PAWN-only).

featsyncklient hlásí serveru svoje rozlišení

Nový SyncCmd::ClientDisplay (5) na kanálu 204: u16 width, u16 height. Posílá se velikost backbufferu hry, ne desktopu - kdo hraje v okně nebo ve fullscreenu na jiném než nativním režimu, má je různé, a hráč vidí ten backbuffer. Záměrně to NENÍ pole v PlayerStatePayload: ten jde ven až 66×/s, kdežto tohle je konstanta na sezení. Klient to pošle, jakmile hodnotu zná, a pak už jen při změně - změna rozlišení i přepnutí borderless přijdou jako device Reset, takže D3DHook si teď backbuffer aktualizuje i v OnReset (dřív se sejmul jen při prvním Present a zůstal zamrzlý). Server: OnPlayerDisplaySP(playerid, w, h) + SP_GetPlayerScreenSize(playerid, &w, &h), s 1Hz flood guardem a odmítnutím nesmyslů pod 320×240. Hodí se na škálování serverem kreslených textdrawů podle skutečné obrazovky.

refactorhookspryč s Detours, všechno jede přes MinHook

Binárka vozila dva trampolínové enginy na jednu práci - te_sdk hookoval RakNet MinHookem, naše hooky (D3D9 Present/Reset/CreateDevice, SetCursorPos/SetCursor, HookManager) šly přes Detours. te_sdk přitom Detours nikdy nevolal, jen je měl vendorované ve stromu. Kromě jedné závislosti místo dvou tím z PE mizí sekce .detourc/.detourd - nestandardní jména sekcí jsou doslova otisk té knihovny při statickém scanu a byly vidět ve výpisu sekcí na VirusTotalu. Dvě věci, které nejsou mechanické: kurzorové hooky teď jmenují modul a export přes MH_CreateHookApiEx místo &::SetCursorPos (to je adresa NAŠEHO import thunku - hookovat ji by chytlo jen volání přes IAT tohohle modulu, a volající, na kterém záleží, je samp.dll; Detours ten rozdíl schovávaly tím, že jmp thunk následují). A HookManager::Shutdown schválně nevolá MH_Uninitialize - stav MinHooku je procesově globální a bydlí v něm i RakNet hooky te_sdk.

fixavcivped_hook si už neodemyká paměť jako RWX

EnsureCivPedHook / RemoveCivPedHook volaly VirtualProtect(..., PAGE_EXECUTE_READWRITE) na vtable slot CCivilianPed::ProcessControl. Ten slot je read-only data - zapisuje se do něj ukazatel a nic se z té stránky nespouští - takže +X nebylo k ničemu potřeba, zato push 40h před VirtualProtect je statická signatura, po které jde spousta YARA pravidel a kterou statické ML enginy (Cynet, DeepInstinct) vidí přímo. Soubor vznikl až po hardeningovém kole, takže obcházel pravidlo z docs/false-positives.md §4 a stihl se dostat do buildu nahraného na VirusTotal. Teď PAGE_READWRITE, stejně jako RawWrite() v gta_patches.cpp. Pravidlo v dokumentaci rozšířeno explicitně i na vtable/IAT patche.

featdisplaydevice_lost_recovery je nově ZAPNUTÁ defaultně a má přepínač v GUI

Obnova ztraceného D3D zařízení byla dosud opt-in „dokud se neověří v provozu". Jenže když se zařízení ztratí, obraz se sám nikdy nevrátí - pokus o Reset, který selže, tedy není horší než nedělat nic, a pořád ho hlídá 60snímkový práh, aby na něj běžná jednorázová chyba kolem změny režimu nedosáhla. Přepínač přibyl do Nastavení → Obecné → ZOBRAZENÍ vedle bezrámečkového okna; na rozdíl od něj se projeví okamžitě, protože Present hook čte hodnotu až ve chvíli, kdy je zařízení ztracené. Vypnout jde dál přes device_lost_recovery = 0.

fixskinssniper HEADSHOT konečně registruje na plugin skinech

Příčina není v meshi a nic z hlavy odstraněné není - je to kostra. GTA nestřílí do modelu, ale do 12 kolizních koulí, které si přepočítává z kostry při každém výstřelu (CPedModelInfo::AnimatePedColModelSkinned @0x4C6F70). Kost, kterou kostra nemá, vrátí RpHAnimIDGetIndex jako -1 a hra tím indexem stejně sáhne do pole matic (matrixArray[-1], žádná kontrola mezí) - koule daného body partu se tím složí do rootu peda. Chybí-li BONE_HEAD (eBoneTag = 5), hlavová koule prostě není u hlavy, paprsek střely ji netrefí, a tím pádem neproběhne ani vanilla pravidlo "sniper + hlava = instant kill", ani SA:MP nemá co ohlásit jako BODY_PART_HEAD. Animace přitom vypadá v pořádku, protože ta používá jiné mapování. Nový modul src/gta/ped_hitcol.{h,cpp}: (1) těsně před nasazením custom clumpu se z ještě originální kostry sejme referenční poloha všech 12 koulí; (2) nová kostra se změří - dočasným přepsáním boneId v herní tabulce m_pColNodeInfos @0x8A6308 na neexistující hodnotu se zjistí, jak vypadá koule s chybějící kostí, a porovnáním se určí přesně (ne heuristikou, a bez sahání do RenderWare struktur), které kosti skinu chybí; tabulka se vrací v __finally; (3) hook na AnimatePedColModelSkinned pak rozbité koule sází zpět na tělo - každá "jede" na nejbližší funkční kouli posunutá o referenční rozdíl, takže chybějící hlava sedí nad živou koulí trupu a jde s pedem (chůze, běh, přikrčení). Skin se správnou kostrou se detekuje jako v pořádku a nechá se úplně na pokoji; stock pedi se nedotkne nikdy. Do logu jde po každém apply jednoznačná odpověď - buď skeleton is MISSING 2 of 12 hit bones (HEAD, NECK) (a jmenovitě tedy i co má autor skinu opravit v DFF), nebo skeleton complete, all 12 hit spheres resolve = kostra je OK a příčinu je nutné hledat jinde. Každý použitý offset se před prvním zápisem ověřuje proti datům, která spočítala sama hra (poloměry koulí musí sedět s tabulkou); při neshodě se funkce natrvalo vypne a jen zaloguje. Vypínatelné přes [assets] skin_hitbox_fix = 0. Podrobně v docs/ped-skin-headshot.md.

fixbuildCI se rozbila o te::sdk::HandleRpcHookAddress() / HandleRpcHookActive()

ty v te_sdk neexistují (ani v připnutém submodulu 45091bb, ani v jeho masteru), takže rpc_manager.cpp šel přeložit jen na stroji, kde je te_sdk lokálně upravený. Diagnostika detouru se proto počítá přímo tady: te::sdk::helper::GetHandleRpcPacketAddress() je přesně adresa, kterou si te_sdk sám předává do MH_CreateHook, a MinHook na x86 patchuje cíl 5bajtovým relativním JMP (E9) - kontrola je tedy stejná, jen bez závislosti na nezveřejněném API. Obojí pod SEH.

ciworkflow běží na všech větvích, ne jen main/develop

Plus workflow_dispatch, concurrency (starší běh téže větve se ruší) a paths-ignore na **/*.md, docs/**, web/**, ať čistě dokumentační commit nepálí dva Windows runnery (commit, který sáhne na docs i na kód, se staví normálně).

choreavodstraněn poslední W^X vzor - nikde už nežádáme PAGE_EXECUTE_READWRITE

Všech pět míst, která patchují bajty herního kódu (gta_patches.cpp, overlay.cpp freeze patch, renderware.cpp pool size, samp_pool.cpp draw distance, samp_netstat_fix.cpp jmp na cave) odemyká stránku jen na PAGE_READWRITE, hned vrací původní ochranu a flushuje icache. push 40h před VirtualProtect je statická signatura, kterou hledá řada YARA pravidel, takže se to počítá i do skóre na VirusTotalu - ne jen do behaviorálního reportu. Cena je pár instrukcí nespustitelné stránky, což může vadit jen vláknu běžícímu ve stejné 4 KB stránce; patchuje se z hlavního vlákna a žádná adresa nesdílí stránku s CdStream ani s RakNet vláknem SA:MP. Navíc přibyl /PDBALTPATH:%_PDB% (do teď se do binárky zapékala absolutní cesta k PDB z build stroje). Kompletní audit zdrojáků proti heuristickým vzorům (injekce, keylogger, persistence, download+exec, anti-debug, screen capture + exfiltrace, ...) je nově v docs/false-positives.md §6.

featsyncpeer payload v3 - rychlost, rotace a interpolační buffer na drátě

v2 vezl jen bod (pozice + yaw), a z bodů se dá rekonstruovat jenom lineární pohyb: na každém vzorku dobrzdí a na dalším škubne. v3 připojuje (trailing pole, takže starý klient i starý server dál fungují beze změny): f32 vx,vy,vz (m/s), u16 interpMs (odesílatelův skutečný interpolační buffer) a - u vozidel, pod flagem SP_PEER_FULL_ROT - plnou rotační bázi fwd.xyz/up.xyz. V reportu (C→S) jde rozšíření za clientTick; v relayi (S→C) až za serverové trailery (ping/jméno/ armour), takže starší klient parsuje byte po bytu přesně to co dřív. Akceptovaný rozsah verzí je [2, 3+8] na obou stranách - mixovaná lobby nepotřebuje negociaci. Nové PAWN gettery: SP_GetPlayerSyncVelocity / SP_GetPlayerSyncInterpMs / SP_GetPlayerSyncRotation.

featlagcompklientský sync vzdálených hráčů je plnohodnotný snapshot interpolátor

Místo dvojice prev/cur drží každý peer kruhový buffer 24 snapshotů (~1 s). Konkrétně: (1) vzorky se razítkují odesílatelovým clientTick přemapovaným přes windowed-minimum clock offset - dřív se razítkovalo časem doručení, takže se síťový jitter vykresloval přímo do pohybu; (2) Hermite interpolace přes hlášené rychlosti místo lerpu (lineárka řeže zatáčky - přesně tam mizely zásahy do strafujícího cíle), s pojistkou na úsečku, když se spline vzdálí o >2 m; (3) dead reckoning se saturací dt' = cap*(1-e^(-dt/cap)) (cap 300 ms), takže ztracený paket = plynulé pokračování a dlouhý výpadek plynulé zastavení, ne útěk přes půl mapy; (4) adaptivní interpolační buffer z naměřené rozteče paketů a jitteru (55-250 ms) místo fixních 120 ms - a reportuje se serveru; (5) aplikovaná póza se exponenciálně dotahuje (frame-rate nezávisle), snap až při driftu > 8 m. Diagnostika (velikost bufferu, jitter) je v overlayi → Připojení → Sync hráčů. Matematika je vytažená do src/network/interp_math.h a pokrytá testy (tests/test_interp_math.cpp) - včetně přetečení GetTickCount a wrapu úhlu.

featlagcompvozidla drží náklon a točí koly

Follower aplikuje plnou rotační bázi (nlerp + reortogonalizace) místo samotného headingu, takže auto na kopci nebo rampě zůstane nakloněné místo srovnání do vodorovna, a dostane i hlášenou rychlost (kola se točí, tlumiče sedí, zvuk motoru odpovídá pohybu).

featactorsnastupování i vystupování actora z vozidla se ANIMUJE, ne teleportuje

ActorCmd::EnterVehicle / ExitVehicle už nesahají na SetPedInCarDirect / RemovePedFromVehicleDirect + PedSetPosition (ped se prostě objevil v sedadle / vedle auta). Místo toho se pedovi nasadí herní CTaskComplexEnterCarAsDriver / AsPassenger (@0x6402F0 / @0x640340) resp. CTaskComplexLeaveCar (@0x63B8C0) - hra sama dojde ke dveřím, otevře je, zahraje get-in/get-out animaci a ped se posadí / vystoupí. Tasky jdou do slotu 2 (TASK_PRIMARY_DEFAULT), jediného primary slotu, na který SA:MP ProcessActors nesahá - stejná úvaha jako u CTaskSimpleCarDrive. Animaci si přehraje KAŽDÝ klient (Enter/Exit se broadcastuje), takže se streamuje příkaz, ne animace - stejně jako u sdíleného GoToPoint walku. ⚠️ Task se NIKDY nestaví v RPC handleru. První pokus to dělal a CTaskManager::SetTask spadl na AV přímo uvnitř RPC 202 (this=0, task=0, slot=2); descriptor už byl v tu chvíli odvázaný, takže actor zůstal navždy sedět a nebylo co to zopakuje - odtud hlášení "/actorexit nedělá nic". Handler teď jen zvedne exitPending/enterPending a task staví OnGameProcess další frame, tedy tam, odkud se v tomhle souboru staví všechny ostatní ped tasky (viz poznámka u CTaskSimpleCarDrive v ApplyActorState: "Doing it here right after SetPedInCarDirect was corrupting memory"). Po dobu animace se vypíná per-frame re-seat guard i on-foot pose sync (jinak by ped skočil do sedadla na prvním framu / klouzal místo lezení); po dokončení autorita rozešle finální pózu. Pojistky: nikdy proti vraku, nastupování jen z <= 30 m (jinak přímé posazení), a timeout (exit 6 s / enter 15 s) spadne zpátky na starý tvrdý eject/seat, takže zablokovaná animace nikdy nespolkne příkaz.

featactorskolizní poškození vozidla s actorem se propaguje

(ReportCmd::ActorVehDamage + ActorCmd::VehicleHealth) - když hráč nabourá do auta, ve kterém sedí actor, spočítá GTA damage jen lokálně u nabourávajícího a SA:MP ho nikdy nerozešle: pro auto s řidičem neposílá unoccupied sync a řidič actor-auta je klientský ped. Nově každý klient hlídá health navázaného vozidla a kdo uvidí propad, nahlásí ho serveru (ne jen autorita - kolizi počítá klient toho, kdo naboural). Server report ověří (existující actor + navázané vozidlo, dosah 150 m, jen směrem dolů, rate limit 200 ms/actor, relay fan-out budget), zapíše SetVehicleHealth, rozešle všem plugin klientům a vyvolá OnActorVehicleDamageSP(actorid, vehicleid, playerid, Float:amount, Float:health). Auto tak dostane damage u všech, může doopravdy vyhořet/vybouchnout a stávající OnActorVehicleDeathSP se konečně spustí i pro auto rozbité nárazem. Skriptový wrapper SP_SetActorVehicleHealth(actorid, Float:health) (funguje i na opravu).

feathudkotvení widgetů k rohům obrazovky

(SP_HUD_ANCHOR_TL/TR/BL/BR/CENTER) - HUD widget lze ukotvit ke zvolenému rohu; x,y jsou pak odstup od hrany a widget se zarovná dovnitř (pravý text je pravostranný, spodní bar roste nahoru), takže drží stejný pixelový odstup na každém rozlišení/poměru stran. SP_HudSet + Text/Bar/Gauge wrappery mají nový anchor parametr; anchor byte se posílá jen když != TL, takže starší klienti zůstávají bajtově kompatibilní. Renderer řeší zarovnání pro všechny typy.

buildcefSP_ENABLE_CEF je nyní default ON

NUI web-UI je zamýšlené UX. Build je gated: bez SP_CEF_ROOT (CEF SDK se nevendoruje) se konfigurace bezpečně přepne na build BEZ CEF s message(WARNING) místo fatal erroru, takže standardní build bez SDK stále projde.

featpeersynccustom nametagy / proximity HUD

(Overlay::DrawPeerNametags) - nad hlavou každého živého plugin peera se vykreslí kompaktní plate (HP bar, vzdálenost, [car]/[drive] marker, server-měřený ping), projektovaný world→screen z peer-sync stavu. Config peer_nametags (default OFF, ať se nedubluje se SA:MP nametagy), žádný extra network traffic - konzumuje existující peer feed. Zároveň je peer payload nově dopředně kompatibilní verzovaný (klient i server akceptují novější verzi, čtou base layout, ignorují trailing appendnutá pole) → budoucí fieldy bez flag-day upgradu.

featactorsPatrolActor - autonomní patrolování po trase

(ActorCmd::SetPatrol) - server pošle route JEDNOU (SP_ActorPatrol(playerid, actorId, points[][3], count, speed, flags)), klient-autorita pak sám walkuje waypoint→waypoint a streamuje chůzi ostatním po existujícím on-foot pose syncu → žádný per-step server traffic. Režimy SP_PATROL_LOOP / SP_PATROL_PINGPONG / SP_PATROL_ONCE, až 16 bodů, SP_ActorStopPatrol route zruší. Route přežije stream cyklus (re-issue GoToPoint na stream-in) i handover autority. Capability PatrolActors je nově inzerovaná v handshaku. Kombat i patrol se nebijí (combat pin má přednost).

featobjstreamplná 3D rotace streamovaných objektů

SP_CreateStreamedObject / SP_BroadcastCreateObject berou navíc rx (pitch, X) a ry (roll, Y) ve stupních (dosud jen rz heading). Klient je aplikuje přes herní CMatrix::SetRotate (@0x59B120, 1.0 US) na matici vytvořeného CObject, takže rotace odpovídá přesně SA:MP konvenci SetObjectRot(rx, ry, rz). pitch/roll jsou VOLITELNÉ trailing wire fieldy (posílané jen když nenulové) → objekty jen s headingem zůstávají bajtově kompatibilní se staršími klienty. Pořadí pawn parametrů je nově (rz, rx, ry, drawDist) - drawDist se přesunul na konec, aby rotační argumenty šly postupně (wire pořadí zůstává rz, drawDist[, rx, ry], na parametrech nezávislé).

featradiosynchronizované rádio "SP Radio"

(docs/radio-sync.md) - server-driven stanice s playbackem ukotveným na UNIX čas: pozice = (now - anchor) % délka, takže všichni naladění slyší stejný moment (~±100 ms, dle NTP hodin klientů). Stanice = běžný asset (jedna smyčkovaná MP3 -> i VC/III rádia), klient dekóduje přes Media Foundation (MP3/WAV/AAC/WMA) do waveOut na vlastním vlákně; vehicle-only režim se po nastoupení RESYNCNE na živé vysílání. Wire SP_EVT_RADIO_SET, pawn SP_RadioPlay/Stop/PlayAll (SP_CAP_RADIO), demo /radio + /radiooff. Nativní stanice (K-DST...) jsou u každého klienta procedurální a synchronizovat jejich OBSAH nejde - "synced K-DST" = jeho hudba jako custom stanice.

featradionapojení na nativní rádio

(config radio_native_hook, default ON, opt-out přes sp-plugin.ini; SEH-guarded) - čtení aktuální stanice (report změny → OnPlayerRadioStationSP), serverové vynucení stanice (SP_SetNativeRadio, SP_SetVehicleNativeRadio pro celou posádku - "všichni v autě slyší řidičovu stanici") a auto-mute nativního rádia, dokud hraje SP stream. SP Radio se v radio-wheelu ladí přes slot User Track Player (finální řešení - viz docs/radio-sync.md; pravý extra index byl zvážen a zamítnut kvůli crash-riziku patche cyklovací logiky).

featnuiNUI web-UI framework

(docs/cef-ui.md) - FiveM-style HTML/JS vrstvy: wire SP_EVT_UI_OPEN/CLOSE/MESSAGE + SP_RPT_UI_EVENT, pawn SP_UiOpen/SP_UiClose/SP_UiMessage + OnPlayerUiEventSP (flood-guard, délkové limity), klientský NuiManager (až 8 vrstev, D3D9 blit v Present hooku, WndProc input capture, JS bridge sp.emit/sp.onMessage) nad IBrowserBackend rozhraním. CEF (Chromium) OSR backend je napsaný v src/ui/cef_backend.cpp za CMake volbou SP_ENABLE_CEF (default OFF - CEF SDK se nevendoruje; drop-in kroky v docs). Klient bez backendu SP_CAP_NUI neinzeruje a UiOpen lokálně odmítne - server VŽDY gatuje na capability.

Opraveno (nerozpoznaný samp.dll build)

fixsampnerozpoznaný samp.dll už neshodí VŠECHEN přístup k SA:MP entitám

SampPool::Init dřív při neznámém buildu vrátil false a GetActorPed, GetVehiclePtr i GetPlayerPed pak natvrdo vracely nullptr. Navenek to nevypadalo jako chyba detekce verze, ale jako "nefungují skiny": SP actoři nikdy nedostali CPed* (SpActorSpawn ... (no CPed yet) donekonečna), vozidlový skin se nenasadil na už streamnuté auto (RefreshVehicleInPlace se ani nezavolal, takže refreshedLive=false a jelo se jen na restream ze serveru), a PedHitCol sweep viděl jen lokálního hráče → 23 of 24 replaced ped model(s) not measured yet, což vypadá jako "ty skiny nikdo nemá". Jediná stopa byl jeden [WRN] na začátku logu. Nově se přebere profil offsetů podle samp_fallback_version (default R5).

featsamprozpoznání 0.3.DL-R1

podle build markeru 0x5A6A3130 na samp.dll+0x128 (TimeDateStamp = 2018-01-25, den před releasem DL R1). Pozor na dvě RODINY PE hlaviček: R1/R2/DL linkují s e_lfanew=0x120 (marker na +0x128), R3-R5 s e_lfanew=0x118 (marker na +0x120) - přečíst 0x00004550 (PE\0\0) na +0x120 tedy samo o sobě vylučuje R3-R5. Offsety pro DL jsou komunitní, ne z IDA, a jsou proto označené jako neověřené.

fixsampneověřené offsety musí nejdřív obhájit pPools

Nový PoolsLookSane() vyžaduje, aby tabulka na pPools vypadala jako stSAMPPools (9 slotů, ≥3 různé ukazatele do commitnuté user-space haldy), než se přes ni cokoli indexuje. Platí pro odhadnutý fallback profil i pro detekované DL; u ověřených 0.3.7 buildů je to no-op.

featsampdo logu se vypíše verzní řetězec ze samotné samp.dll

('0.3.7-R5', '0.3.DL-R1', ...). Spor "hráč tvrdí, že má R5" se dosud řešil ručním dekódováním bajtů PE hlavičky z logu - a odpověď se s tvrzením neshodovala.

fixuiřádek "SA:MP klient" v overlay označí odhad jako odhad

S fallbackem by jinak svítil zeleně "0.3.7-R5" i u buildu, který jsme nikdy neidentifikovali.

Opraveno (instalátor)

fixinstallerpryč se zaškrtávátkem LAA na závěrečné stránce

Bylo nadbytečné (PatchLaa ten patch udělal už během instalace, s elevací a zavřenou hrou - sp-laa.ps1 pak jen napsal already enabled - nothing to do), stálo hráče dvě potvrzení navíc (Ano/Ne + UAC po dokončené instalaci) a vypadalo rozbitě: MSI podkládá zaškrtávátko na ExitDialogu neprůhlednou bílou plochou, takže se přes grafiku táhl bílý pruh přes čtvrtou odrážku. Když PatchLaa neuspěje, hráč o tom ví z karty VELKÝ ADRESNÍ PROSTOR (LAA) v overlay, která čte skutečný stav bitu a umí ho zapnout; Zapnout 4GB (LAA).cmd zůstává vedle hry pro ruční spuštění.

Opraveno (lag compensation & sync)

opravenosec(lagcomp): klientem hlášený interpMs se clampuje na 300 ms

Je to jediné v3 pole s přímým exploitem - nahlas obrovský buffer a server rewindne cíle dál dozadu, tedy přesně ta výhoda "střelba za roh", kterou má rewind omezovat. Poctivý strop je klientovo vlastní maximum (250 ms), cokoli výš je lež → bere se jako neznámé.

fixlagcompklient nehlásí svůj interpolační buffer, když má smoothing vypnutý

Hlásil by svou (nepoužitou, minimální) hodnotu 55 ms, jenže s vypnutým smoothingem interpoluje SA:MP se svým podstatně větším bufferem - server by rewindoval málo a zásahy by padaly za pohyblivý cíl. Nově se posílá 0 = "neznámé" a server použije vlastní odhad.

fixlagcompneinterpoluje se přes MEZERU mezi vzorky

Když se peer na chvíli odmlčel (streamnul se ven) a vrátil, byly poslední dva vzorky sekundu daleko od sebe; blend mezi nimi táhl hráče kus cesty zpátky, což pak spustilo snap a viditelně cuklo. Nad kMaxSpanMs / SP_LC_MAX_SPAN_MS (400 ms) se bere čerstvý vzorek (klient) resp. lineární interpolace místo splinu (server).

fixlagcompzlepšení latence už peera nezmrazí

Když se offset baseline přeukotví níž, posune se celá časová osa dozadu a razítka nakrátko "regresují". Původní kód je zahazoval jako reorder, takže peer stál, dokud se osa nedotáhla. Teď rozhoduje odesílatelův clientTick: neposunul se → skutečný reorder/duplikát (zahodit), posunul se → jen se pohnulo mapování (přijmout).

fix!lagcompopravený model latence rewindu

Historie se razítkuje časem odeslání (clientTick přemapovaný na server clock), a za toho předpokladu vyjde rewind = ping_střelce + interp - ping cíle se vykrátí, protože jeho uplink už je v razítku obsažen. Starý vzorec sp/2 + tp/2 + interp tedy podstřeloval o půl pingu střelce a navíc připočítával cíli jeho vlastní ping; proto zásahy lagujícího střelce na pohyblivý cíl padaly za cíl. interp je nově střelcova skutečná hodnota z payloadu v3, ne konstanta. Odvození je rozepsané u _SP_LagCompViewDelay a v docs/lagcomp.md §7.2.

fixlagcomprewind interpoluje po stejné křivce jako klient

_SP_LagCompPosAt je Hermite přes hlášené rychlosti (fallback na lineární pro hráče bez pluginu/v3), a zásah se testuje v okně kolem spočteného času (SP_LC_REWIND_WINDOW_MS, 60 ms) - počítá se nejlepší okamžik. Model je dobrý, ne přesný, a každá milisekunda chyby je u pohyblivého cíle vzdálenost, kterou by jinak zaplatil hráč neuznaným zásahem. Tolerance navíc roste se vzdáleností střelby (SP_LC_RADIUS_PER_100M), takže fixní poloměr už netrestá střelbu na dálku.

fixlagcomppózu vozidla smí zapsat jen řidič

Dřív ji psal každý sedící peer, takže auto s pasažérem kmitalo mezi dvěma skoro stejnými pózami každý frame.

fixlagcompinterpolace on-foot hráčům nenuluje rychlost

PedSetPosition nulovalo m_vecMoveSpeed 60×/s, ze které si SA:MP odvozuje blend chůze/běhu - hráči klouzali v idle póze. Nový PedSetPositionKeepVelocity píše jen pozici.

fixsyncklient posílal 3× víc peer reportů, než server relayoval

Klient jel 66 Hz, server floor byl 50 ms (20 Hz), takže se dvě třetiny paketů zahodily ještě před fan-outem. Klient teď jede ~30 Hz (peer_sync_rate_hz, 10-66) a serverový floor je 30 ms. Plynulost dělá kvalita interpolace, ne hrubá frekvence.

Změněno (konsolidace custom RPC protokolu + cleanup, viz docs/protocol.md)

feat!protocolkanálový wire protokol

27 per-action custom RPC id (200-243) nahrazeno handshake párem + 5 kanály: SP_HELLO (200), SP_HELLO_ACK (201), SP_ACTOR_CMD (202), SP_EVENT (203), SP_SYNC (204), SP_ASSET (205), SP_REPORT (206). Každá kanálová zpráva = u8 command id + payload (bajtově shodný s původním wire - žádný parser se neměnil). Nové featury od teď berou command id, nikdy nové RPC id - id 207-255 (49 slotů) zůstávají trvale volné. Overhead +1 B/zprávu. Plugin zatím nebyl vydán, takže stará per-action id NEMAJÍ kompatibilní shim - kanálový protokol JE protocol v1; klient i server si verzi dál negotiují v handshaku pro budoucí evoluce.

refactorcleanupmrtvý kód pryč

nepoužívaná IRpcHandler abstrakce (rpc_handler_base.h) + HelloHandler::Handle stub, mrtvé actor-animation handlery (173/174) a nepoužívaná pole ActorData.anim*, nepoužívané konstanty (RPC_GIVE_ACTOR_DAMAGE, SP_RPC_PREFIX), nevolaná ActorManager::ReadString, zakomentovaný WorldVehicleAdd model-rewrite (+ opravený zavádějící log), mrtvý klíč protocol_version v sp-plugin.ini.

refactorcleanup6 kopií čtečky u8-length-prefixed stringů

sjednoceno do include/sp_bitstream.h (sp::net::ReadU8String) - discord/statebag/notify/ world-texture/HUD čtou stejným bounded helperem.

docsdocs/protocol.md

kompletní specifikace protokolu (id mapa, command tabulky, pravidla verzování/rozšiřování, zdůvodnění každého rozhodnutí); opravené zastaralé wire komentáře v sp_rpc_ids.h (HUD_SET x2/y2, HIT_EVENT targetId, APPLY_DONE refreshedLive).

choreversion0.1.0 → 1.0.0

(první release kandidát).

Fixed / Added (technical audit & bug-fix sprint - viz docs/technical-audit.md)

fixgtapád na dopravních cedulích (CEntity::CreateEffects)

z kolegova crash dumpu (0xC0000005, read @0x00000004, eip=0x0053388E). Vanilla GTA nekontroluje návratovou hodnotu CCustomRoadsignMgr::CreateRoadsignAtomic:

  0x533887  call 0x6FF2D0     ; vrátí NULL při neúspěšné RenderWare alokaci
  0x53388C  mov  ebx, eax
  0x53388E  mov  edi, [ebx+4] ; ← AV

Ta funkce vrací NULL na šesti alokačních cestách (RpMaterialCreate, RwRasterCreate, RwRasterLock, RwTextureCreate, RpGeometryCreate, chybějící roadsign font globals), takže stačí, aby se do view dostal objekt s cedulovým 2dfx ve chvíli, kdy RW alokace selhávají

  • v hlášeném případě pár sekund po stažení ~7,4 MB custom assetů.

Oprava přesměruje ve skokové tabulce efektů slot 7 (EFFECT_ROADSIGN) na 0x533B9C - na stejný cíl "není co stavět, jdi na další efekt", kam už dispatchují nevyužité typy 2/4/5/8/9. Cedule zůstanou, přijdou jen o nápis. ⚠️ Napoprvé to byl detour s null checkem do code cave a bylo to špatně: cedulová větev přepisuje EDI ukazatelem na text (0x533839 mov edi,[esi+28h]), zatímco 0x533B9C počítá s tím, že EDI je pořád čítač efektové smyčky (inc edi / cmp edi,eax / mov [esp+1Ch],edi). Skok doprostřed větve tím rozbil index smyčky a hra odletěla do neznáma - AV exec @ 0xFFFFC98C, v registrech esi/edi = bitové vzory 2.383f/1.4f (rozměry cedule) a ebp=3 (počet řádků). Přeskočení přes skokovou tabulku do té větve vůbec nevstoupí, takže EDI zůstává nedotčené a na cíl se přijde přesně tak, jak na něj chodí sama hra. V GUI jako Pády: dopravní cedule (patch_roadsign_crash, default zapnuto, vypnutelné za běhu).

fixgtakaždé přiřazení primary tasku jde přes PedSetPrimaryTaskSafe

CTaskManager::SetTask začíná mov esi,ecx / mov ecx,[esi+edi*4], takže nulové this fauluje na adrese slot*4 (odtud AV read @ 0x00000008, eip=0x681B01). Tenhle pád dorazil do hry s this i task nulovým, přestože je oba call sites pár řádků nad tím kontrolují - a příčinu se zatím nepodařilo najít: ctor 0x63B8C0 (retn 14h, obnovuje esi/edi), CTask::operator new 0x61A5A0 (cdecl tail-jump) i samotný SetTask (retn 0Ch) jsou z disassembly ověřeně vyvážené a slot offsety 0x00/0x14 potvrzuje adresování [esi+edi*4] resp. [esi+edi*4+14h]. Než se to vysvětlí, wrapper volání s nulou odmítne a zaloguje REFUSED SetTask(mgr=… task=… slot=…) - chybějící animace plus jeden řádek v logu je lepší než spadlá hra. Není to root-cause fix, je to pojistka.

fixgtaApplyCarDriveTask volal ctor s 1 argumentem místo 3 → koroze zásobníku

CTaskSimpleCarDrive(CVehicle*, CTaskUtilityLineUpPedWithCar*, bool updateCurrentVehicle) bere TŘI parametry (ověřeno z binárky - 0x63C340 končí retn 0Ch - i z gta-reversed), deklarovaný byl jen s vozidlem. U __thiscall to selže na obě strany: * ctor si chybějící utility-task pointer přečetl ze smetí na zásobníku a dereferencoval ho → AV na 0x63C3E0 (mov edx,[edi+18h], edi = smetí) hned po spawnu k vehicle actorovi; * callee uklízí 12 bajtů, caller pushnul 4 → volajícímu zůstal zásobník o 8 bajtů posunutý, takže hned následující načtení taskMgr/task vrátilo nuly → to je ten dřívější pád CTaskManager::SetTask(this=0, task=0, slot=2) na 0x681B01. Jedna chyba, dvě různá místa pádu. Spala jen proto, že kvůli posunutým slot offsetům (níže) sonda "je slot 2 volný?" nikdy nehlásila volno a tahle cesta se skoro nevolala - oprava offsetů ji odkryla. Arity VŠECH ostatních volaných ctorů jsem proti binárce přepočítal, sedí.

fixgtaCTaskManager slot offsety byly posunuté o 4 bajty

OFF_TASKMGR_PRI_BASE / OFF_TASKMGR_SEC_BASE byly 0x04 / 0x18, správně je 0x00 / 0x14 (plugin-sdk CTaskManager.h i gta-reversed, obojí VALIDATE_SIZE 0x30). Původní komentář popisoval layout CPedIntelligence (tam m_pPed OPRAVDU je první a CTaskManager je inline na +0x04 - a přesně to už PedGetTaskManager vrací), takže se ty dva structy popletly. Důsledek: každý ručně psaný přístup do slotu sahal o slot vedle - test "je primary slot 2 volný?" četl slot 3 (a tak ApplyCarDriveTask běžel každý frame místo jednou), PedSetSecondaryTask nuloval DUCK místo ATTACK (takže MakeAbortable guard, kvůli kterému existuje, nikdy nezafungoval) a PedClearAttackTask mazal špatný sekundární slot. Volání přes herní SetTask/SetTaskSecondary byla vždy v pořádku - berou INDEX slotu, ne offset. Střelbu a patrol je potřeba přetestovat, chovají se teď podle původního záměru.

fixrenderNUI vrstva zanechávala render state ve hře → černá obrazovka

NuiManager::Render nastavoval ALPHABLENDENABLE/SRCBLEND/DESTBLEND/ZENABLE, FVF a texture stage bez state blocku a nikdy je nevrátil (PostFX i ImGui to dělají správně). RenderWare si render state cachuje, takže co považuje za nastavené, znovu neposílá - hra pak renderovala s vypnutým Z-testem a cizím blendem. Nově obalené CreateStateBlock(D3DSBT_ALL)Apply(), stejně jako v PostFX.

fixrenderoverlay se už neinicializuje ani nerebuilduje na ztraceném device

Overlay::Init běžel při každém Presentu bez ohledu na TestCooperativeLevel, takže při initu na LOST device tiše selhaly všechny D3DPOOL_DEFAULT textury, ale m_imguiReady se stejně zalatchovalo → overlay bez loga/pozadí až do dalšího Resetu. A CreateDeviceObjects() se volalo přímo UVNITŘ herního Reset(), což je CoCreateInstance(WICImagingFactory) + dekód stovek kB obrázků na alt-tab návratové cestě (dlouhé černé okno + COM reentrance v citlivém místě). Teď je init gatovaný na deviceOk a rebuild odložený na první Present po Resetu.

fixrenderC++ výjimka z render cesty už nenechá device v BeginScene

__except v RenderGuarded chytá jen SEH a build jede na /EHsc, takže throw z Overlay::Render nebo CaptureManager::OnPresent (obojí alokuje std::string/std::vector) proletěl skrz OnPresent s otevřeným scene bracketem → hra kreslila do scény, která se nikdy neprezentovala. RenderGuardedBody má nově vlastní try/catch(...) a EndScene() se volá vždy.

fixdiagwatchdog už nehlásí hang při každém alt-tabu

GTA má ve WinMain idle větev Sleep(100) pro nefokusované okno, která přeskakuje render path; heartbeat se ale bumpuje z CGame::Process, takže každé přepnutí na plochu vypadalo jako hang a vysypalo 12řádkový HANG-PROBE stack zaparkovaný v ntdll!NtDelayExecution. Ten šum přehlušoval skutečné hangy. Watchdog teď stall neresetuje, pokud foreground okno nepatří našemu procesu.

featdiagD3D device-state trace

OnPresent loguje každou ZMĚNU TestCooperativeLevel (+ 1 Hz heartbeat a počítadlo ztracených framů, dokud je device lost) a selhaný Present HRESULT; OnReset loguje každý pokus o Reset, ne jen selhání. Tím jde z jednoho logu rozlišit tři jinak nerozeznatelné stavy: device se nikdy neztratil (černá je herní) / device visí v DEVICENOTRESET a Reset nikdy nepřijde / Reset přišel a selhal (leak na naší straně).

fixactorsgunner - animace střelby

skutečný root cause "kulky bez animace": scriptovaná LOOPOVANÁ idle anim (Coplook_idle) se blendovala přes peda a maskovala aim/fire animace combat tasku. Server ji teď před každým shoot příkazem maže (ClearActorAnimations); driver střelby ZŮSTÁVÁ plnohodnotný CTaskComplexKillPedOnFootArmed (kontinuální automatická palba, funkční ammo-detekce → damage + callback). Mezi-iterace se samostatným UseGun taskem byla slepá ulička dvakrát: (1) extended-vtable task ve slotu = rozbitý stack → crash na /guardshoot (slotované tasky MUSÍ mít genuine vtable - viz warning v gta_funcs.h), (2) samostatný UseGun s CMD_FIRE vystřelí JEDNU ránu a skončí (je to sub-task komplexního tasku) → "střílí po 1", nefunkční detekce. Poziční genuine-vtable UseGun (ApplyShootPosTask) zůstává jen pro POZOROVATELE bez resolvnutého targetu - jeden aim/výstřel na relayed SHOT_EVENT (RPC 238) přesně sedí na cadence tracerů. Nutný rebuild .asi + rekompilace filterscriptu.

fixactorsrotace řidiče přesně sedí na vozidlo

actor ped v autě neprochází normálním in-vehicle attachment updatem, matrix mu zůstávala stará. Nový PedMatchVehicleRotation kopíruje rotační řádky matice vozidla na peda každý frame (řidič i pasažér, včetně náklonu ve svahu).

fixai-driveauto už nemizí / neskáče zpátky

server se nikdy nedozvěděl, kam klientská CarAI auto dovezla → SA:MP streamoval/rekonstruoval vůz ze STARÉ pozice. Server teď průběžně bookkeepuje pózu (SetVehiclePos/SetActorPos, throttle 1 s) z relayed reportů; klient na sync-ovaných vozidlech konzumuje echo nativních RPC 159/176 (nový RpcManager::ConsumeCurrentRpc); >5m snap-korekce z SP_UpdateActor se během živého drivu přeskakuje; při předání authority server novému klientovi znovu pošle drive příkaz (auto už nezamrzne).

featactorssynchronizace pozice pěších actorů

pose report/relay pipeline (RPC 219/221) rozšířen z vozidel i na pěší actory: authority sampluje jdoucího peda (~20 Hz jen při pohybu + arrival settle), followeři běží lokální GoToPoint (animace) a korigují se jen při driftu >1,5 m. Ostatní hráči už vidí přesunuté actory správně.

featauthoritynejbližší hráč + hystereze + PINNING

SP_ElectActorAuthority už nebere nejnižší ID, ale NEJBLIŽŠÍHO eligible plugin hráče (preference streamed-in), s hysterezí 30 m + 5 s dwell proti ping-pongu; periodický re-elect tick (2 s). Explicitní SP_SetActorAuthority autoritu PINuje - auto re-elect ji nikdy nepřepíše, dokud je hráč validní (review finding: tick přepisoval demo pin guardova targetu); pin se uvolní odchodem hráče nebo dalším SP_ElectActorAuthority. Wire (RPC 220) beze změny.

fixlagcomptrojité respawny při lagcomp 0

pod lagcompmode 0 aplikuje damage z UseGun tasku samo GTA (skutečné kulky, správná death pipeline); manuální zápis health se tam už nedělá (dvojí damage + race se SA:MP death mašinérií byl zdroj opakovaných respawnů). Pro lagcomp 1/2 přidán death-boundary latch (3 s grace po killing blow).

featactorsactor damage oběma směry

hráč→actor jede přes NATIVNÍ OnPlayerGiveDamageActor (demo ho nově obsluhuje - dummy konečně DOSTÁVÁ damage; SA:MP damage sám neaplikuje, skript musí SetActorHealth). Směr, který nativní API nemá - actor střelil HRÁČE - pokrývá nový RPC 242 SP_ACTOR_HIT_PLAYER: klient oběti reportuje každý zásah gunnera, server validuje (flood/amount/range) a volá forward OnActorGiveDamagePlayerSP(actorid, playerid, Float:amount, weaponid) (amount 0.0 = damage aplikoval engine pod lagcomp 0). Původně navržený player→actor report kanál byl na žádost zrušen jako redundantní.

fixrenderblack screen

saturation boost (>100 %) se přeskakuje na multisamplovaném backbufferu (StretchRect resolve na některých driverech tiše vrací černou → celý frame přepsán černě); postfx_satboost_msaa=1 ho na ověřeném GPU force-povolí. Overlay/PostFX draw v Present je nově pod SEH; Present/Reset ignorují cizí (non-HAL) devicy jiných overlayů; failed Reset se loguje s HRESULT.

fixsyncreview hardening

echo-consume nativních RPC 159/176 je nově DISTANCE-gated: bookkeeping echo (blízko živé pózy) se konzumuje, skutečný skriptový teleport (daleko) projde a shodí sync latch; authority má freshness okno přes arrival transition (echo za jízdy už neteleportuje čerstvě zaparkované auto); server při jízdě aktualizuje i pózu ACTORA (SetActorPos), takže řidič správně streamuje hráčům v cíli (předtím dojelo auto bez řidiče).

featlagcompVLASTNÍ lagcomp postavený na SP peer-sync kanálu

přepis sp_lagcomp.inc: historie z ~66 Hz peer-sync reportů (SP_LagCompRecordTimed s clientTick odesílatele; 25 Hz sampler jen fallback pro ne-plugin hráče), jitter-korigované časy vzorků přes windowed-min clock-offset, rewind = ping_cíle/2 + ping_střelce/2 + interp s EMA pingy (starý vzorec = plný RTT, přestřeloval), strop rewindu, forward OnLagCompShotFlagged (return 1 = povolit i v ENFORCE), diagnostika SP_LagCompChecked/Flagged/Rejected/Ping. Relay 223 nově appenduje volitelný u16 pingMs (kompatibilní oběma směry); klient ho drží v PeerSyncManager::Peer::pingMs; sp_plugin.inc ukládá sp_syncClientTick + getter SP_GetPlayerSyncClientTick. Viz docs/lagcomp.md §6. Rebuild .asi (volitelný - jen kvůli ping fieldu) + rekompilace filterscriptu.

fixactorsgunner - armed task ponechán, damage na všech módech

driver střelby zůstává CTaskComplexKillPedOnFootArmed (na vyžádání; UseGun auto-retrigger experiment vrácen zpět) + anti-stuck re-apply prázdného primary slotu. Manuální damage (1 HP/ránu + death-boundary latch proti multi-respawnu) běží na VŠECH lagcomp módech - engine actor kulkám damage lokálního peda reálně neaplikuje ("id 1 nebere HP"). Pin drží pozici (on-foot sync je při střelbě vypnutý, kotva už nenásleduje approach pohyb tasku). Rebuild .asi.

fixradiomute přes HLASITOST místo StopRadio (přepínání + herní HUD)

StopRadio rádio logicky VYPNUL: klávesa přepínání ho pak jen znovu zapínala na tutéž stanici (z User Tracks nešlo odejít) a nekreslil se ani herní HUD s názvem stanice. Nově se mutuje CAudioEngine::SetMusicMasterVolume(0) (re-assert 4 Hz) - rádio zůstává zapnuté, stanice se normálně cyklí (vč. HUD labelu), poll vidí skutečnou změnu → SP stream se pustí a kanál se obnoví na hráčův prefs volume.

fixradioplynulý visualizer

spektrum se ukládá surové per buffer (dekódovací kadence je hrubá) a vyhlazuje se až v overlay per-FRAME (attack 22/s, release 5/s) - žádné zasekávání.

fixoverlayPERMANENTNĚ zamrzlá myš po zavření menu

root cause v SetGameInputFrozen: capture originálních bajtů běžela přes JEDEN globální flag a sejmula, co zrovna na adrese bylo. Když capture proběhla ve chvíli, kdy už byl site zamrznutý (partial apply / re-entrance / pořadí volání), uložila frozen bajty (NOPy 0x90) JAKO originál → další zavření overlaye "obnovilo" NOPy → čtení myši zůstalo mrtvé napořád. Nově per-site capture s tvrdou pojistkou: originál se sejme JEN když aktuální bajty nejsou frozen vzor - frozen bajty se jako originál uložit nemůžou.

fixoverlayfreeze myši po zavření menu ALT+S

spolknutí WM_SYSKEYDOWN toggle kombinace nechalo Windows v domnění, že byl stisknut samotný ALT; jeho puštění pak přes DefWindowProc aktivovalo MODÁLNÍ menu smyčku (SC_KEYMENU) - message pump hry se zastavil a obraz i myš zamrzly do dalšího stisku klávesy. WndProc teď SC_KEYMENU polyká (herní okno žádný menu bar nemá). Rebuild .asi.

fixactorsgunner - server-autoritativní "target jsi TY"

čtení lokálního player id ze samp.dll je verzně křehké; špatný read na klientovi id 1 (vracel 0) způsobil OBĚ hlášené chyby naráz: (a) při shoot-at-0 se id 1 považoval za target → resolvnul VLASTNÍHO peda → guard "střílel po obou a bral HP oběma"; (b) při shoot-at-1 id 1 hledal hráče 1 jako remote (peer-sync o sobě nechodí - relay přeskakuje odesílatele) → nic nenašel → guard stál a jakožto authority nikdo neviděl nic. Shoot příkaz má nově volitelný trailing byte isLocalTarget (server ví autoritativně, komu posílá); klient jím řídí resolve i damage gate, id-compare zůstává jen jako legacy fallback. Kompatibilní oběma směry. Rebuild .asi + rekompilace filterscriptu.

fixactorsgunner event-fallback u streamnutého hráče

resolve remote peda přes SA:MP pool (CPlayerInfo→CRemotePlayer→wrapper→ped) je ověřený jen pro R1 a na jiných buildech tiše selhává → "NO_VALID_TARGET(event-fallback)" i když oběť stála vedle. Nový VERZE-NEZÁVISLÝ fallback: remote ped se najde skenem GTA ped poolu (0xB74490, gta_sa global) ukotveným na ŽIVOU pozici hráče z peer-syncu (≤3 m, exclusion list = lokální ped + všechny actor pedy, SEH-guarded). Armed task se teď aplikuje na správného peda na každém klientovi bez ohledu na SA:MP verzi. Rebuild .asi.

opravenotweak(radio): visualizer - delší bary + max plynulost

hop analýzy 50 → 25 ms (~40 snapshotů/s), bary 4+36 px (z 3+22), easing attack 40/s + release 10/s, a úrovně sousedních pásem se mezi bary INTERPOLUJÍ (žádné skoky mezi segmenty prstence - plynulá vlna kolem gauge).

fixradiovisualizer - 20 Hz hop analýza

jeden snapshot na celý MF buffer (~4 Hz) byl na 120+ BPM moc hrubý i se správným časováním. Buffery se teď analyzují v ~50ms krocích (jeden časovaný snapshot na krok, okno 4096 vzorků se posouvá) → ~20 snapshotů/s publikovaných přesně na playback pozici; overlay easing doladěn (attack 28/s, release 8/s). Bary jedou S hudbou.

fixradiovisualizer drží RYTMUS

tři chyby výpočtu: (1) analýza běžela při DEKÓDOVÁNÍ, ale waveOut fronta drží ~2 s zvuku → bary pulzovaly sekundy před tím, co slyšíš; snapshoty pásem se teď publikují až když jejich audio skutečně hraje (waveOutGetPosition, per-buffer fronta snapshotů, reset s waveOutReset). (2) Okno analýzy bylo jen jeden MF buffer (u MP3 ~26 ms → 60Hz pásmo ~1,5 periody = šum); nově rolling mono okno 4096 vzorků (~93 ms). (3) Bez auto-gainu hlasité masterované tracky držely pásma u stropu a "nedýchala"; nově normalizace proti pomalu klesajícímu per-band peaku (plný 0..1 rozsah pro beat) + gamma 1.5 na prohloubení údolí. Rebuild .asi.

fixradiosmyčka zapínání/vypínání (toast spam)

hra vrací zvolenou stanici i po RadioStop (hlavně User Tracks slot), takže se re-sighting téže stanice bral jako "hráč přepnul" → release → report → server re-tune → mute → dokola. Klient si teď pamatuje, KTEROU stanici SP stream potlačuje (m_suppressedStation, reset per session) - záměr hráče = pouze JINÁ stanice. Demo navíc duplicitní user-tracks report ignoruje, když už SP Radio hraje (žádný re-tune/re-toast).

fixradiopřepínání stanic při hrajícím SP streamu

mute logika dřív zabila KAŽDOU nativní stanici, kterou hráč zapnul (a změnu ani nereportovala serveru) → z SP Radia se nešlo přepnout pryč. Nově: první stanice po nástupu do auta = auto-start → mute (SP stream drží reproduktory); KAŽDÁ další = záměr hráče → SP stream se lokálně okamžitě pustí, nativní rádio hraje a změna se reportuje serveru (demo: reálná stanice odladí SP Radio, User Tracks slot ho naladí zpět).

featradio/radiocur zná i nativní stanice

include ukládá poslední reportovanou nativní stanici (SP_GetPlayerNativeRadio); /radiocur ukáže SP stream (synced) NEBO K-DST & spol. jménem.

featradiohlasitost řídí HERNÍ slider hráče

efektivní volume = server volume × Options→Audio radio volume (0..64, CMenuManager @0xBA6798), sledováno živě za přehrávání - SP stream se chová jako nativní rádio.

featradioaudio visualizer okolo gauge speedometru

při hrajícím SP streamu pulzuje kolem gauge 32 radiálních barů podle živého 8-pásmového spektra (Goertzel nad PCM ve worker vlákně, fast-attack/slow-decay; basy dole, výšky nahoře, zrcadleno). Nulová cena, když rádio nehraje. Rebuild .asi + rekompilace filterscriptu.

fixradio"No station file" - scanner neznal .mp3

SP_AssetScan (server plugin sp-samp-server-fixes) klasifikoval jen .dff/.txd a mp3 tiše přeskočil → SP_AssetIdByName("sp_radio.mp3") = -1, i když soubor ležel v scriptfiles/assets/radio (nebyl to remote/Linux problém). Nový typ 3 = SP_ASSET_AUDIO (.mp3/.wav/.ogg). Nutný rebuild server pluginu (./build.sh přes Docker pro Linux .so; Windows .dll ověřen lokálně).

featradiointernetové streamy + .pls stanice

RadioSet mode 3: klient přehraje přímo http(s) audio stream přes Media Foundation (žádný asset transfer, žádný anchor - live stream je sdílený z principu; auto-reconnect při výpadku, vehicle-gate zachován). Include: SP_RadioPlayUrl(/All) + SP_RadioPlayPls (server rozparsuje .pls a vytáhne první FileN= URL). Demo /radio fallback: bez mp3 assetu zkusí assets/radio/sp_radio.pls.

featradio/radiocur

SP_GetPlayerRadioName (server-side evidence naladěné stanice, set/clear v Play/PlayUrl/Stop/ResetPlayer) + demo příkaz zobrazí aktuální synced stanici. Rebuild .asi + rekompilace filterscriptu.

fixai-drivecrash při /driveto na PŘEVRÁCENÉ/shořelé auto

drive mašinérie se aplikovala na vrak (řidič-ped umřel s autem, část stavu vozidla už uvolněná) → AV v konstruktoru CTaskSimpleCarDrive (gta_sa+0x23C3E0, potvrzeno crash-probe logem kolegy). Nový WRECK GUARD: dokud má bound vozidlo health <= 1, drží se VŠE - seat/re-seat, engine, CarAI, CarDrive task, follow i okamžitý DRIVE_TO apply (v handleru 207 i v ApplyActorState). Jízda se obnoví, až server auto respawne (OnActorVehicleDeathSP → respawn → fresh CVehicle, latch se sám re-armuje). Rebuild .asi.

refactoractorsgunner - SYMETRICKÝ armed task (finální model)

server pošle "střílej na hráče X" a KAŽDÝ klient, který X resolvne (lokální ped u X, jinak X-ův remote ped), aplikuje skutečný CTaskComplexKillPedOnFootArmed s tímto pedem jako targetem → všichni vidí plnohodnotný combat na X. Každý klient si kreslí tracery vlastních výstřelů (kadence 1:1 s muzzle flash); authority (auto-pin na X) navíc reportuje 237 → relay 238 slouží JEN klientům bez streamnutého X (aim póza + CWeapon::Fire zvuk/záblesk + tracer). Damage aplikuje výhradně X-ův klient (1 HP/ránu + latch) + report 242. Rebuild .asi + rekompilace filterscriptu.

fixactorspozorovatel gunnera SLYŠÍ a VIDÍ střílet

poziční UseGun s CMD_FIRE na observer pedovi nikdy nevystřelil (weapon state není READY, actor nehraje reload anim → věčný aim/reload cyklus = "furt odměřuje"); navíc recreace tasku per event restartovala aim animaci. Nově: UseGun s CMD_AIM jako STABILNÍ póza (rebuild jen při posunu cíle >3 m) + nový PedFireWeaponAtPos (CWeapon::Fire s vynuceným READY+clip, interní rate-limit) na každý relayed výstřel → skutečný muzzle flash, ZVUK střelby a kulka + explicitní tracer. Pozorovatel teď vidí přesně to, co target. Rebuild .asi.

refactoractorsgunner = AUTHORITY simuluje, ostatní POZORUJÍ

konec N nezávislých combat AI na klientech. "Server řekne: střílej na hráče X" → include auto-pinne authoritu na X (target má sebe i gunnera vždy streamnuté); JEN authority běží skutečný armed task (aim/fire/reload, detekce výstřelů, vlastní damage 1 HP/ránu + latch, report 237/242). VŠICHNI ostatní žádnou combat AI nemají: gunner u nich stojí na kotvě, otáčí se za cílem (resolved ped, jinak poslední relayed endpoint) a každý relayed výstřel (238) přehraje poziční aim/fire animaci + tracer. Scéna je deterministicky stejná pro všechny. Rebuild .asi + rekompilace filterscriptu.

featactorsOnActorVehicleDeathSP (RPC 243) - respawn actor auta konečně funguje

SA:MP klient neposílá unoccupied-vehicle sync (včetně death notifikace za OnVehicleDeath) pro auto S řidičem - a actor auto řidiče klientsky MÁ, takže server se o vraku nikdy nedozvěděl a OnVehicleDeath nepřišel. Authority teď vrak bound vozidla ohlásí (RPC 243, health <= 1, latch s re-arm po respawnu); include validuje authoritu a volá forward OnActorVehicleDeathSP(actorid, vehicleid). Demo na něm respawnuje Sultan (OnVehicleDeath zůstává jako záloha). Rebuild .asi + rekompilace filterscriptu.

fixdemo/spobject je broadcast

objekt se streamuje VŠEM plugin hráčům (SP_BroadcastCreateObject/RemoveObject), ne jen volajícímu. (Pozn.: demo objekt nepersistuje pro později připojené hráče.) Rekompilace filterscriptu.

fixdiscordruntime OFF→ON znovu zapne presence

Discord zahazuje SET_ACTIVITY poslané před READY dispatchem. Při startu to maskoval pozdější server push, ale při runtime re-enable byl okamžitý push jediný → spojení se "navázalo", presence nikdy nenaskočila. Connect() teď po handshaku ČEKÁ na READY (až 2 s), worker průběžně vyčítá frames z pipe (neucpává Discordí stranu) a selhání zápisu pipe recykluje + re-pushne aktivitu (přežije restart Discordu). Rebuild .asi.

fixactorsgunner multi-client - root cause dvou chyb

(1) te_sdk GetPlayerId() četl SA:MP uint16 lokální id jako 32-bit int (garbage horní bity); nemaskované porovnání v real-target gate pak na klientech s id 1+ zabilo damage + shot/damage reporty → id 0 neviděl střelbu, id 1 nedostával červená čísílka. Opraveno v te_sdk (uint16 read) + defensivní maska v gate. (2) On-foot pose sync během střelby aktualizoval kotvu podle peda, kterého combat task posouval → combat pin "pinoval" na místo, kam si gunner odběhl = pin mrtvý, guard běhal po mapě a authority ten běh streamovala všem. On-foot sync je nyní při isShooting vypnutý - pin drží pozici. Rebuild .asi.

featai-driveFULL rotace v pose syncu (kopce/náklony)

wire 219/221 nese volitelný trailing (fwd+up vektory matice); followeři aplikují nlerp-blend celé rotace místo zploštělého headingu → synchronizované auto do kopce konečně JEDE do kopce (pitch/roll). Kompatibilní oběma směry (starý klient/server tail nečte/nerelayuje → heading fallback). Řidič sedí správně díky PedMatchVehicleRotation (kopíruje celou matici auta).

featactorsENTER/EXIT_VEH fyzicky posadí/vyhodí peda

EXIT (206) dřív jen mazal descriptor a ped seděl dál; teď RemovePedFromVehicleDirect (vyčistí driver/passenger backrefs + m_pMyVehicle + bInVehicle flag + flush tasků) a ped se postaví 1,8 m vedle dveří řidiče. ENTER (205) umí i přesazení z jiného vozidla (unseat → SetPedInCarDirect).

fixactorsgunner - armed task se aplikuje VŠUDE + anti-stuck

ResolveTargetPed má zpět fallback na LOCAL peda (server řekne "střílej na X" → task běží na každém klientovi i když X není resolvnutelný - řeší "pro id 1 /guardshoot nedělá nic"); damage i shot-report jsou gated na SKUTEČNÉ target id, takže fallback je čistě aim/animace (bystander nikdy nedostane damage ani špatné tracer endpointy). Anti-stuck: když GTA combat task samo ukončí (damage event), throttlovaná kontrola prázdného primary slotu ho znovu aplikuje ("gunner se občas úplně zasekne").

featai-driveplynulé předání authority

povýšený klient před spuštěním CarAI snapne své vozidlo na EXTRAPOLOVANOU synchronizovanou pózu (pozice + rychlost + plná rotace), takže simulace naváže přesně tam, kde předchozí authority skončila - žádný zpětný cuk pro ostatní.

fixactorstracery zpět na REÁLNOU kadenci

server už neposílá tracery slepým 400ms timerem; shot AUTHORITY (target guardu) reportuje RPC 237 přesně při úbytku náboje v zásobníku actora (firedShot detekce) a server ho relayuje jako 238 ostatním → tracer + fire animace pozorovatelů sedí 1:1 na skutečné výstřely. SP_BroadcastActorShot v include zůstává pro skriptované efekty. Rebuild .asi + rekompilace filterscriptu.

fixassetsmanifest tiše zahazoval assety nad 64

SP_AssetAutoManifest ořezával seznam na SP_ASSET_MANIFEST_MAX (64), takže klient s 80+ assety ve scriptfiles/assets/ nikdy nedostal zbytek ("v cache 64, ve scriptfiles 80+"). Manifest teď odchází v DÁVKÁCH po 64 (klientský registr merguje, ACK per dávka, stack zůstává malý) - funguje libovolný počet assetů. Demo počítá readiness přes všechny dávky (pending manifests + pending transfers; _AssetsReady až po poslední, jinak hrozil apply skinů na klienta bez části assetů). Default SP_AUTO_QUEUE_MAX zvednut 256 → 1024 (80+ assetů × více joinerů se do ringu musí vejít). Jen rekompilace filterscriptu (PAWN-only).

docsdocs/technical-audit.md

kompletní technický audit + per-issue report (root cause / files / design / side effects / future work).

Přidáno

featassetsRUNTIME auto-discovery + SHA-256 (žádný offline krok, žádné defines)

server plugin sp-samp-server-fixes dostal nativy SP_AssetScan(dir) (výpis .dff/.txd + nativní SHA-256 každého souboru) a gettery. sp_plugin.inc je zavolá jednou v SP_Init a SP_AssetAutoManifest/SP_AssetAutoTransfer/SP_AssetIdByName jedou přímo z toho scanu. Gamemode nemá žádné asset defines/hashe/paths - .dff/.txd hodíš do scriptfiles/assets/, restart, hotovo, klient si to sám stáhne. Generátor gen_asset_hashes.ps1 + generovaný asset_hashes.inc odstraněny (nahrazeny runtime cestou). Demo mapuje asset→model přes SP_AssetIdByName("m4.txd"). #pragma dynamic 16384 v demu kvůli větším lokálním polím.

perfassetsadaptivní throttle přes NetStats

viz commit; streaming se sám škrtí podle NetStats_MessagesRecvPerSecond (headroom k ackslimitu) + NetStats_PacketLossPercent.

featnotifytransient toast notifikace (RPC 241)

server pošle klientovi toast (level + titulek + text + doba), který sjede vpravo nahoře, chvíli drží a fadene pryč. Nový NotificationManager (fronta 6 toastů, TTL, slide/fade animace, level barvy info/success/warning/error), kreslený v overlay always-draw cestě. Nová capability SP_CAP_NOTIFY (advertizovaná v handshaku), natives SP_Notify / SP_NotifyAll. Demo /notify. Nutný rebuild .asi.

perfassetsstreaming rate auto-odvozený z ackslimit

SP_Init čte živou hodnotu přes GetConsoleVarAsInt("ackslimit") a dopočítá per-tick chunk budget = ackslimit * SP_XFER_ACK_SAFETY_PCT (40%) * tick_ms/1000, takže se nemusí ručně ladit a nemůže trhnout kick ať je server.cfg jakkoli (i ackslimit 0 → strop SP_XFER_ACKLIMIT_UNLIMITED). Budget je globální (napříč klienty), takže rychlost jednoho klienta nikdy nepřekročí jeho vlastní ackslimit s rezervou. SP_Init to zaloguje. Odstraněny statické SP_XFER_GLOBAL_CHUNKS_PER_TICK / SP_XFER_CHUNKS_PER_TICK. Jen recompile filterscriptu (PAWN-only).

feathudvektorová drawing primitiva + SP logo přes server HUD (RPC 224)

HUD framework uměl jen text / bar / gauge; nově kreslí i čáru, obdélník (outline i filled), kruh a vestavěné SP logo. Wire rozšířen o druhý bod x2,y2; value u primitiv = tloušťka/poloměr px (neклampuje se), SP_HUD_FLAG_FILLED pro plný obdélník/kruh. Kresleno přes ImGui foreground draw list. Nové natives SP_HudDrawLine / SP_HudDrawRect / SP_HudDrawCircle / SP_HudDrawLogo; SP_HudSet má teď parametry x2,y2,extraFlags. Demo /drawdemo (čára + obdélníky + kruh + logo). Nutný rebuild .asi. (Textury z vlastních assetů zatím ne - jen vestavěné logo; svg/txd texture draw je další krok.)

featoverlaySettings dvousloupcově + menší okno

Settings tab už není jeden vysoký scrollovací sloupec: kompaktní ovládání vlevo, POST-PROCESSING vpravo, ABOUT dole. Okno zmenšeno na 820×680 (z 940×810). Nutný rebuild .asi.

featdemosp_training.pwnsp_demo.pwn + pokrytí VŠECH RPC scénářů
  • Demo filterscript přejmenován na sp_demo.pwn (rekompiluj na sp_demo.amx).

Nově exercituje každý SP RPC, ne jen podmnožinu - přibyly příkazy /actorwalk (MOVE 204), /actorreset (UPDATE 203), /actorenter+/actorexit (ENTER/EXIT_VEH 205/206), /shootpos (SHOOT_POS 210), /despawn+/respawn (DESPAWN/SPAWN 202/201), /postfx+/postfxoff (POSTFX 233), /spobject+/spobjectoff (object streamer 234/235), /job <name> (state bag 227/228), /brand (HUD 224/225) a /sphelp, které vypíše celý seznam s RPC id.

  • sp_plugin.inc: doplněn chybějící stock SP_ActorMoveTo (RPC 204 - klient ho

odjakživa uměl přijmout přes OnSpActorMove, ale server neměl jak ho poslat). Dokompletována sada broadcast helperů (SP_BroadcastActorMoveTo, SP_BroadcastUpdateActor, SP_BroadcastActorEnterVehicle, SP_BroadcastActorExitVehicle, SP_BroadcastActorShootAtPos, SP_BroadcastDespawnActor).

featworld-textureMTA "retexture what I look at" - aim-based world retex
  • GetAimedWorldModel() (client, gta_funcs.cpp) raycastuje dopředu od lokálního

hráče přes už ověřený CWorld::ProcessLineOfSight @0x56BA00 (jen buildings+objects, ať netrefí vlastního peda) a vrátí model id world objektu, na který koukáš (CEntity+0x22). Bez chození po GTA object poolu (žádný neověřený CPools pointer → nemůže crashnout).

  • model id 0 = „objekt, na který koukám" pro dump i replace: /retexdump (bez argu)

zaměří zeď/budovu a dumpne JEJÍ textury + model id do sp-plugin.log; /retex <tex> on-foot retexturuje objekt, na který míříš. Přesný nástroj místo dumpu podle id.

  • DumpStreamedWorldObjectTextures() (renderware.cpp) zůstává jako bulk fallback

(/retexdump all) - vypíše všechny nahrané world-object modely (id>=615, atomic/timed přes kalibrovaný GetModelType), taky bez pool-walku, capnuté. (Pozn.: bulk jde podle id, takže se cap vyčerpá na vegetaci dřív než dojde k budovám - proto je default aim.)

  • /retex [model] <targetTex> [srcTex]: volitelné vedoucí model id (z dumpu), jinak

vozidlo / aim on-foot. Wire: RPC 240 modelId==0=aim, 0xFFFFFFFF=bulk, jinak model.

  • /retexsrc + SP_DumpAssetTextures - dumpne texture names přímo z custom TXD

assetu (source discovery pro <srcTex>), ať člověk netipuje jméno, co v TXD není. Sdílený helper DumpTxdDictTextureNames(dict, label) (walk libovolného RwTexDictionary). Wire: RPC 240 modelId==0xFFFFFFFE + u32 assetId. Nutný rebuild .asi.

fixhandshakeklient neadvertizoval ObjectStream capability

object streamer (RPC 234/235) byl na klientu plně implementovaný a zapojený, ale HelloPayload::capabilities bit Capability::ObjectStream (1<<9) vynechával → server ho viděl jako „nepodporováno" a SP_CreateStreamedObject tiše no-opoval (demo /spobject hlásilo „object streamer not detected"). Bit doplněn. Nutný rebuild .asi.

fixdemobrand HUD (SUPERPARBA.EU / REALNA ZEME 12) je na joinu OFF

top-left branding překrýval SA:MP chat, takže se už při připojení nezobrazuje. Přepínatelný příkazem /brand. Live speedometer (bottom-right) zůstává.

featrenderPost-Processing / ENB-lite

full-screen colour grade (tint / brightness / vignette / sytost) v D3D9 Present hooku pod overlayem, fixed-function (žádný shader-compile). Default OFF (postfx_* v ini), server override přes RPC 233 (SP_SetPostFX). State-block snapshot/restore, ať to nerozbije render hry.

featrendersaturation / sytost 0-300 %

<100 odbarvuje do šedi (fixed-function), >100 zvýrazňuje barvy přes ps_2_0 pass (snapshot backbufferu StretchRectem + redraw, který posune pixel od jeho luminance). d3dcompiler se načítá dynamicky; když chybí nebo D3D krok selže, boost se tiše přeskočí (nikdy černá obrazovka). Live slider v overlay Settings.

featui3D damage numbers

dealt damage plave ve 3D nad hlavou cíle (CSprite::CalcScreenCoors projekce), velký bold outline + spawn pop, barevně dle hit/kill/took. Velikost konfigurovatelná (combat_number_scale, 25-400 %) + live slider v overlay Settings. Kreslí se jen nad reálným cílem, ne nad lokálním hráčem.

featnetworkper-instance skin sync mezi hráči

registry vozidel + helpery (SP_SetVehicleSkin / SP_ClearVehicleSkin / SP_SyncVehicleSkinsToPlayer / SP_OnVehicleStreamInSkin), gated na SP_SetPlayerAssetsReady (apply jen klientům s cache assetů - jinak prázdný clone = neviditelné vozidlo).

featlagcompclient-side lagcompmode 4 push

server zapne klientskou interpolaci za běhu přes RPC 236 (SP_SetClientLagComp).

featuiAlt+S

jako nový default toggle overlaye (byl Ctrl+Shift+S).

Přidáno

featrenderWorld-Texture Shadery v1

výměna herní textury podle jména za custom art z TXD assetu (retextury světa). WorldTextureManager + RPC 239 (replace) / 240 (dump jmen). Swap = repoint rasteru cílové textury (SEH-guarded), re-apply po reloadu modelu (throttled Tick). Natives SP_ReplaceWorldTexture / SP_DumpModelTextures, demo /retexdump + /retex <jméno>. Opt-in, bez hot-path hooku. Viz docs/world-texture-shaders.md.

featdemoM4 weapon skin + Snoop ped skin

/applym4, /applysnoop (ped se aplikuje instantně přes SetPlayerSkin v apply-done, bez respawnu).

featactorsgunner shot sync mezi hráče

výstřely armed actora se teď synchronizují všem. Autorita actora (RPC 220) hlásí každý reálný výstřel (RPC 237), server ho rozešle ostatním (RPC 238) a všichni nakreslí stejný bullet tracer od actora - dřív ho viděla jen oběť. Damage zůstává lokální u oběti. Demo (sp_training): guard teď broadcastuje shoot na všechny klienty (ne jen cíli), takže armed combat task (míření/palba) běží u všech, a autorita = cíl (má guarda i sebe streamnuté → spolehlivý reporter).

Změněno

fixactorsgunner mířil na špatného hráče

ResolveTargetPed fallbackoval na lokálního peda, když cíl nebyl streamnutý → actor mířil (a dával damage) komukoli lokálnímu. Teď drží palbu, dokud se skutečný cíl nestreamne → gunner je konzistentní pro všechny.

změněnoclient-side lagcomp smoothing dotažený

interpoluje teď i heading (nejkratší arc), takže vzdálení plugin hráči už nejsou "sikmo". Potlačení nativního SA:MP syncu je nově opt-in (lagcomp_suppress_native, default off): SA:MP tak dál řídí rotaci + animace vzdáleného peda a naše 66 Hz smoothing jen přepisuje pozici/heading nad tím.

změněnovehicle-actor sync mnohem plynulejší

frame-rate nezávislý exponenciální ease (k = 1-e^(-dt*RATE)) + report ~20 Hz + arrival stop (auto se zastaví přesně na cíli, stopped flag v pose reportu 219/221, resend proti ztrátě paketu).

Opraveno

fixassetssekání ~95 ms/frame (AssetManager)

deferred/failed GLOBAL apply job se re-queueoval každý frame a každý pokus dělal synchronní force-load modelu (LoadAllRequestedModels ~95 ms) → hra na ~10 fps. Re-queue teď nese notBeforeTick (cooldown 500 ms), takže drahý retry běží ~2×/s místo 60×/s. Diagnostika: per-stage frame timing v per-frame hooku loguje pomalé framy s viníkem.

fixskincrash na POŠKOZENÉM vozidle + /applycar

RefreshVehicleInPlace swapnul model, ale damage stav (rozbité dveře/panely/deformace) ukazoval na komponenty starého modelu → CVehicle::Render deref null → crash. Nově se před swapem volá CAutomobile::Fix (reset na pristine). + clone se na /restorecar retiruje, takže reapply staví čerstvý.

fixcombatdamage numbers - obří hodnoty & pozice

HIT_EVENT dostal targetId (starý .amx proti novému .asi proto ukazoval nesmysly - nutný recompile obojího); přidána clamp proti absurdním hodnotám a fix, ať čísla plavou nad cílem, ne lokálně.

fixskinper-instance apply asset-less klientovi

mapoval prázdný clone → vozidlo zmizelo pro ostatní. Nově gated na cache readiness.

fixlagcompenforce režim bral legitimní zásahy

validátor odpojen z OnPlayerWeaponShot (LOG-only), re-enable až po vyladění rewindu.

fixuiHUD při pauze

speedometer / server HUD / damage čísla se schovají, když je hra pozastavená (Esc menu / loading) přes CTimer pause flagy - už nefloatují přes pause menu.

Opraveno (dřívější)

fixnetworkCRITICAL - R5 actor pool byl nesprávně označen jako "N/A"
  • kVersionTable nastavovalo m_offActorPool = 0xFF (sentinel "nenamapováno")

pro SA:MP R5, ale GetActorPed na rozdíl od GetVehiclePtr tenhle sentinel vůbec nekontrolovalo - 0xFF se použilo přímo jako offset do stSAMPPools, četlo se z něj cokoliv nesouvisejícího a bralo se to jako ukazatel na CActorPool. Sanity checky (TryActorPool) většinu odpadu odfiltrují, ale falešná pozitivní shoda by vrátila úplně špatný CPed*, do kterého by ActorManager každý frame zapisoval pozici/HP/úkoly/zbraně - memory corruption, odpovídající opakovaným Exception 0xC0000005 hlášeným na R5. Doplněn stejný guard, jaký už měl GetVehiclePtr. Navazující oprava - ten "N/A" předpoklad byl navíc špatně: podle BlastHackNet/SAMP-API (0.3.7-R5-1 hlavičky, LUCHARE+Northn - stejný RE zdroj, který tabulka už cituje pro R1–R4) má R5 plnou 9-poolovou strukturu a CActorPool* sedí na Pools+0x10. Křížově ověřeno proti offsetům, které tahle tabulka pro R5 už dřív správně měla (Vehicle=0x00, Player=0x04 přesně sedí na m_pVehicle/m_pPlayer ze stejné reference). m_offActorPool opraveno z 0xFF na 0x10 - extended actors (drive/shoot/vehicle sync) by tak měly na R5 znovu fungovat, ne jen bezpečně no-opnout. Needověřeno v reálném klientovi (žádné Windows/SA:MP prostředí k dispozici), ale nízké riziko: guard proti 0xFF a vtable-range sanity check v TryActorPool zůstávají - případný zbytkový nesoulad selže bezpečně (nullptr), ne pádem.

fixoverlaydiagnostika pro "starý sp-plugin.ini přebíjí nový default"
  • overlay_key z dřív nasazeného sp-plugin.ini (např. ještě z doby, kdy

šlo jen o holou klávesu) tiše vyhraje nad kódovým defaultem CTRL+SHIFT+S - Config nemá žádnou "upgrade" cestu, prostě načte, co je na disku. Bez logu se to snadno splete s "nový default nefunguje". LoadKeybind teď zaloguje, co se z ini reálně načetlo, a varuje, když výsledná kombinace nemá žádný modifikátor (typický příznak zastaralé hodnoty).

Přidáno

feathandshakeimproved handshake (anti-spoof, ACK, version negotiace)

SP_HELLO dostal auth_tag v místě starého reserved tailu (stejných 15 bytů, payload_version zůstává 1 → plná zpětná kompatibilita obousměrně: starý server tail ignoruje a přijme, nový ho validuje). Je to keyed FNV-1a hash přes ostatní pole (klient ComputeHelloAuthTag ↔ server _SP_HelloAuthTag, bit-for-bit shodné), který odmítne casual spoof (fake RPC 200). Default lenient (SP_STRICT_AUTH=0: neshoda = přijmout + zalogovat), striktní odmítání je opt-in - takže to nemůže rozbít detekci pluginu. Není to kryptograficky silné (klíč je v klientu). Server po úspěchu pošle RPC_SP_HELLO_ACK (232) s negotiated protokolem + accepted flagem; klient ho zpracuje (HelloHandler::OnHelloAck) a overlay ho zobrazí v SESSION kartě. Protokol se negociuje jako min(client, server). Legacy klient posílá auth_tag = 0.

featuiplný Superparba HUD + ručičkový speedometer

HUD framework dostal nový typ widgetu SP_HUD_GAUGE (analogový budík: oblouk + tick marky + otočná ručička + digitální readout, kreslený foreground draw listem). Demo sp_training.pwn staví plný HUD: branding „SUPERPARBA.EU" + „REALNA ZEME 12" vlevo nahoře a profesionální ručičkový speedometer vpravo dole, živě řízený z OnPlayerSyncSP (jen ve vozidle). Overlay karta „SYNC HRÁČŮ" zvýšena, ať se vejde lagcompmode+smoothing.

featnetworknetworked state bags (FiveM-style element data)

server-authoritative key→value na hráči replikovaný plugin klientům (RPC 227/228/229, typy int/float/string). StateBagManager (src/network/statebag_manager.{h,cpp}), natives SP_SetPlayerState Int/Float/String, SP_RemovePlayerState, SP_ClearPlayerState, capability SP_CAP_STATE_BAGS. Základ pro job/faction/custom stavy bez bespoke RPC.

featuihitmarkery & damage numbers

server přes RPC 230 řekne klientovi, ať blikne hitmarker + vyplave damage číslo (SP_ShowHit, kind hit/kill/took). Screen-space, transientní (CombatFeedback + foreground draw list), žádná world→screen projekce. Demo je řídí z OnPlayerGiveDamage/OnPlayerTakeDamage. Capability SP_CAP_COMBAT_FEEDBACK.

featcoreDiscord Rich Presence

DiscordRpc mluví přímo s lokálním Discord IPC pipe (\\.\pipe\discord-ipc-0..9, dokumentovaný framing, žádný Discord SDK). Background thread drží pipe; server pushuje jméno serveru + počet hráčů přes RPC 231 (SP_SetDiscordPresence). Default vypnuto - zapíná se discord_enable=1 + discord_client_id v ini.

featcorecrash reporter

CrashReporter instaluje unhandled-exception filter, který při pádu zapíše minidump (.dmp, MiniDumpWriteDump) + kontext (.txt: verze, exception kód/adresa) do data diru. Vrací CONTINUE_SEARCH, takže SA:MP handler poběží dál. Vypínatelné crash_reporter=0. Link dbghelp.lib.

docsRenderWare/DX featury (návrh)

docs/renderware-features.md drží architekturu a plán pro World-Texture shadery, COL streaming, Vehicle tuning/neon, Post-processing (ENB-lite) a client-side object streamer. Hluboké RW/D3D RE - implementace až po ověření na reálném klientovi (stejná disciplína jako RefreshVehicleInPlace).

featlagcomplagcompmode 4 - server-side rewind hit validace (Varianta A)

nový opt-in include pawn/include/sp_lagcomp.inc. Drží časovanou historii pozic každého hráče (ring buffer 32 vzorků, ~1.2 s) plněnou samplerem GetPlayerPos (25 Hz, funguje i bez pluginu) + volitelně high-res vzorky z OnPlayerSyncSP pro plugin hráče. Když přijde lag-kompenzovaný zásah (OnPlayerWeaponShot), rewindne cíl na to, co zhruba viděl střelec (ping + SP_LC_INTERP_MS) a ověří, jestli je reportovaný hit point geometricky plausibilní (SP_LC_HIT_RADIUS) - implausibilní zásahy (typicky „za rohem" / spoof) LOGuje nebo ODMÍTNE. Módy SP_LC_MODE_OFF/LOG/ ENFORCE. API SP_LagCompEnable/ValidateShot/Record/ResetPlayer/Mode/Rejected. Čistý PAWN, žádné RE. Peer payload rozšířen na v2 (clientTick timestamp pro budoucí clock-offset rewind; PLAYER_STATE_VERSION=2, klient i server). Rozbor + API v docs/lagcomp.md.

featlagcompclient-side lagcompmode 4

nový LagCompClient (src/network/lagcomp_client.{h,cpp}). Dvě části: (1) InitGame observer - non-blocking hook na RPC_InitGame (139), který přečte serverový vanilla lagCompMode (pořadí polí ověřené proti samp-packet-list + SAMP.Lua rpc_init_game_reader; bool = 1 bit, špatný parse → „neznámý", ne pád) a zobrazí ho v overlay (Connection tab). Read-only, RPC projde do SA:MP nedotčené. (2) Experimentální interpolace vzdálených plugin hráčů - z peer kanálu (223) drží krátkou historii pozic a interpoluje pohyb ostatních plugin hráčů zápisem do vzdáleného pedu. Default OFF (lagcomp_smoothing=0 v sp-plugin.ini), protože zapisuje do paměti pedů a koliduje s vanilla onfoot sync - needověřeno na reálném klientovi, může rubberbandit (stejná disciplína jako RefreshVehicleInPlace). Zapíná se jen na testování.

featuiserver-driven HUD framework

server přes RPC (224 SET / 225 REMOVE / 226 CLEAR) posílá lehké HUD widgety, které klient kreslí přes hru každý frame (foreground draw list, nezávisle na tom, jestli je overlay menu otevřené). Typy SP_HUD_TEXT a SP_HUD_BAR, pozice v normalizovaných souřadnicích (0..1), barva 0xRRGGBBAA, per-widget viditelnost. Klient: HudManager (src/network/hud_manager.{h,cpp}), lock-free main-thread jako ostatní manažery, wire-symetrické čtení s bezpečným cappem délky textu. Server: natives SP_HudSetText / SP_HudSetBar / SP_HudSet / SP_HudRemove / SP_HudClear, capability SP_CAP_SERVER_HUD. Demo: /hud, /hudoff + živý speedometer řízený z OnPlayerSyncSP v sp_training.pwn. Overlay: karta "SERVER HUD" (počet prvků).

featcoreauto-update check (notify-only)

plugin na startu na pozadí (WinHTTP, detached thread, krátké timeouty) stáhne version.json manifest (update_url, default superparba.eu), porovná latest se zabudovanou SP_VERSION a při novější verzi ukáže nenápadný banner nahoře + kartu "AKTUALIZACE" v Connection tabu s odkazem ke stažení a tlačítkem Skrýt. Nic se nestahuje/nespouští samo - hráč klikne na odkaz sám. Vypnutelné update_check=0. Nové src/core/update_checker.{h,cpp}, sample web/version.json, link winhttp.lib.

docsnávrhy lagcompmode 4 a CEF/NUI

docs/lagcomp.md rozebírá, jak SA:MP lag compensation reálně funguje (mody 0/1/2, bLagCompensation v InitGame RPC 139, client-authoritative hit model) a navrhuje lagcompmode 4 jako plugin-only režim nad peer-sync historií (server-rewind validace jako bezpečná první varianta). docs/cef-ui.md je integrační plán třetí UI vrstvy (CEF off-screen render, JS bridge, distribuce UI bundlu přes stávající asset streaming). Obojí zatím návrh - RE zásahy do hit-registrace a CEF integrace potřebují reálný klient k ověření.

featnetworkplugin-to-plugin ("peer") player state sync

nový obousměrný side-channel mezi klienty, kteří oba jedou na SP pluginu, nad rámec toho, co syncuje vanilla SA:MP. Každý plugin klient ~10x/s streamuje svůj kompaktní stav (pozice, heading, rychlost v km/h, HP, flagy in-vehicle/driver) serveru jako RPC_SP_PLAYER_STATE_REPORT (222); server ho přeposílá jen ostatním plugin hráčům, kteří mají odesílatele streamnutého (IsPlayerStreamedIn = distance cull zdarma), jako RPC_SP_PLAYER_STATE_SYNC (223) s prefixem u16 playerId.

  • Klient: nový PeerSyncManager (src/network/peer_sync.{h,cpp}) - throttlovaný self-report + tabulka peerů s expirací (kPeerTimeoutMs), tickovaný z Hook_GameProcess pod stávajícím SEH netem. Čte jen prověřené gettery (PedGetPosition/PedGetHealth/PedGetVehicle/VehicleGetVelocity/ VehicleGetHeading) + nový PedGetHeading (mirror VehicleGetHeading, stejné už ověřené offsety CEntity matrix +0x14 / forward +0x10) - žádné nové adresy. Nová capability Capability::PeerSync (bit 3) v SP_HELLO.
  • Server: SP_HandlePlayerState v sp_plugin.inc - rate-limit (SP_SYNC_MIN_INTERVAL_MS), gate na SP_CAP_PEER_SYNC, uložení posledního stavu per hráč, callback OnPlayerSyncSP(...) a relay streamnutým peerům. Přístupové natives SP_PlayerSyncFresh / SP_GetPlayerSyncPos / SP_GetPlayerSyncMotion / SP_GetPlayerSyncFlags / SP_GetPlayerSyncHealth. Demo /peers + OnPlayerSyncSP v sp_training.pwn.
  • Overlay: karta "SYNC HRÁČŮ (PLUGIN)" v Connection tabu (živí peeři, počet odeslaných reportů/přijatých updatů + kompaktní tabulka id/vozidlo/rychlost/HP).
  • Scope/omezení: dodán transport + server relay + PAWN API + diagnostika, vše prověřenými mechanismy. Klientská konzumace peer pozic (plynulejší interpolace/smoothing vzdálených plugin hráčů, custom nametagy) je záměrně ponechána jako navazující krok - vyžaduje zápis do paměti vzdálených pedů a ověření na reálném klientovi; peer stav se zatím jen cachuje a vystavuje.
featoverlayrebindable toggle combo + redesigned UI

overlay se dřív otvíral holým INSERT, který kolidoval s jinými nástroji a šel snadno zmáčknout omylem. Default je teď kombo CTRL+SHIFT+S, plně rebindovatelné za běhu (Settings tab → Rebind → zmáčkni novou kombinaci, Esc pro zrušení) i ručně v sp-plugin.ini (overlay_key=MOD+MOD+KEY, viz resources/sp-plugin.ini). Nový Keybind (src/imgui/overlay.h/.cpp): serializace do/z textu, detekce držených modifikátorů přes GetAsyncKeyState (jen live-state čtení, ne ten "toggled" bit, co dřív pod SA:MP nešel spolehlivě přečíst - viz starší fix), capture-mód ve WndProc co zhltne klávesy během rebindu, ať nic neprosákne do hry/ImGui. Config (src/config/config.h/.cpp) dostal SetAndSave - text-preserving zápis jedné hodnoty zpátky do ini (komentáře a ostatní klíče zůstanou netknuté). UI přestylováno z pěti stackovaných CollapsingHeader na taby (Assets / Actors / Connection / Settings) s kartami (BeginCard/EndCard), status pill v hlavičce (barevná tečka vykreslená přes draw-list, ne Unicode glyph - výchozí ImGui font atlas glyph typu ● stejně neobsahuje) a patičkou s aktuální klávesovou zkratkou.

featassetsinstant in-place vehicle refresh - no SetVehicleVirtualWorld needed
  • SP_APPLY_INSTANCE (/applycar) už po úspěšném apply nemusí čekat na

server-driven restream. gta::RefreshVehicleInPlace (src/gta/renderware.cpp) rovnou přestaví RwObject ALREADY-LIVE vozidla (bez destroy/recreate, bez virtual-world bounce, neviditelné pro ostatní hráče): CEntity::DeleteRwObjectSetModelIndexNoCreateCreateRwObject (CEntity vtable sloty 8/6/7) a navíc CAutomobile::SetupModelNodes (0x6A0770), aby se přebindovaly wheel/door/component dummy frame ukazatele (m_aCarNodes, CAutomobile+0x648) na nový clump - bez tohoto kroku samotný swap clumpu/model indexu spadne při první interakci (typicky vstup do vozidla), protože staré ukazatele v m_aCarNodes[] míří do uvolněné geometrie. Zapojeno v AssetManager::ProcessInstanceApply; nová informace refreshedLive se posílá zpět serveru přes RPC 218 (SP_HandleAssetApplied/OnPlayerAssetApplied v sp_plugin.inc/ sp_training.pwn). Fallback nikdy nesmí ejectnout cizího hráče: RefreshVehicleInPlace vrací rozlišený VehicleRefreshResult (Occupied/ModelNotLoaded jsou retryable, NotAutomobile/InvalidEntity/Crashed permanentní). Obsazené vozidlo se needěje násilně restreamovat - místo toho jde do tiché fronty (m_pendingRefresh) a AssetManager::RetryPendingRefreshes to zkouší znovu z OnGameProcess (throttled, stejná kadence jako reclaim pump), čistě lokálně, bez RPC a beze změny obsazení komukoliv. Pawn strana (sp_training.pwn) volá disruptivní SP_RestreamVehicleReseat (eject+reseat) jen když je ve vozidle sám žadatel (GetPlayerVehicleID(playerid) == target) - pokud vozidlo řídí/obsazuje JINÝ hráč, aplikace se prostě tiše dokončí na pozadí, jakmile se vozidlo uvolní nebo příště přirozeně restreamne, aniž by to ten druhý hráč vůbec pocítil. Adresy (CEntity vtable sloty, CAutomobile::ctor/SetupModelNodes, CVehicle::m_pDriver/ m_apPassengers/m_nVehicleClass) pocházejí z DK22Pac/plugin-sdk (stejná 1.0 US sada, křížově ověřená proti již fungujícím ADDR_RequestModel/ ADDR_LoadAllRequestedModels v tomto souboru), ale nejsou ověřené proti živému debug session - nebylo k dispozici Windows/SA:MP prostředí. Návrh je proto čistě aditivní a bezpečný na selhání: RefreshVehicleInPlace je SEH-guarded a při čemkoliv neočekávaném (neplatný pointer, obsazené vozidlo, ne-automobil, nebo výjimka) vrátí false beze změny stavu - stávající server-driven restream zůstává fallbackem, dokud se tohle needověří v reálném klientovi.

featassetsper-vehicle skinning - clone models, safe restream, anti-abuse

SP_APPLY_INSTANCE je teď plně implementovaný (dřív jen protokol, bez replaceru). AssetManager::ProcessInstanceApply alokuje volný model slot (AllocateVehicleCloneModel, MTA engineRequestModel/MakeVehicleAutomobile pattern), byte-kopíruje rodičovské CVehicleModelInfo, aplikuje custom DFF/TXD jen na klon (soukromý TXD slot přes CTxdStore::AddTxdSlot, aby se skin nerozlil na ostatní instance stejného modelu), a namapuje vehicleId → cloneModelId. Přepnutí proběhne přes non-blocking observer OnWorldVehicleAdd (nativní RPC 164), který přepíše ModelID v paketu ještě než ho SA:MP zpracuje - vozidlo se tak rovnou vytvoří s klonovaným modelem. EnsureCloneModelArt rebuilduje custom clump, pokud SA:MP mezitím model force-unloadl (vozidlo bylo mimo dosah streamu a vrátilo se). Nové pawn příkazy /applycar a /restorecar (pawn/filterscripts/sp_training.pwn), server-side SP_RestreamVehicleReseat (bezpečný restream i obsazeného vozidla - eject → restream → re-seat) a anti-abuse pro RPC 218 (APPLY_DONE je plně klientem kontrolovaný - pending-apply tracking + rate limit v pawn/include/sp_plugin.inc, viz _SP_ApplyRecordPending/_SP_ApplyFloodOk).

featassetsdynamic DFF/TXD streaming pipeline

server distribuuje vlastní modely/textury klientům bez manuální instalace. Moduly: AssetManager (orchestrace + registry), TransferManager (chunk reassembly na main threadu, SHA-256 verify + cache write na worker threadu → neblokuje frame), CacheManager (content-addressed disk cache <sha256>.asset, dedup, atomic rename), vlastní SHA-256. Přenosový protokol přes RPC 211–218 (MANIFEST/BEGIN/CHUNK/ACK/COMPLETE/CANCEL/APPLY/APPLY_DONE), chunk 400 B (pod RakNet MTU 576), manifest negotiace (nestahovat, co je v cache), resume-ready. Klientský RW replacer (src/gta/renderware.cpp, ověřené GTA:SA 1.0 US adresy dle MTA/BFS CRenderWareSA/CModelInfoSA): ReadTXD/ReadDFF z cache přes RwStreamOpen, import textur do TXD slotu modelu, CVehicleModelInfo::SetClump (GLOBAL i INSTANCE/clone). Apply běží na main/render threadu (RW není thread-safe) přes frontu drénovanou v OnGameProcess, SEH-guarded.

featoverlaymoderní ImGui diagnostický overlay

ImGui vendorováno jako submodule (external/imgui, DX9/Win32 backend), zapnutelné SP_ENABLE_IMGUI (default ON), toggle INSERT. SP brand styl (teal accent), embedded logo (superparba.eu headlogo, PNG přes WIC → D3D9 textura, žádná runtime síť ani DX SDK). Sekce: Asset streaming (aktivní downloady s progress bary, počet assetů, velikost cache), Extended actors, Connection. Oprava build-breaking bugu v overlay.cpp (chybějící signatura funkce) + detours.hdetours_x86.h.

featpawnserver-side asset streaming API

(pawn/include/sp_plugin.inc) - SP_AssetSendManifest, SP_AssetTransferFile (byte-accurate chunking přes fblockread), SP_AssetApply (GLOBAL/INSTANCE), SP_AssetCancel + callbacky OnPlayerAssetsMissing, OnPlayerAssetTransferDone. SHA-256 se předává jako 64-char hex (precompute offline). Funkční demo v sp_training.pwn (/loadskin → manifest → transfer → per-instance apply na Infernus 411). Nástroj tools/gen_asset_hashes.ps1 proskenuje scriptfiles/assets a vygeneruje asset_hashes.inc (path/hash/size #define per soubor).

featactorkompletní gunner

actor drží MP5 (GiveWeapon + explicit RequestModel + LoadAllRequestedModels + AddWeaponModel), útočí přes GTA CTaskComplexKillPedOnFootArmed (0x621190) v primary slotu, přišpendlený na místě + natočený na cíl, viditelné tracery (CBulletTraces::AddTrace 0x726AF0) + damage synced na skutečný výstřel (detekce ubytí náboje) + line-of-sight (CWorld::ProcessLineOfSight 0x56BA00). RE: CTaskSimpleUseGun vtable[7]=ProcessPed (ne [5]); všechny GTA weapon-tasky padají na actor pedu (0x625082).

featnetmultiplayer gunner target resolution

actor cílí správného hráče podle shootTarget ID (ne vždy lokálního) přes SA:MP player pool chain (R1: CPlayerPool+0x2ECPlayerInfoCRemotePlayer → wrapper+0x44 → GTA CPed), vtable-validované; damage jen když je cíl lokální hráč.

Opraveno

fixassetsCRITICAL - GLOBAL apply crashed the client if a live instance of the target model existed

/applyboth (SP_APPLY_GLOBAL) přepisoval sdílenou CVehicleModelInfo (clump + pVisualInfo/CVehicleStructure) modelu přímo v paměti, i když ve světě existovala živá instance daného modelu (typicky vozidlo, ve kterém hráč právě sedí jako řidič - /spawninf/applyboth). Ta instance byla postavená proti staré CVehicleStructure; jejím přepsáním pod ní vzniknul nekonzistentní stav a klient spadl. ProcessGlobalApply (src/assets/asset_manager.cpp) teď před swapem zkontroluje ModelHasLiveInstance a apply bezpečně odmítne (APPLY_DONE ok=0) místo korupce paměti. Ověřeno statickou revizí: kontrola i samotný swap běží synchronně na stejném (main) threadu z OnGameProcess, takže mezi kontrolou a swapem není race. Známé omezení: odmítnutí platí pro jakoukoli živou instanci modelu, ne jen obsazenou (bezpečnější, ale striktnější než SP_RestreamVehicle's occupied-only skip) a pawn-side callback (OnPlayerAssetApplied v sp_training.pwn) na ok=0 zatím jen loguje FAILED bez automatického restream-and-retry - na produkčním serveru je potřeba nejdřív odstranit/restreamnout živé instance modelu, teprve pak /applyboth. /applycar (INSTANCE/clone model) tímhle vůbec netrpí, protože nikdy nesahá na sdílené model info.

fixassetsclone model se po restreamu (stejné vehicleId) přestal znovu pinovat → ztráta textury

EnsureCloneModelArt po force-unloadu klonu (SA:MP uvolní model, jakmile zanikne poslední živá instance) správně re-loadoval geometrii, ale g_pinnedModels (dedup množina pro PinModel) se nikdy nečistila mimo FreeCloneModel → druhý a další refcount re-pin se tiše přeskočil (insert().second == false), protože SA:MP nativní refcount pole při unloadu vynuloval, ale naše dedup množina o tom nevěděla. Klon tak po prvním respawnu zůstal nechráněný proti dalšímu vytěsnění streamerem - přesně symptom „vozidlo respawne pod stejným ID a custom textura zmizí/nenačte se". Opraveno g_pinnedModels.erase(cloneModelId) před re-pinem v src/gta/renderware.cpp. Známá navazující mezera (nefixováno, mimo rozsah): stejným způsobem může zůstat nechráněný i rodičovský model (AllocateVehicleCloneModel ho pinuje jen jednou při vytvoření klonu) - pokud SA:MP/streamer force-unloadne rodiče, EnsureCloneModelArt to nekontroluje (jen stav klonu), takže stejná třída bugu se teoreticky může vrátit jinou cestou. Sledovat, pokud se skin ztrácí i bez respawnu vozidla samotného.

fixassetssafe model apply - force-load + live-instance restream

apply modelu crashoval GPU driver při renderu, protože (a) model nebyl načtený (apply se odkládal), nebo (b) se nahrazoval clump modelu, který měl živou instanci (auto ve světě) → stará instance renderovala proti neplatným datům. Nyní: EnsureVehicleModelLoaded natáhne model přes streaming bez spawnu instance; po výměně se všechny živé instance daného modelu restreamnou (DeleteRwObject vtable[8] + CreateRwObject vtable[7], ověřeno s0beit/MTA) přes SA:MP vehicle pool → re-instancují se z nového clumpu bez crashe. Diagnostika: new clump has N atomics, restreamed N live instance(s).

fixoverlayrender in Present, input via WndProc, version-independent input freeze
  • kreslení přesunuto z EndScene do Present (1×/snímek); EndScene volal

windowed-mode mód vícekrát → overlay se duplikoval/trhal (draw v Present obalen vlastním BeginScene/EndScene). INSERT toggle přes WndProc WM_KEYDOWN (spolehlivé; GetAsyncKeyState nefungovalo). Při otevřeném menu se hra zmrazí patchem CPad::UpdatePads (0x541DD0, gta_sa.exe → funguje na všech SA:MP 0.3.7 R1–R5, žádné per-verzi samp.dll offsety) + ImGui software kurzor; myš tak ovládá jen overlay, ne kameru/střelbu. Overlay větší (940×560), vycentrovaný, default skrytý.

fixoverlayoverlay never appeared on INSERT

dvě příčiny: (1) m_imguiReady se v D3D hooku nastavoval true i když Overlay::Init skončil předčasně (HWND ještě nebyl k dispozici), takže se init už nikdy nezopakoval → overlay zůstal tmavý navždy; nyní se m_imguiReady váže na skutečný IsInitialized() a init se opakuje každý frame, dokud neuspěje. (2) Toggle četl GetAsyncKeyState(VK_INSERT) & 1 (low „toggled" bit), který je procesně globální a SA:MP ho svým pollingem kláves spotřeboval → čteno vždy 0; přepnuto na edge-detekci přes high (down) bit. HWND se navíc bere spolehlivě z IDirect3DDevice9::GetCreationParameters (fallback na statickou adresu). Přidán WndProc subclass (myš/klávesnice v overlay) + diagnostické logy (first EndScene, initialized).

fixassetsthrottled chunk sender

SP_AssetTransferFile posílal všechny chunky v jednom těsném cyklu; 27 MB DFF = ~68 000 RELIABLE RPC v jednom server-tiku → přetečení RakNet bufferu / zamrznutí. Nyní enqueue do slotu + tick timer (SP_XFER_CHUNKS_PER_TICK chunků/tick, default ~400 KB/s), non-blocking, s abort na disconnect (SP_AssetAbortPlayer).

fixvehicleCRITICAL - wrong handbrake offset corrupted m_pFireParticle → crash

OFF_VEH_HANDBRAKE byl 0x57C, což ale podle plugin-sdk není handbrake, ale m_pFireParticle (FxSystem_c*, POINTER). VehicleSetHandbrake(veh, 100.0f) tak zapisoval 0x42C80000 do particle pointeru; hra/modpack ho pak dereferencoval → deterministický crash (potvrzeno: ESI=0x42C80000=100.0f v registru napříč všemi pády; VDBG trasa ukázala, že náš per-frame kód doběhl čistě a crash je v cizím kódu o frame později). Handbrake-zápisy odstraněny; parkování nyní přes ověřený brzdový pedál (m_fBreakPedal @ 0x4A0) + gas 0. Handbrake se navíc reálně nikdy nenastavoval.

fixdriverarrival detection read wrong offsets + stale position

OnGameProcess četl veh+0x08/+0x0C (transform y,z) jako (x,y) z inline transformu, který je pro fyzikálně jedoucí vozidlo zastaralý. Nyní čte živou pozici z matice (VehicleGetPosition, matrix+0x30) se správnými osami. Actor teď cíl detekuje spolehlivě.

fixdriverdrive/weapon/shoot state survives stream cycle

native HideActor dřív plně wipnul actor descriptor (Free), ShowActor ho re-inicializoval jako Standard. Vehicle actor po vystreamování a návratu přestal jezdit. HideActor nyní jen zahodí GTA-vazbu (CPed + shoot task) a zachová SP descriptor; ShowActor zachovává actorType + rozšířený stav; OnGameProcess znovu naváže CPed a přes ApplyActorState obnoví seat/weapon/drive/shoot.

fixdrivervalidate CVehicle* before use

IsVehiclePtrValid (vtable sanity) brání dereferenci stale pointeru po stream-outu vozidla.

fixdrivercorrect GTA driving-style mapping

rpc::DriveStyle se dřív přímo castoval na eCarDrivingStyle (jiné číslování); default 0=StopForCars se vydával za „AvoidCars". Přidán mapDriveStyle (Normal→AvoidCars 2, IgnoreTraffic→Plough 3, Aggressive→IgnoreLights 4). Konstanty DRIVING_STYLE_* opraveny dle plugin-sdk.

fixdriverapply health for blocking SP_ACTOR_UPDATE

RPC 203 je blocking (SA:MP ho nevidí), takže health se aplikuje přímo na CPed (PedSetHealth, CPed+0x540).

fixgunnerequip weapon before firing

(Likely - nutno ověřit runtime) - shoot RPC (209/210) nesou jen weaponId; arming volal pouze PedSetCurrentWeapon, nikdy GiveWeaponToPed. Bez předchozího SET_WEAPON (208) ped zbraň v inventáři neměl → nic nevystřelil. Nový EnsureWeaponArmed zbraň dá (s municí, default 9999 pokud 0) a teprve pak ji equipne, na všech 4 arming místech. Poznámka: vtable[5]=ProcessPed mechanismus a aim/fire-state stále vyžadují ověření na běžícím klientu.

Změněno

fixstreamingremove blind actor draw-distance patch (crash fix)

PatchActorDrawDistance slepě přepisovalo každý push 200.0f v samp.dll .text. RE R1 samp.dll ukázalo 5 míst, z nichž jen 0x67972 je actor-creation (jeho funkce referuje actor-pool řetězec CNetGame@0x21A0F8 + pools+0x3CD); zbylá 4 patří jiným subsystémům (objekty/pickupy). Blind patch tak korumpoval 4 nesouvisející místa → crash při stream-inu actorů. Volání odstraněno z init cesty. Draw distance bude řešena SP sync vrstvou (plugin drží actor CPed naživu sám), ne patchem samp.dll. PatchActorDrawDistance zůstává dormantní.

refactorgtaconsolidate vehicle/autopilot memory access

roztroušené inline magic offsety (0x390+0x2A, +0x40, +0x08) přesunuty do pojmenovaných, plugin-sdk ověřených akcesorů v gta_funcs.h (VehicleGetPosition, VehicleStopAutopilot, VehicleSetCruiseSpeed, VehicleGetCarMission, VehicleSetHandbrake).

Přidáno

docsreverseRE analýza SA:MP 0.2.5 binárek - RPC systém, Actor pool, dispatch tables
buildInicializace adresářové struktury projektu
docsREADME, CHANGELOG, základní dokumentace

Analýza

analýzasamp_a.dll - 116 RPC handlerů, RakNet vtable offsets, actor dispatch
analýzasamp-server.exe - 7 actor PAWN natives, AMX native table, actor pool global
analýzasamp-server.exe.i64 - IDA Pro databáze, cross-references
analýzaa_actors.inc, actorex.pwn - Actor API a příklady použití

v0.0.1

2026-07-12 (Initial Setup) 1 položek
změnaPočáteční commit, prázdný repozitář

radio + combat UX

4 položek
featcombathitmarker zvuk z assetů + volba v menu

při zásahu (dealt Hit/Kill) se přehraje krátký "cross" zvuk. Zdroj = běžné SP ASSETY streamované ze serveru (SP_SetHitSounds(playerid, id1, id2, id3) → EventCmd::HitSounds registruje asset ids na klientu; demo je bere z hit1/2/3.wav|mp3 přes SP_AssetIdByName v _AssetsReady). V klientském menu (COMBAT) volba Vyp / Zvuk 1-3 + slider hlasitosti; nedostupný slot (chybí asset) je disabled s tooltipem. Přehrává se přes persistent waveOut s retriggerem (Media Foundation dekód libovolného formátu do PCM), nezávisle na herních zvucích. Nová HitSound třída, config combat_hit_sound / combat_hit_sound_vol.

featradiozamčení ladění spolujezdcům (default), konfigurovatelné

SP_ApplyVehicleRadioTune(playerid, station) vynucuje politiku: řidičova stanice se pamatuje per-vozidlo a pushuje spolujezdcům; spolujezdec, který přepne, se vrátí na řidičovu (return -1). SP_SetPassengerRadioLock(false) povolí spolujezdcům ladit nezávisle. User Tracks slot (SP Radio) je vždy povolený. Demo /passengerradio toggle + napojení v OnPlayerRadioStationSP.

fixradioSP Radio (user tracks) se sdílí spolujezdcům podle passenger-locku

dřív SP Radio naladil jen ten, kdo přepnul user tracks (per-hráč), takže spolujezdci nic neslyšeli. Nově (demo): zámek ON → řidič naladí user tracks a SP Radio se automaticky sdílí všem v autě (stejný anchor = totéž současně), včetně spolujezdce, který nastoupí později (OnPlayerStateChange). Zámek OFF → každý si ladí sám. Řidičovo přepnutí na reálnou stanici SP Radio stáhne z celého auta. Off-by-one "fix" v ID stanice VRÁCEN (indexy byly správně - user tracks = 12).

fixradiospam toastu u zamčeného spolujezdce

zamčený spolujezdec cyklující stanice (řidič na user tracks) generoval zeď identických toastů "Ladi jen ridic". Nově: toast rate-limited (1×/3 s), a zamčeného pasažéra v autě na SP Radiu vrátí zpět na SP Radio (force re-tune, cooldown 1 s) místo nativního revertu - drží se řidičovy stanice bez spamu a bez restart-stormu SP streamu.

Vygenerováno z CHANGELOG.md · 2026-09-08 · GitHub ↗