HU/Debugging
Scriptelés során gyakran találkozhat olyan hibákkal, amelyek nem azonnal nyilvánvalóak. Ez az oldal megpróbál rámutatni néhány alapvető stratégiára, hogy megtalálja a hibát.
Contents
Hibakereső konzol
Az MTA tartalmaz egy beépített hibakereső konzolt, mely a scriptekből, vagy az MTA-ból származó hibákat jeleníti meg. Vegye figyelembe, hogy Admin hozzáféréssel kell, hogy rendelkezzen (hacsak nem módosítja ezt az ACL-ben), megnyithatja a hibakereső konzolt a debugscript *x* beírásával, ahol az x a hibakeresési szintet jelöli:
- 1: csak hibákat
- 2: hibákat és figyelmeztetéseket
- 3: hibákat, figyelmeztetéseket és információs üzeneteket
A debugscript 3 beírásával az összes üzenet látható. A 3-as és a 2-es szint a legtöbb helyzetben ajánlott. A debugscript-nek engedélyezettnek kell lennie, amikor teszteli a scriptjeit, ez segíteni fog, hogy könnyedén észrevegye és megoldja az esetleges gépelési hibákat, vagy más problémákat.
Példa
Ez a példa tartalmaz két hibát:
function SayHello(message, player) if (getPlayerName(player) == "Fedor") outputChatbox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Ha ezt a kódot lefuttatjuk akkor ezt a hibaüzenetet fogjuk kapni:
- INFO: Loading script failed: myResource\script.lua:2: 'then' expected near ´outputChatbox'
Ez azt jelenti, hogy a scriptet nem lehet elemezni, mert szintaktikai hiba volt benne. Ez megmutatja a script elérési útját a resource könyvtárból, így azonosítani tudja azt a scriptet, ahonnan a hiba származik. A fájlnév után egy kettőspontot és egy számot jelenít meg, ez a szám azt a sorszámot jeleníti meg, ahol megtalálhatja hol van a hiba a script-ben. Azután pedig a hibaüzenet, mely a létrejövő hibától függően változik. Ha megnézzük a hibaüzenetet, akkor könnyen megállapíthatjuk, hogy a hiba a myResource könyvtárból származik, és a script.lua fájljának a második sorából. A hibaüzenet tisztán mutatja, hogy elfelejtettük a "then"-t! De ezt könnyedén kijavíthatjuk!
function SayHello(message, player) if (getPlayerName(player) == "Fedor") then outputChatbox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Most a script szépen be fog töltődni és semmilyen hibaüzenetet nem fog visszaadni mindaddig, míg egy 'Fedor' nevű játékos nem ír ki valamit a chat-ba, ebben a pillanatban a hibakereső konzól ezt fogja visszaadni:
- ERROR: myResource\script.lua:2: attempt to call global 'outputChatbox' (a nil value)
Ez az error azt jelenti, hogy az outputChatbox egy nil érték, az-az, nem létezik. Ez azért van, mert a funkciót outputChatBox-nak hivják, és nem outputChatbox-nak. Figyeljen a nagy 'B' betűre:
function SayHello(message, player) if (getPlayerName(player) == "Fedor") then outputChatBox("Hello Fedor") end end addEventHandler("onChatMessage", root, SayHello)
Természetesen ez csak egy példa, és csak a hibaüzeneteknek a felszínét érintettem. Nagyon sok más üzenet és eshetőség létezik, de egy általános elképzelést kell kapnia.
Szerver & Cliens hibakeresési napló
Szerver
Menjen a: (MTA mappa)>server>mods>deathmatch mappába
Van itt két majdnem azonos fájl.
- A local.conf fájl a "host game" menü item-re vonatkozó beállításokat tartalmazza, mely az MTA főmenüjében van. Ez egy gyors és egyszerű módja az ideiglenes szerver indításának a client oldaláról. Ha a client (host) leáll, akkor a server is leáll.
- Az mtaserver.conf akkor van használatban, amikor az "MTA Server.exe" végrehajtódott az (MTA fő mappa)>server-ből. Ez futtatja a szervert a saját konzolablakában, mely nem igényli a client meglétét vagy futtatását. Ez akkor lehet hasznos, ha hosszabb időre szeretne szervert futtatni.
Attól függen, hogy milyen módon futtat, aszerint kell szerkeszteni ezeket a fájlokat. A kérdéses beállítások a következők:
<!-- Megadhatja a debugscript log fájl helyét és nevét. Ha üresen hagyja, akkor a server nem fogja elmenteni ezt a fájlt --> <scriptdebuglogfile>logs/scripts.log</scriptdebuglogfile> <!-- Megadhatja a log fájl debug szintjét! Lehetséges értékek: 0, 1, 2, 3. Ha nincs beállítva, akkor automatikusan 0. --> <scriptdebugloglevel>0</scriptdebugloglevel>
Bizonyosodjon meg arról, hogy megvan-e adva a log név. Azt is meg tudja határozni, hogy a hibák melyik szintje legyen naplózva. Ha ez 0, akkor semmi nem lesz naplózva. A szintek magyarázata a cikk tetején található. Ha a naplózási szint megváltozott 3-ra, akkor ebben az esetben minden szerver oldali script hibát a (MTA fő mappa)>server>mods>deathmatch>logs>scripts.log fájlba fogja naplózni.
Kliens
Menjen a: (MTA mappa)>server>clientscript.log
Ez a file az összes kliensoldali script hibákat naplózza. Ez alapértelmezetten naplózásra van beállítva, nincs szükség beállításra.
Hibakeresési stratégiák
Számos stratégia létezik, amelyek támogatják a hibakeresést. Legtöbbjük közé tartozik a kimenő Hibaüzenetek, a különböző információktól függően helyezkednek el.
Hasznos funkciók
Vannak olyan funkciók, amelyek jól jöhetnek a hibakereséshez.
- outputDebugString vagy outputChatBox bármilyen információ kiírásához (használja az outputDebugString a technikai kiíráshoz)
- tostring() egy változó, amely az értéket egy string-re változtatja. Hasznos, ha az érték nem egy szám vagy egy string.
- inspect visszaad egy az ember számára olvasható stringet bármilyen adattípusról, beleértve a táblákat, nem elemi felhasználói adatokat, mint az ACL groupok, felhasználók, elemeke, stb.
- iprint hasonló az ellenőrzéshez, kivéve, hogy annyi paramétert vesz igénybe, amennyi szökséges, majd kiírja őket a debugscript-be.
- getElementType ellenőrzni az MTA elem típusát.
- isElement ellenőrzi, hogy az MTA elem létezik-e.
Adjon meg debugmessage-t annak ellenőrzéséhez, hogy mikor, illetve milyen gyakran hajtja végre a kód egy részét
Egy tipikus példa igazolja, hogy az if-rész az végrehajtódott-e, vagy sem. Ehhez csak adjon hozzá egy olyan üzenetet, amit majd később fel fog ismerni az if -részben.
if (variable1 == variable2) then outputDebugString("variable1 is the same as variable2!") -- do anything end
Egy másik használata a változó értékek módosításának ellenőrzése lenne. Először keresse meg a szerkeszteni kívánt változó összes előfordulását, majd addjon hozzá egy üzenetet.
Adjon hozzá debugmessage-t egy változó értékének ellenőrzéséhez
Tegyük fel, hogy létre szeretne hozni egy markert, de nem abban a pozícióban jelenik meg, mint amire számított. Az első dolog, amit érdemes lehet ellenőrizni, hogy a createMarker függvény lefutott-e. De amíg ezt használja, közben azokat az értékeket is tudja ellenőrizni, amik a createMarker funkcióban vannak használva.
outputChatBox("posX is: "..x.." posY is: "..y.." posZ is: "..z) createMarker(x,y,z)
Ez mind a három változót kiírja, melyek a marker kordinátájaként lettek használva. Feltételezve, hogy kiolvassa ezeket a map fájlból, most már össze tudja hasonlítani a debug kiírást a kívánt értékekkel. A tostring() gondoskodni fog arról, hogy a változók értékei összefűzhetőek legyenek stringként, még akkor is, ha ez egy logikai érték.
Példa
Képzelje el, hogy valahol létrehozott egy collision shape-t, és szeretné, ha valami végrehajtódna miután a játékos tíz másodpercig benne marad:
function colShapeHit(player) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit", root, colShapeHit) function colShapeLeave(player) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave", root, colShapeLeave)
Amikor a játékos belép a colshape-be, a debugscript a következő üzenetet fogja kiírni:
- ERROR: ..[path]: attempt to index global 'colshapeTimer' (a nil value)
Ez azt jelenti, hogy megpróbált rámutatni egy olyan táblára, ami nem létezik (mert a tábla egy nil értékű, nem létezik). Ez akkor történik, amikor a timer id-jét a táblába akarja helyezi. Hozzá kell adnunk egy ellenőrzést, hogy a tábla létezik-e, és ha nem, akkor hozzon létre egyet.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit", root, colShapeHit) function colShapeLeave(player) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",root,colShapeLeave)
Még mindig figyelmeztetést fogunk kapni, amikor a játékos belép a colshape-be, várjon az üzenetre, majd lépjen ki belőle (colshape-ből):
- WARNING: [..]: Bad argument @ 'killTimer' Line: ..
Ettől eltekintve (erről majd később beszélünk) minden jónak tűnik. A játékos belép a colshape-be, a timer elindul, ha ott marad az üzenet megjelenik, ha kilép, a timer megsemmisül.
Egy kevésbé észrevehető hiba
De valami oknál fogva az üzenet kétszer jelenik meg amikor a colkörben marad egy autóban ülve. Úgy tűnik, hogy valamennyi kód kétszer hajtódik végre, ezért debug üzenetet adunk hozzá, hogy ezt ellenőrizze.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a debug message outputDebugString("colShapeHit") -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a debug message outputDebugString("colShapeLeave") -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Most észre vesszük, hogy mindkét handler functions kétszer hajtódik végre, amikor egy járműben vagyunk, de csak egyszer, ha gyalogosok vagyunk. Úgy tűnik, hogy a jármű meghívja a colshape-t is. Az elmélet megerősítése érdekében biztosítjuk, hogy a player változó valójában egy játékoselemre hivatkozzon.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
A debug elmondja nekünk, hogy az egyik player változó az egy játékos, a másik pedig egy jármű elem. Mivel csak arra akarunk reagálni, ha egy játékos lép be a colshape-be, ezért hozzáadunk egy if-et, mely megakadájozza, hogy a function lefusson, ha ez nem egy játékos elem.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Most a scriptnek a kívánt módon kell működnie, de továbbra is kiírja a fent említett hibaüzenetet. Ez azért van, mert az időzítő, melyet ki akarunk kapcsolni mikor egy játékos elhagyja a colshape-et, már nem létezik mikor elérte a 10 másodpercet (és ezáltal lefutott a tizedik másodperc után). Különböző módokon tudunk megszabadulni ezektől a figyelmeztetésektől (mivel tudjunk, hogy a timer már nem létezik, és csak akkor akarjuk megsemmisíteni, ha létezik). Az egyik mód az, hogy ellenőrizzük, tényleg létezik-e a táblázatban szereplő timer. Ehhez használnunk kell az isTimer-t, amit akkor fogunk használni, amikor megsemmisítettük a timert:
if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end
Tehát a teljes működő kód így nézne ki:
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Teljesítménybeli problémák elhárítása
Ha a szervere több erőforrást használ el, mint kelle, vagy csak azt szeretné, hogy a scriptjei hatékonyak legyenek, megtalálhatja a probléma forrását egy nagyszerű eszköz használatával, amely az alapértelmezett resource csomaggal együtt érkezik: performancebrowser. Elindíthatja a 'start performancebrowser'-el. Ha ez nem létezik, akkor letöltheti a legfrissebb resourcet a GitHub repository-ról. Ez az eszköz hihetetlen mennyiségű információt nyújt a teljesítménybeli hibakereséshez. Memória szivárgások, elem szivárgások, CPU igényes scriptek könnyedén megtalálhatóak a teljesítményböngészőn keresztül. Ha a 'd' opciót használja a Lua időzítőben, akkor láthatja, hogy mely function-ök használja a CPU-t.
A teljesítményböngésző eléréséhez megkell nyitni a web böngészőt, és beírni a következő címet: http://serverIPHere:serverHTTPPortHere/performancebrowser/ Vegye figyelembe, hogy a vége az szükséges. Például: http://127.0.0.1:22005/performancebrowser/ Be kell jelentkeznie egy játékbeli admin felhasználóval, vagy egy olyan felhasználóval mely jogosult a 'resource.performancebrowser.http' és 'resource.ajax.http' belépéshez. A legtöbb szükséges információ a Lua időzítőben és a Lua memória kategóriákban található, olyan értékeket keressen, amelyek sokkal magasabbak, mint a többi érték.
Példák olyan scriptekre, amelyek teljesítményproblémákat okozhatnak
Adatok hozzáadása egy táblához, de nem távolítja el sosem. Ez hónapokba/évekbe telhet, mielőtt problémákat okozna.
local someData = {} function storeData() someData[source] = true -- There is no handling for when a player quits, this is considered a memory leak -- Using the Lua timing tab you can detect the RAM usage of each resource. end addEventHandler("onPlayerJoin", root, storeData)
Az elem szivárgás lehetséges, ha ideiglenes colshape-eket használ, és nem távolítja el azokat. Ez idővel sávszélességi, CPU és memória teljesítménybeli problémákat okozhat.
function useTemporaryCol() local col = createColCircle(some code here) if (normally this should happen) then destroyElement(col) end -- But sometimes it didn't so the script ended but the collision area remained and over time -- you may end up with hundreds to thousands of pointless collision areas. -- The Lua timing tab allows you to see the amount of elements each script has created. end
Magas CPU igényes szerverek FPS dropokat eredményezhetnek, amitől a szerver játszhatatlanná válik. Ez egy forgalmas szerveren 24 órán belül nagy károkat okozhat. A "refs" mennyisége a Lua időzítőben felismeri az ilyen fajta scripteket, meglepő módon a Lua időzítő nem segített ebben az esetben, de a Lua memória igen.
addEventHandler("onPlayerJoin", root, function() -- Code for joiner addEventHandler("onPlayerQuit", root, function() -- Code for when they have quit -- See the problem? It's bound to root which the event handler is being added again and again and again end) end)
Egy function túl intenzíven használja a processzorját, mert bármit is csinál az sok időt vesz igénybe. Ez csak egy function, ami sok időt vesz igénybe a befejezéshez. Teljesítményböngésző nélkül nem tudná, hogy mi okozza, de a teljesítményböngészővel láthatja a Lua időzítőben, hogy a resource sok CPU-t használ. Ha beírja az Options mezőbe a 'd'-t, akkor még a fájl nevét is megmondja, és a function első vonalánál, hogy mennyi CPU-t használ.
function someDodgyCode() for i=1, 100000 do -- some code end end
Fordította
2018.11.05. Surge
2017.04.17. Kevin