Saveti o skriptanju

Započeo Bit Frosty, Maj 07, 2021, 14:02:51 POSLE PODNE

prethodna tema - sledeća tema

0 članova i 1 gost pregledaju ovu temu.

Ovo su vise saveti nego tutoriali, ali posto je najprikladniji forum za ovakvu temu onda i objavljujem ovde.
Napomenucu odmah, vecina koda za primere je iz Panama Roleplay, nema nikakvog razloga sto sam njega uzeo osim sto mi je prvi upao u oci kad sam otvorio downloads. Nazalost kod nas sve je copy/paste iz istih modova, i vecina gresaka se nadje svugde u svakom kodu.





Skracivanje imena funkcija i ostalih stvari

[pawn]
#define SPD                                                     ShowPlayerDialog
#define SCMA                                    SendClientMessageToAll
#define DSL                                        DIALOG_STYLE_LIST
#define IPI                                        INVALID_PLAYER_ID
#define DSI                                                   DIALOG_STYLE_INPUT
#define DSP                                                DIALOG_STYLE_PASSWORD
#define DSMSG                                      DIALOG_STYLE_MSGBOX
[/pawn]

To je pogresno, ime funkcije treba da sto preciznije i krace opise njenu funkciju to jest ono sto ona radi. Ako je SendClientMessage onda odmah znas da je to slanje poruke clientu (igracu u nasem slucaju). I nije bitno u kojim jezicima i sta si radio, znaces sustinu funkcije. Ukoliko vam je tesko da kucate, koristite neki IDE koji podrzava autocomplete. Za sa-mp server bih preporucio Visual Studio Code ili Sublime (po meni Visual Studio Code ja lakse da se podesi i ima bolji intelisense - to jest bolje uocava greske i upozorava na nih).

Primer iz Sublime text editora:

Kao sto vidite kucanjem SCM se svejedno moze dobiti puno ime funkcije i nema potrebe za skracivanjem, sve sto treba da uradite je da stisnete space ili tab nakon sto naidjete na odgovarajucu preporuku od intelisense. A takva navika ce vam pomoci ubuduce kada budete prelazili na nove jezike i u nova okruzenja.





Definicije boja

Definicije boja (mada i ostalih stvari), gledao sam da dosta ljudi definise neke stvari vise puta pod razlicitim imenima, i da mnogo puta te definicije su bezpotrebne.
[pawn]
#define CRVENA                                                        0xfa5555AA
#define ZELENA                                                          0x33AA33AA
#define BELA                                                          0xFFFFFFFF
#define CRNA                                             0x000000FF
#define PLAVA                                                         0xA9C4E4FF
#define NARANDZASTA1                                            0xFF9933AA
#define SIVA                                                          0xAFAFAFAA
#define ZUTA                                                          0xFFFF00AA
#define Helper                                              0x00FF00FF
#define NARANDZASTA                                                   0xFFAF00FF
#define LJUBICASTA                                            0xC2A2DAAA
#define SVETLOPLAVA                                            0x33CCFFAA
#define SVETLOCRVENA                                        0xFF6347AA
#define PANAMA                                                        0x2e9cd1FF
#define ANTICHEAT                                          0xED6412FF
#define NARACRVENA                                                      0xFF4500AA
#define WARLJUB                                            0x8B008BAA

#define pink                              "{eb2fd5}"
#define green                             "{3bab16}"
#define white                             "{ffffff}"
#define red                                 "{e82323}"
#define blues                               "{24c1ff}"
#define yellows1                            "{e8e52c}"
#define yellows2                          "{edd351}"
#define oranges1                            "{ff9430}"
#define oranges2                            "{fc681e}"
#define greens1                               "{61ba6e}"
#define greens2                             "{318739}"
#define reds                              "{c73a3a}"
#define yellow                            "{e8e52c}"
[/pawn]
Ovo je jedan od primera kako najgore uraditi takve stvari.

  • Imena boja su pola na srpskom, pola na engleskom.

  • Neka imena boja su definisana kao mnozina.

  • Neke boje su definisano samo za jedan format.

  • RGBA i SA-MP embeded boje se ne poklapaju po RGB.

Imena i nazive stvari uvek pokusavajte da nazivate na engleskom. Samo nihove vizuelne reprezentacije stavljajte na srpskom ili lokalizujte (u slucaju sa vise jezika).

Uvek pokusavajte da definisite stvari kako jesu, i da pri koristenju zvuce logicno. Ako napravite niz new Houses[50];, i onda pristupate nizu Houses[id] to je potpuno pravilno, ali kada citate zvuci kao da to cemu ste pristupili je u stvari niz (a ne samo elemenat niza), i da ima jos makar jednu dimenziju. Zato je pametnije da definisete kao new House[50];.

Ukoliko imate stvari koje se predstavljaju u razlicitim formatima ili oblicima, pokusajte da ih definisete samo jednom pa da ih konvertujete, ili da ih obavezno definisite u svim formatima/oblicima. Mozda vam trenutno treba samo u jednom obliku, ali sutra kada budete radili moze vam zatrebati i moze da vas zbuni zasto neceg nema u drugom obliku, da li ima razlog za to ili ne.

Kao sto sam gore vec rekao, probajte da definisete samo jednom, ako ne onda u oba formata, ali tako da bude isto. Ako SendClientFunkcija ocekuje RGBA boju kao drugi argument, a takodje moze da koristi embeded rgb format u trecem argumentu, onda definisite boje da budu iste.
[pawn]
#define CRVENA 0xfa5555AA
#define red "{e82323}"
[/pawn]
To ne samo da su dve boje pod razlicitim nazivom (jezikom), u razlicitom formatu, vec su i dve razlicite boje
Ako uzmemo 0xfa5555AA kao osnovnu i pravilnu boju onda bi i embeded trebala da bude "{FA5555}"
To jest crvena boja pravilno definisana u RGBA i SA-MP Embeded formatu ( {RGB} } ) bi bilo nesto kao:
[pawn]
#define COLOR_RED     0xFA5555AA
#define EMBEDED_RED   "{FA5555}"
[/pawn]
Sad da li ce te vi imati prefix color_ da li on biti vekilim ili malim slovom, to je sve stvar licnog misljenja. Ali to da bude na engleskom, da u oba formata bude ista boja to je ono sto je pravilno (i logicno na kraju krajeva).





Funkcije i njihova upotreba

Zapamtite jednu stvar, funkcije su vasi najbolji prijatelji u programiranju. Svako odvajanje logike je uvek dobra stvar (dokle god se ne pretera kao i sa alkoholom :D )

Ukolio vidite da ponajvlate neke stvari cesto, onda je to verovatno posao za funkciju. Neki od primera bi bilo da ako cuvate logove, pa svaki put kad treba da cuvate, koristite fopen, format, fwrite, fclose...Ako to napisete 2x, to je ok, ako vam to zatreba i treci put, dobra ideja je da se napravi funkcija LogInFile/SaveLog (ili kako god vi mislite da je pametno da nazovete vasu funkciju).

U pawn ima naredba stock koja prigusuje unused (neiskoristeno) upozorenje za neku definiciju (promenive, funkcije i slicno). Jako je korisna za includove i module kada hocete da definisete nesto sto se mozda nece koristiti ali opet nekome moze biti korisna i on ce je iskoristiti, onda se funkcije definisu sa stock naredbom. Ali ne i funkcije u gamemodu! Ako ste napravili neku funkciju u skripti a ne koristite je onda je lakse da je komentarisete. Mada to obicno kaze da ste nesto zaboravili. Zato nikad ne definiste stvari sa stock osim ako tacno znate da to tako treba, i da je to za ubuduce. Opet, lakse je da se komentarise, pa kada se pozove a funkcija ne postoji (komentarisana je), najde se u skripti i opet se prodje okom da se proveri da li je sve tacno i kako treba da bude. Izuzetak ovome su modularne skripte gde svaki modul treba da ima funkcije preko kojih ce mod komunicirati sa tim modulom. I funkcije treba da se prave za sve sto se moze koristiti (nemora da znaci da ce biti iskoristeno odmah ili ikad). Kako se ovo moze objasniti? Lako, moduli i jesu includovi - tacka.

Nemojte sve da trpate u jednu funkciju, odvajajte logicki sve sto se moze odvojiti a da ima smisla.
Recimo ovakva komanda (koja je u stvari public funkcija):
[pawn]
CMD:altchat(playerid)
{
   if( PlayerInfo[ playerid ][ xAdmin ] < 1 ) return SendErrorMessage(playerid, "Niste ovlasceni.");
   if( ALTPoruke[ playerid ] )
   {
      for (new i = 0; i < MAX_LINES; i ++)
      {
         strmid(AltChatTD_Text[playerid], " ", 0, 1);
      }
      for (new i = 0; i < MAX_LINES; i ++)
      {
         PlayerTextDrawSetString(playerid, AltChatTD_Player, AltChatTD_Text[playerid]);
         PlayerTextDrawShow(playerid,AltChatTD_Player);
      }
      ALTPoruke[ playerid ] = false;
      SendInfoMessage(playerid, "Ugasili ste alternativni cet!");
   }
   else if( !ALTPoruke[ playerid ] )
   {
      ALTPoruke[ playerid ] = true;
      SendInfoMessage(playerid, "Upalili ste alternativni cet!");
   }
   return 1;
}
[/pawn]
Logicnije je da se prikazivanje i sakrivanje textdrawvova vrsi uz pomoc funkcije. Tako da ako hocemo da sakrijemo chat privremeno (recimo na primer koristimo kameru necemo da nam TD-ovi smetaju) to mozemo a da ne ponavljamo sami sebe. Pomerajuci loop u funkciju mozemo kasnije da pozivamo samo funkciju.
[pawn]
CMD:altchat(playerid)
{
   if( PlayerInfo[ playerid ][ xAdmin ] < 1 ) return SendErrorMessage(playerid, "Niste ovlasceni.");
   if(!ShowPlayerAltChat(playerid)) return SendErrorMessage(playerid, "Error pri prikazivanju poruka");

   ALTPoruke[ playerid ] = !ALTPoruke[ playerid ];

   if(ALTPoruke[ playerid ] )
      ShowPlayerAltChat(playerid);
   else
      HidePlayerAltChat(playerid);

   SendInfoMessage(playerid, "%s ste alternativni cet!", ALTPoruke[ playerid ] ? ("Upalili") : ("Ugasili"));
   return 1;
}

ShowPlayerAltChat(playerid)
{
   if(!IsPlayerConnected(playerid)) return 0;
   for (new i = 0; i < MAX_LINES; i ++)
   {
      strmid(AltChatTD_Text[playerid], " ", 0, 1);
      PlayerTextDrawSetString(playerid, AltChatTD_Player, AltChatTD_Text[playerid]);
      PlayerTextDrawShow(playerid,AltChatTD_Player);
   }
   return 1;
}

HidePlayerAltChat(playerid)
{
   if(!IsPlayerConnected(playerid)) return 0;
   for (new i = 0; i < MAX_LINES; i ++)
   {
      PlayerTextDrawHide(playerid,AltChatTD_Player);
   }
   return 1;
}
[/pawn]

Takodje je dobra praksa da se svaki input (unos) sanira/validuje sto je pre moguce (vise o tome ispod).





Nizovi i pelje (loop-ovi)

[pawn]
enum oOrgInfo {
   oID,
   oName[ ORG_IME ],
   oPreFix[ 10 ],
   oColor[ 24 ],

   oTip,
   oMaxClanova,
   oUbacenihClanova,

    oSkin1,
   oSkin2,
   oSkin3,
   oSkin4,
   oSkin5,
   oSkin6,
   
   oZSkin1,
   oZSkin2,
   oZSkin3,
   oZSkin4,
   oZSkin5,
   oZSkin6,

    oLider1[ ORG_LIDER ],
    oLider2[ ORG_LIDER ],

   oClan1[ ORG_CLAN ],
   oClan2[ ORG_CLAN ],
   oClan3[ ORG_CLAN ],
   oClan4[ ORG_CLAN ],
   oClan5[ ORG_CLAN ],
   oClan6[ ORG_CLAN ],
   oClan7[ ORG_CLAN ],
   oClan8[ ORG_CLAN ],
   oClan9[ ORG_CLAN ],
   oClan10[ ORG_CLAN ],
   oClan11[ ORG_CLAN ],
   oClan12[ ORG_CLAN ],
   oClan13[ ORG_CLAN ],
   oClan14[ ORG_CLAN ],
   oClan15[ ORG_CLAN ],
   oClan16[ ORG_CLAN ],
   oClan17[ ORG_CLAN ],
   oClan18[ ORG_CLAN ],
   oClan19[ ORG_CLAN ],
   oClan20[ ORG_CLAN ],
   oClan21[ ORG_CLAN ],
   oClan22[ ORG_CLAN ],
   oClan23[ ORG_CLAN ],
   oClan24[ ORG_CLAN ],
   oClan25[ ORG_CLAN ],
   oClan26[ ORG_CLAN ],
   oClan27[ ORG_CLAN ],
   oClan28[ ORG_CLAN ],
   oClan29[ ORG_CLAN ],
   oClan30[ ORG_CLAN ],

   oRank1[ ORG_RANK ],
   oRank2[ ORG_RANK ],
   oRank3[ ORG_RANK ],
   oRank4[ ORG_RANK ],
   oRank5[ ORG_RANK ],
   oRank6[ ORG_RANK ],

   Float:oPozExtX,
   Float:oPozExtY,
   Float:oPozExtZ,

   Float:oPozIntX,
   Float:oPozIntY,
   Float:oPozIntZ,

   Float:oDutyPoint[ 3 ],
   oDutyInt,
   oDutyVW,
   Float:oEquipPoint[ 3 ],
   oEquipInt,
   oEquipVW,

    oMaxPort,
   oControlType,
   Float:oDrugField[ 3 ],
   Float:oSafePos[ 3 ],
   oSafeMoney,
   oSafeDrug[ 4 ],
   oSafeDrugCode[ 4 ],

   oInt,
   oVw
}
[/pawn]
Ako pogledate malo taj enum primeticete oClan1 do oClan30 (pored slicnih stvari koje se ponavljaju). Zlatno pravilo je da ako imate nesto sto se ponavlja, onda se to moze i definisati kao niz i provuci kroz petlju. Nemojte sebe da ponavljate, nemate potrebe za time, i olaksacete sebi zivot koristeci nizove i petlje. Da, mozda je za sitnicu lakse tako, ali sutra ce to trebati da se prosiri i umesto 1 stvari da izmenite moracete da izmenite 50, plus vam niko nece garantovati da niste negde zaboravili da dodate/izmenite nesto.

Umesto toga da sve trpate u enum, da ponavljate, i da se mucite zar nije lakse da se definise:
[pawn]new oClan[MAX_ORGANISATIONS][MAX_ORG_MEMBERS][MAX_PLAYER_NAME];[/pawn]
Primetite MAX_ORGANISATIONS umesto broja, MAX_ORG_MEMBERS umesto ORG_CLAN, da se ne bi pitali kasnije da li je Klan ili %u010Clan. MAX_PLAYER_NAME, ukoliko u nekoj od sledecih verzija odjednom povecaju/skrate maksimalnu duzinu nika

Da umesto u komandama/funkcijama imate ovo:
[pawn]
      if( strcmp( imeigraca, OI[ OrgID ][ oClan1 ], true ) == 0) { strmid( OI[ OrgID ][ oClan1], "Niko", 0, strlen( "Niko" ), ORG_CLAN); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan2 ], true ) == 0) { strmid( OI[ OrgID ][ oClan2 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan3 ], true ) == 0) { strmid( OI[ OrgID ][ oClan3 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan4 ], true ) == 0) { strmid( OI[ OrgID ][ oClan4 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan5 ], true ) == 0) { strmid( OI[ OrgID ][ oClan5 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan6 ], true ) == 0) { strmid( OI[ OrgID ][ oClan6 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan7 ], true ) == 0) { strmid( OI[ OrgID ][ oClan7 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan8 ], true ) == 0) { strmid( OI[ OrgID ][ oClan8 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan9 ], true ) == 0) { strmid( OI[ OrgID ][ oClan9 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan10 ], true ) == 0) { strmid( OI[ OrgID ][ oClan10 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan11 ], true ) == 0) { strmid( OI[ OrgID ][ oClan11 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan12 ], true ) == 0) { strmid( OI[ OrgID ][ oClan12 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan13 ], true ) == 0) { strmid( OI[ OrgID ][ oClan13 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan14 ], true ) == 0) { strmid( OI[ OrgID ][ oClan14 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan15 ], true ) == 0) { strmid( OI[ OrgID ][ oClan15 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan16 ], true ) == 0) { strmid( OI[ OrgID ][ oClan16 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan17 ], true ) == 0) { strmid( OI[ OrgID ][ oClan17 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan18 ], true ) == 0) { strmid( OI[ OrgID ][ oClan18 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan19 ], true ) == 0) { strmid( OI[ OrgID ][ oClan19 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan20 ], true ) == 0) { strmid( OI[ OrgID ][ oClan20 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan21 ], true ) == 0) { strmid( OI[ OrgID ][ oClan21 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan22 ], true ) == 0) { strmid( OI[ OrgID ][ oClan22 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan23 ], true ) == 0) { strmid( OI[ OrgID ][ oClan23 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan24 ], true ) == 0) { strmid( OI[ OrgID ][ oClan24 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan25 ], true ) == 0) { strmid( OI[ OrgID ][ oClan25 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan26 ], true ) == 0) { strmid( OI[ OrgID ][ oClan26 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan27 ], true ) == 0) { strmid( OI[ OrgID ][ oClan27 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan28 ], true ) == 0) { strmid( OI[ OrgID ][ oClan28 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan29 ], true ) == 0) { strmid( OI[ OrgID ][ oClan29 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan30 ], true ) == 0) { strmid( OI[ OrgID ][ oClan30 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else return SendErrorMessage( playerid, "Taj igrac nije u izabranoj organizaciji!" );
[/pawn]
Zar nije lakse da napravite funkciju
[pawn]
      RemoveMemberFromOrganisation(orgid, memberName[])
      {
         if( MAX_ORGANISATIONS <= orgid < 0) return -2;
         for(new i = 0; i < MAX_ORG_MEMBERS); i++)
         {
            if( strcmp( imeigraca, oClan[orgid], true ) == 0)
            {
               strmid( oClan[ orgid ][ i ], "Niko", 0, strlen( "Niko" ), MAX_ORG_MEMBERS );
            }
            return i;
         }
         return -1;
      }

[/pawn]
I onda je samo pozovete?
[pawn]
      new memberid = RemoveMemberFromOrganisation(OrgID, imeigraca)
                if(memberid == -2) return SendErrorMessage( playerid, "Nepostojaca organizacija!" );
      if(memberid == -1) return SendErrorMessage( playerid, "Taj igrac nije u izabranoj organizaciji!" );
[/pawn]

Sada imate funkciju i ako vam ikada zatreba da maknete igraca iz organizacije samo je pozovete. Ako se pitate gde i kako vam to moze zatrebati osim u komandi ne treba dugo da razmisljate, ako recimo igrac dobije ban. Banovani igraci ne trebaju da zauzimaju slotove u organizacijama, po meni je to ligicno.

Pazite, imaju razlicite konvencije naziva stvari. Neko voli da sve naziva isto, neko voli da za promenive koristi jedno, za funkcije drugo i slicno. Po meni je to sve pitanje licnog misljenja i kako ko voli. Mislim da je sve ispravno dokle god se prati postojeca konvencija. Recimo sa-mp, funkcije su nazvane u CamelCase formatu, to jest SendClientMessage, PlayerTextDrawSetString i tako dalje (Mesali su i oni pomalo, pogotovo redosled i logiku ali OpenMP radi na tome da to ispravi). Zato i ja pratim CamelCase za funkcije, kao sto sam za primer uzeo
RemoveMemberFromOrganisation. Ime funkcije tacno opisuje sta radi, poklapa se sa nacinom kako su definisane nativne sa-mp funkcije, prvo sanira input, vraca gresku i tek onda radi ono sto treba (O validaciji i sanaciji inputa ispod).





Sanacija i validacija inputa (unosa/ulaza)

Pre nego sto pocnem, da naglasim nesto. Sanacija i validacija se vecinom preklapaju i mesaju posto rade jako slicne stvari i obicno se rade zajedno. Ako neko kaze sanacija za validaciju (ili obrnutu) nemojte odmah da ga napadate posto covek nije rekao nista pogresno, samo je iskoristio drugaciji termin. Iako se tehnicki oni razlikuju, u praksi se sretnu oba izraza za obije radnje, i niko normalan nece praviti problem oko toga posto vecinom rade istu stvar.

A za one koji bi samo hteli da znaju
Sanacija je kada ispravljate input (ili neke druge stvari), ali se i vracanje greske nakon validacije moze reci da je sanacija. Posto u sustini posao saniranja inputa je da ne dozvolimo invalid vrednosti dalje u kodu.
Validacija je kada proveravate da li je input ocekivan, u formatu i obliku kojem treba da bude (i mozda vracamo gresku). Ukoliko se taj input ispravlja to je vec sanacija (nakon validacije).

Recimo kada ocekujete integer kao argument komande, a igrac unese float, mi ga validujemo, vidimo da je invalid i vracamo gresku, ili saniramo to tako sto pretvaramo float u integer (ukoliko je to moguce i ispunjava nase uslove ako ih imamo).

Zlatno pravilo je da svaka funkcija za sebe validuje i sanira input (argumente), pogotovo onaj od strane korisnika (i administratora - oni su takodje korisnici). Ako recimo imate funkciju koja ocekuje id igraca, a taj id igraca se koristi za pristup nizu, unos nepostojeceg id-a moze dovesti do overflow-a.
Recimo za primer imamo ovakav uprosteni slucaj:
[pawn]
new pAdminLevel[MAX_PLAYERS];
SerPlayerAdminLevel(playerid, level)
{
    pAdminLevel[playerid] = level;
}
[/pawn]

Niz nam je za MAX_PLAYERS (recimo 1000 koliko je najvise u samp-u).
Za primer ako ne saniramo input u komandi a pozovemo nasu funkciju, i recimo sscanf (ili strval ili nesto trece) nam vrati INVALID_PLAYER_ID (65535), ili cellmin/cellmax, funkcija ne proverava playerid da li je validan vec direktno pristupa nizu. Tu odmah imamo memory overflow (sto ce verovatno izazvati crash). Zato se input uvek treba sanirati, i sanirati sto pre.

[pawn]
SerPlayerAdminLevel(playerid, level)
{
    if(!IsPlayerConnected(playerid)) return 0;
    if(MAX_LEVEL < level < 0) return 0;
    pAdminLevel[playerid] = level;
    return 1;
}[/pawn]
Posto IsPlayerConnected funkcija proverava da li je igrac konektovan, a nepostojeci id (manji od 0 ili vec od broja slotova) nikad nemoze biti konektovan, onda ce ona da sanira playerid. Nakon sto smo to sanirali mozemo da saniramo level, s jedne strane je nepotrebno, ali sa druge stvari ako se taj level prosledi nekom nizu negde drugo, moze doci do overflow, zato se uvek sanira (pa cak i ako se ista stvar proverava u razlicitim funkcijama koje pozivaju jedna drugu). Ovakve sitne provere vam nikad nece usporiti server, makar ne da vi to osetite, i smatraju se dobrom praksom u programiranju.

Sanaciju inputa najvise mozemo da vidimo u komandama, i tu je ona najbitnija posto vecinom komande pozivaju igraci i prosledjuju svoj input (sto otvara vrata za svakakve bagove, greske i sigurnostne rizike).
Input se sanira sto pre je to moguce, i dobra praksa je da se odmah vraca greska cim se na nju naidje (i to je zdrav smisao da cim vidis da nesto nemozes, stanes i kazes nemoze to tako a ne da zavrsis sve pogresno i kazes nisam mogao to tako).
Recimo za primer ova komanda:
[pawn]
CMD:cc( playerid, params[], help) {
   if( PlayerInfo[ playerid ][ xAdmin ] >= 1 ) {
      for( new j; j < 110; j++ ) {
         SendClientMessageToAll( -1, "" );
      }
      SendClientMessageToAll(-1, ""blues"Clear Chat "pink"| "blues"Chat je ociscen od strane "pink"Admina.");
      SendClientMessageToAll(-1, ""pink"| "blues"www."pink"panama-roleplay"blues".info "pink"|");
   }
   else return SendErrorMessage( playerid, "Nemate dozvolu za koristcenje ove komandi!");
   return 1;
}
[/pawn]
Komanda je ispravna i nema greske u njoj. Ali ako pogledamo proveru za admina i kako se vraca greska, treba nam da procitamo citavu komandu da shvatimo da ce se vratiti poruka ako igrac nije admin. Zato je uvek pametnije da se greska odmah vrati:
[pawn]
CMD:cc( playerid, params[], help) {
   if( PlayerInfo[ playerid ][ xAdmin ] >= 1 ) return SendErrorMessage( playerid, "Nemate dozvolu za koristcenje ove komandi!");
   
   for( new j; j < 110; j++ ) {
      SendClientMessageToAll( -1, "" );
   }
   SendClientMessageToAll(-1, ""blues"Clear Chat "pink"| "blues"Chat je ociscen od strane "pink"Admina.");
   SendClientMessageToAll(-1, ""pink"| "blues"www."pink"panama-roleplay"blues".info "pink"|");
   return 1;
}
[/pawn]
Komanda je ista, nista nije promenjeno, samo je greska odmah vracena cim se na nju naislo. Nema nepotrebnog dodatnog bloka, i kada saniramo sve na pocetku onda bez brige mozemo da radimo u kodu dalje posto smo sigurni da nemamo nemogucih situacija (recimo kada bi igrac pozvao admin komandu).

Kao jos jedan primer komande:
[pawn]
CMD:disarm( playerid, params[] ) {
    if( PlayerInfo[ playerid ][ xAdmin ] >= 3 ) {
        if( !AdminDuty[ playerid ] ) return SendErrorMessage( playerid, "Da bi koristili ovu komandu morate biti Admin na duznosti" );
      new id;
       if( sscanf( params, "u", id ) ) return SendUsageMessage( playerid, "/disarm [ ID/Deo Imena ]");

       if( id != INVALID_PLAYER_ID && PlayerLogged[ id ] != false) {

          SendInfoMessage( id, "Admin %s ti je oduzeo oruzje", ImeIgraca( playerid ) );
         SendInfoMessage( playerid, "Oduzeo si oruzje igracu %s", ImeIgraca( id ) );
         ResetAllWeapons( id );
      }
   }
   else return SendErrorMessage( playerid, "Niste u mogucnosti koristiti ovu komandu." );
   return 1;
}
[/pawn]

Zasto ne ovako?
[pawn]
CMD:disarm( playerid, params[] )
{
   if( PlayerInfo[ playerid ][ xAdmin ] >= 3 ) return SendErrorMessage( playerid, "Niste u mogucnosti koristiti ovu komandu." );
   if( !AdminDuty[ playerid ] ) return SendErrorMessage( playerid, "Da bi koristili ovu komandu morate biti Admin na duznosti" );

   new id;
   if( sscanf( params, "u", id ) ) return SendUsageMessage( playerid, "/disarm [ ID/Deo Imena ]");
   if( !IsPlayerConnected(playerid)) return SendErrorMessage(playerid, "Nepostojeci igrac" );
   if(!PlayerLogged[ id ]) return SendErrorMessage(playerid, "Igrac nije ulogovan" );

   ResetAllWeapons( id );
   SendInfoMessage( id, "Admin %s ti je oduzeo oruzje", ImeIgraca( playerid ) );
   SendInfoMessage( playerid, "Oduzeo si oruzje igracu %s", ImeIgraca( id ) );
   return 1;
}
[/pawn]
Odmah smo sanirali sve sto pre, i nakon toga znamo da nemozemo imati sranja. Ako radimo na ovaj nacin, odmah primetimo da smo u prvom nacinu zaboravili da vratimo gresku ukoliko igrac ne postoji ili nije ulogovan, a ovako odmah vidimo da imamo nesto prazno. I kod nam je mnogo lakse citati. Ako su ovo kratke komande, i mozemo da vidimo koja se greska vraca nakon if-a, u duzim komandama gde se if naredba zavrsava mozda moramo i da scrolamo da bi videli, a do tad nam moze i pobeci iz glave sta smo hteli da vidimo ili prosto da se izgubimo u blokovima pa da trazimo onaj koji nam treba. Zato se sanacija i validacija radi sto pre.






Kratki zakljucci

Ukoliko vidite da cesto ponavljate neke nardbe, onda je to verovatno posao za funkciju.
Ako mozete nesto logicki da odvojite, odvojite (nekad je dobra ideja da neke stvari idu i u poseban fajl ne samo posebnu funkciju), zato je i nastalo Objektno Orijentisano Programiranje.

Ukoliko imate promenive ili nesto sto se ponavlja, pa stavljate 1,2,3...To odmah znaci da je to posao za niz i/ili loop.

Koristite engleski i pratite konvencije koristene do vas (ako radite na postojecem projektu) da se ne mesaju stilovi, formati i slicno. Najbitnije je da kod konstantno ima isti izgled i stil radi lakseg citanja i odrzavanja.

Uvek validujte input, nemojte se brinuti hoce li to opteretiti CPU (nije citanje/pisanje fajlova). I validujte ga sto pre i sto pre vratite gresku.

Definisite stvari u raznim formatima jednom pa konvertujte ili pazite da oba formata imaju iste vrednosti (u samp-u najvise se odnosi na boje).
Poslednja Izmena: April 07, 2023, 18:10:30 POSLE PODNE od Paradox West

Nisam sve procitao,ne da mi se citati sve, ali prosao sam kroz text na preskoke i slazem se sa svima sto sam zapazio.
Vjerujem da ljudi ne bi pravili te greske da se "ne bave" samo sa pawn-om, vec da programiraju u nekog jeziku sa strane, te bi uceci osnove tog jezika naucili i o nizovima i slicno.
Ali kako je tako je, ovo je samp forum i pretezno ljude interesuje samp i zele da skriptaju bez da znaju ista o programiranju.

Sve je to odlicno samo vecina "nas" copy-paste compile nema error-a i haj guraj jbg
CitatNe idite uokolo tumačeći kako vam svijet duguje život. Svijet vam ne duguje ništa. On je bio prvi ovdje.



Citat: Deleted User poslato Maj 07, 2021, 14:02:51 POSLE PODNE
Ovo su vise saveti nego tutoriali, ali posto je najprikladniji forum za ovakvu temu onda i objavljujem ovde.
Napomenucu odmah, vecina koda za primere je iz Panama Roleplay, nema nikakvog razloga sto sam njega uzeo osim sto mi je prvi upao u oci kad sam otvorio downloads. Nazalost kod nas sve je copy/paste iz istih modova, i vecina gresaka se nadje svugde u svakom kodu.





Skracivanje imena funkcija i ostalih stvari

[pawn]
#define SPD                                                     ShowPlayerDialog
#define SCMA                                    SendClientMessageToAll
#define DSL                                        DIALOG_STYLE_LIST
#define IPI                                        INVALID_PLAYER_ID
#define DSI                                                   DIALOG_STYLE_INPUT
#define DSP                                                DIALOG_STYLE_PASSWORD
#define DSMSG                                      DIALOG_STYLE_MSGBOX
[/pawn]

To je pogresno, ime funkcije treba da sto preciznije i krace opise njenu funkciju to jest ono sto ona radi. Ako je SendClientMessage onda odmah znas da je to slanje poruke clientu (igracu u nasem slucaju). I nije bitno u kojim jezicima i sta si radio, znaces sustinu funkcije. Ukoliko vam je tesko da kucate, koristite neki IDE koji podrzava autocomplete. Za sa-mp server bih preporucio Visual Studio Code ili Sublime (po meni Visual Studio Code ja lakse da se podesi i ima bolji intelisense - to jest bolje uocava greske i upozorava na nih).

Primer iz Sublime text editora:

Kao sto vidite kucanjem SCM se svejedno moze dobiti puno ime funkcije i nema potrebe za skracivanjem, sve sto treba da uradite je da stisnete space ili tab nakon sto naidjete na odgovarajucu preporuku od intelisense. A takva navika ce vam pomoci ubuduce kada budete prelazili na nove jezike i u nova okruzenja.





Definicije boja

Definicije boja (mada i ostalih stvari), gledao sam da dosta ljudi definise neke stvari vise puta pod razlicitim imenima, i da mnogo puta te definicije su bezpotrebne.
[pawn]
#define CRVENA                                                        0xfa5555AA
#define ZELENA                                                          0x33AA33AA
#define BELA                                                          0xFFFFFFFF
#define CRNA                                             0x000000FF
#define PLAVA                                                         0xA9C4E4FF
#define NARANDZASTA1                                            0xFF9933AA
#define SIVA                                                          0xAFAFAFAA
#define ZUTA                                                          0xFFFF00AA
#define Helper                                              0x00FF00FF
#define NARANDZASTA                                                   0xFFAF00FF
#define LJUBICASTA                                            0xC2A2DAAA
#define SVETLOPLAVA                                            0x33CCFFAA
#define SVETLOCRVENA                                        0xFF6347AA
#define PANAMA                                                        0x2e9cd1FF
#define ANTICHEAT                                          0xED6412FF
#define NARACRVENA                                                      0xFF4500AA
#define WARLJUB                                            0x8B008BAA

#define pink                              "{eb2fd5}"
#define green                             "{3bab16}"
#define white                             "{ffffff}"
#define red                                 "{e82323}"
#define blues                               "{24c1ff}"
#define yellows1                            "{e8e52c}"
#define yellows2                          "{edd351}"
#define oranges1                            "{ff9430}"
#define oranges2                            "{fc681e}"
#define greens1                               "{61ba6e}"
#define greens2                             "{318739}"
#define reds                              "{c73a3a}"
#define yellow                            "{e8e52c}"
[/pawn]
Ovo je jedan od primera kako najgore uraditi takve stvari.

  • Imena boja su pola na srpskom, pola na engleskom.

  • Neka imena boja su definisana kao mnozina.

  • Neke boje su definisano samo za jedan format.

  • RGBA i SA-MP embeded boje se ne poklapaju po RGB.

Imena i nazive stvari uvek pokusavajte da nazivate na engleskom. Samo nihove vizuelne reprezentacije stavljajte na srpskom ili lokalizujte (u slucaju sa vise jezika).

Uvek pokusavajte da definisite stvari kako jesu, i da pri koristenju zvuce logicno. Ako napravite niz new Houses[50];, i onda pristupate nizu Houses[id] to je potpuno pravilno, ali kada citate zvuci kao da to cemu ste pristupili je u stvari niz (a ne samo elemenat niza), i da ima jos makar jednu dimenziju. Zato je pametnije da definisete kao new House[50];.

Ukoliko imate stvari koje se predstavljaju u razlicitim formatima ili oblicima, pokusajte da ih definisete samo jednom pa da ih konvertujete, ili da ih obavezno definisite u svim formatima/oblicima. Mozda vam trenutno treba samo u jednom obliku, ali sutra kada budete radili moze vam zatrebati i moze da vas zbuni zasto neceg nema u drugom obliku, da li ima razlog za to ili ne.

Kao sto sam gore vec rekao, probajte da definisete samo jednom, ako ne onda u oba formata, ali tako da bude isto. Ako SendClientFunkcija ocekuje RGBA boju kao drugi argument, a takodje moze da koristi embeded rgb format u trecem argumentu, onda definisite boje da budu iste.
[pawn]
#define CRVENA 0xfa5555AA
#define red "{e82323}"
[/pawn]
To ne samo da su dve boje pod razlicitim nazivom (jezikom), u razlicitom formatu, vec su i dve razlicite boje
Ako uzmemo 0xfa5555AA kao osnovnu i pravilnu boju onda bi i embeded trebala da bude "{FA5555}"
To jest crvena boja pravilno definisana u RGBA i SA-MP Embeded formatu ( {RGB} } ) bi bilo nesto kao:
[pawn]
#define COLOR_RED     0xFA5555AA
#define EMBEDED_RED   "{FA5555}"
[/pawn]
Sad da li ce te vi imati prefix color_ da li on biti vekilim ili malim slovom, to je sve stvar licnog misljenja. Ali to da bude na engleskom, da u oba formata bude ista boja to je ono sto je pravilno (i logicno na kraju krajeva).





Funkcije i njihova upotreba

Zapamtite jednu stvar, funkcije su vasi najbolji prijatelji u programiranju. Svako odvajanje logike je uvek dobra stvar (dokle god se ne pretera kao i sa alkoholom :D )

Ukolio vidite da ponajvlate neke stvari cesto, onda je to verovatno posao za funkciju. Neki od primera bi bilo da ako cuvate logove, pa svaki put kad treba da cuvate, koristite fopen, format, fwrite, fclose...Ako to napisete 2x, to je ok, ako vam to zatreba i treci put, dobra ideja je da se napravi funkcija LogInFile/SaveLog (ili kako god vi mislite da je pametno da nazovete vasu funkciju).

U pawn ima naredba stock koja prigusuje unused (neiskoristeno) upozorenje za neku definiciju (promenive, funkcije i slicno). Jako je korisna za includove i module kada hocete da definisete nesto sto se mozda nece koristiti ali opet nekome moze biti korisna i on ce je iskoristiti, onda se funkcije definisu sa stock naredbom. Ali ne i funkcije u gamemodu! Ako ste napravili neku funkciju u skripti a ne koristite je onda je lakse da je komentarisete. Mada to obicno kaze da ste nesto zaboravili. Zato nikad ne definiste stvari sa stock osim ako tacno znate da to tako treba, i da je to za ubuduce. Opet, lakse je da se komentarise, pa kada se pozove a funkcija ne postoji (komentarisana je), najde se u skripti i opet se prodje okom da se proveri da li je sve tacno i kako treba da bude. Izuzetak ovome su modularne skripte gde svaki modul treba da ima funkcije preko kojih ce mod komunicirati sa tim modulom. I funkcije treba da se prave za sve sto se moze koristiti (nemora da znaci da ce biti iskoristeno odmah ili ikad). Kako se ovo moze objasniti? Lako, moduli i jesu includovi - tacka.

Nemojte sve da trpate u jednu funkciju, odvajajte logicki sve sto se moze odvojiti a da ima smisla.
Recimo ovakva komanda (koja je u stvari public funkcija):
[pawn]
CMD:altchat(playerid)
{
   if( PlayerInfo[ playerid ][ xAdmin ] < 1 ) return SendErrorMessage(playerid, "Niste ovlasceni.");
   if( ALTPoruke[ playerid ] )
   {
      for (new i = 0; i < MAX_LINES; i ++)
      {
         strmid(AltChatTD_Text[playerid], " ", 0, 1);
      }
      for (new i = 0; i < MAX_LINES; i ++)
      {
         PlayerTextDrawSetString(playerid, AltChatTD_Player, AltChatTD_Text[playerid]);
         PlayerTextDrawShow(playerid,AltChatTD_Player);
      }
      ALTPoruke[ playerid ] = false;
      SendInfoMessage(playerid, "Ugasili ste alternativni cet!");
   }
   else if( !ALTPoruke[ playerid ] )
   {
      ALTPoruke[ playerid ] = true;
      SendInfoMessage(playerid, "Upalili ste alternativni cet!");
   }
   return 1;
}
[/pawn]
Logicnije je da se prikazivanje i sakrivanje textdrawvova vrsi uz pomoc funkcije. Tako da ako hocemo da sakrijemo chat privremeno (recimo na primer koristimo kameru necemo da nam TD-ovi smetaju) to mozemo a da ne ponavljamo sami sebe. Pomerajuci loop u funkciju mozemo kasnije da pozivamo samo funkciju.
[pawn]
CMD:altchat(playerid)
{
   if( PlayerInfo[ playerid ][ xAdmin ] < 1 ) return SendErrorMessage(playerid, "Niste ovlasceni.");
   if(!ShowPlayerAltChat(playerid)) return SendErrorMessage(playerid, "Error pri prikazivanju poruka");

   ALTPoruke[ playerid ] = !ALTPoruke[ playerid ];

   if(ALTPoruke[ playerid ] )
      ShowPlayerAltChat(playerid);
   else
      HidePlayerAltChat(playerid);

   SendInfoMessage(playerid, "%s ste alternativni cet!", ALTPoruke[ playerid ] ? ("Upalili") : ("Ugasili"));
   return 1;
}

ShowPlayerAltChat(playerid)
{
   if(!IsPlayerConnected(playerid)) return 0;
   for (new i = 0; i < MAX_LINES; i ++)
   {
      strmid(AltChatTD_Text[playerid], " ", 0, 1);
      PlayerTextDrawSetString(playerid, AltChatTD_Player, AltChatTD_Text[playerid]);
      PlayerTextDrawShow(playerid,AltChatTD_Player);
   }
   return 1;
}

HidePlayerAltChat(playerid)
{
   if(!IsPlayerConnected(playerid)) return 0;
   for (new i = 0; i < MAX_LINES; i ++)
   {
      PlayerTextDrawHide(playerid,AltChatTD_Player);
   }
   return 1;
}
[/pawn]

Takodje je dobra praksa da se svaki input (unos) sanira/validuje sto je pre moguce (vise o tome ispod).





Nizovi i pelje (loop-ovi)

[pawn]
enum oOrgInfo {
   oID,
   oName[ ORG_IME ],
   oPreFix[ 10 ],
   oColor[ 24 ],

   oTip,
   oMaxClanova,
   oUbacenihClanova,

    oSkin1,
   oSkin2,
   oSkin3,
   oSkin4,
   oSkin5,
   oSkin6,
   
   oZSkin1,
   oZSkin2,
   oZSkin3,
   oZSkin4,
   oZSkin5,
   oZSkin6,

    oLider1[ ORG_LIDER ],
    oLider2[ ORG_LIDER ],

   oClan1[ ORG_CLAN ],
   oClan2[ ORG_CLAN ],
   oClan3[ ORG_CLAN ],
   oClan4[ ORG_CLAN ],
   oClan5[ ORG_CLAN ],
   oClan6[ ORG_CLAN ],
   oClan7[ ORG_CLAN ],
   oClan8[ ORG_CLAN ],
   oClan9[ ORG_CLAN ],
   oClan10[ ORG_CLAN ],
   oClan11[ ORG_CLAN ],
   oClan12[ ORG_CLAN ],
   oClan13[ ORG_CLAN ],
   oClan14[ ORG_CLAN ],
   oClan15[ ORG_CLAN ],
   oClan16[ ORG_CLAN ],
   oClan17[ ORG_CLAN ],
   oClan18[ ORG_CLAN ],
   oClan19[ ORG_CLAN ],
   oClan20[ ORG_CLAN ],
   oClan21[ ORG_CLAN ],
   oClan22[ ORG_CLAN ],
   oClan23[ ORG_CLAN ],
   oClan24[ ORG_CLAN ],
   oClan25[ ORG_CLAN ],
   oClan26[ ORG_CLAN ],
   oClan27[ ORG_CLAN ],
   oClan28[ ORG_CLAN ],
   oClan29[ ORG_CLAN ],
   oClan30[ ORG_CLAN ],

   oRank1[ ORG_RANK ],
   oRank2[ ORG_RANK ],
   oRank3[ ORG_RANK ],
   oRank4[ ORG_RANK ],
   oRank5[ ORG_RANK ],
   oRank6[ ORG_RANK ],

   Float:oPozExtX,
   Float:oPozExtY,
   Float:oPozExtZ,

   Float:oPozIntX,
   Float:oPozIntY,
   Float:oPozIntZ,

   Float:oDutyPoint[ 3 ],
   oDutyInt,
   oDutyVW,
   Float:oEquipPoint[ 3 ],
   oEquipInt,
   oEquipVW,

    oMaxPort,
   oControlType,
   Float:oDrugField[ 3 ],
   Float:oSafePos[ 3 ],
   oSafeMoney,
   oSafeDrug[ 4 ],
   oSafeDrugCode[ 4 ],

   oInt,
   oVw
}
[/pawn]
Ako pogledate malo taj enum primeticete oClan1 do oClan30 (pored slicnih stvari koje se ponavljaju). Zlatno pravilo je da ako imate nesto sto se ponavlja, onda se to moze i definisati kao niz i provuci kroz petlju. Nemojte sebe da ponavljate, nemate potrebe za time, i olaksacete sebi zivot koristeci nizove i petlje. Da, mozda je za sitnicu lakse tako, ali sutra ce to trebati da se prosiri i umesto 1 stvari da izmenite moracete da izmenite 50, plus vam niko nece garantovati da niste negde zaboravili da dodate/izmenite nesto.

Umesto toga da sve trpate u enum, da ponavljate, i da se mucite zar nije lakse da se definise:
[pawn]new oClan[MAX_ORGANISATIONS][MAX_ORG_MEMBERS][MAX_PLAYER_NAME];[/pawn]
Primetite MAX_ORGANISATIONS umesto broja, MAX_ORG_MEMBERS umesto ORG_CLAN, da se ne bi pitali kasnije da li je Klan ili %u010Clan. MAX_PLAYER_NAME, ukoliko u nekoj od sledecih verzija odjednom povecaju/skrate maksimalnu duzinu nika

Da umesto u komandama/funkcijama imate ovo:
[pawn]
      if( strcmp( imeigraca, OI[ OrgID ][ oClan1 ], true ) == 0) { strmid( OI[ OrgID ][ oClan1], "Niko", 0, strlen( "Niko" ), ORG_CLAN); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan2 ], true ) == 0) { strmid( OI[ OrgID ][ oClan2 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan3 ], true ) == 0) { strmid( OI[ OrgID ][ oClan3 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan4 ], true ) == 0) { strmid( OI[ OrgID ][ oClan4 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan5 ], true ) == 0) { strmid( OI[ OrgID ][ oClan5 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan6 ], true ) == 0) { strmid( OI[ OrgID ][ oClan6 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan7 ], true ) == 0) { strmid( OI[ OrgID ][ oClan7 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan8 ], true ) == 0) { strmid( OI[ OrgID ][ oClan8 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan9 ], true ) == 0) { strmid( OI[ OrgID ][ oClan9 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan10 ], true ) == 0) { strmid( OI[ OrgID ][ oClan10 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan11 ], true ) == 0) { strmid( OI[ OrgID ][ oClan11 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan12 ], true ) == 0) { strmid( OI[ OrgID ][ oClan12 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan13 ], true ) == 0) { strmid( OI[ OrgID ][ oClan13 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan14 ], true ) == 0) { strmid( OI[ OrgID ][ oClan14 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan15 ], true ) == 0) { strmid( OI[ OrgID ][ oClan15 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan16 ], true ) == 0) { strmid( OI[ OrgID ][ oClan16 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan17 ], true ) == 0) { strmid( OI[ OrgID ][ oClan17 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan18 ], true ) == 0) { strmid( OI[ OrgID ][ oClan18 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan19 ], true ) == 0) { strmid( OI[ OrgID ][ oClan19 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan20 ], true ) == 0) { strmid( OI[ OrgID ][ oClan20 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan21 ], true ) == 0) { strmid( OI[ OrgID ][ oClan21 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan22 ], true ) == 0) { strmid( OI[ OrgID ][ oClan22 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan23 ], true ) == 0) { strmid( OI[ OrgID ][ oClan23 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan24 ], true ) == 0) { strmid( OI[ OrgID ][ oClan24 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan25 ], true ) == 0) { strmid( OI[ OrgID ][ oClan25 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan26 ], true ) == 0) { strmid( OI[ OrgID ][ oClan26 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan27 ], true ) == 0) { strmid( OI[ OrgID ][ oClan27 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan28 ], true ) == 0) { strmid( OI[ OrgID ][ oClan28 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan29 ], true ) == 0) { strmid( OI[ OrgID ][ oClan29 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else if( strcmp( imeigraca, OI[ OrgID ][ oClan30 ], true ) == 0) { strmid( OI[ OrgID ][ oClan30 ], "Niko", 0, strlen( "Niko" ), ORG_CLAN ); }
      else return SendErrorMessage( playerid, "Taj igrac nije u izabranoj organizaciji!" );
[/pawn]
Zar nije lakse da napravite funkciju
[pawn]
      RemoveMemberFromOrganisation(orgid, memberName[])
      {
         if( MAX_ORGANISATIONS <= orgid < 0) return -2;
         for(new i = 0; i < MAX_ORG_MEMBERS); i++)
         {
            if( strcmp( imeigraca, oClan[orgid], true ) == 0)
            {
               strmid( oClan[ orgid ][ i ], "Niko", 0, strlen( "Niko" ), MAX_ORG_MEMBERS );
            }
            return i;
         }
         return -1;
      }

[/pawn]
I onda je samo pozovete?
[pawn]
      new memberid = RemoveMemberFromOrganisation(OrgID, imeigraca)
                if(memberid == -2) return SendErrorMessage( playerid, "Nepostojaca organizacija!" );
      if(memberid == -1) return SendErrorMessage( playerid, "Taj igrac nije u izabranoj organizaciji!" );
[/pawn]

Sada imate funkciju i ako vam ikada zatreba da maknete igraca iz organizacije samo je pozovete. Ako se pitate gde i kako vam to moze zatrebati osim u komandi ne treba dugo da razmisljate, ako recimo igrac dobije ban. Banovani igraci ne trebaju da zauzimaju slotove u organizacijama, po meni je to ligicno.

Pazite, imaju razlicite konvencije naziva stvari. Neko voli da sve naziva isto, neko voli da za promenive koristi jedno, za funkcije drugo i slicno. Po meni je to sve pitanje licnog misljenja i kako ko voli. Mislim da je sve ispravno dokle god se prati postojeca konvencija. Recimo sa-mp, funkcije su nazvane u CamelCase formatu, to jest SendClientMessage, PlayerTextDrawSetString i tako dalje (Mesali su i oni pomalo, pogotovo redosled i logiku ali OpenMP radi na tome da to ispravi). Zato i ja pratim CamelCase za funkcije, kao sto sam za primer uzeo
RemoveMemberFromOrganisation. Ime funkcije tacno opisuje sta radi, poklapa se sa nacinom kako su definisane nativne sa-mp funkcije, prvo sanira input, vraca gresku i tek onda radi ono sto treba (O validaciji i sanaciji inputa ispod).





Sanacija i validacija inputa (unosa/ulaza)

Pre nego sto pocnem, da naglasim nesto. Sanacija i validacija se vecinom preklapaju i mesaju posto rade jako slicne stvari i obicno se rade zajedno. Ako neko kaze sanacija za validaciju (ili obrnutu) nemojte odmah da ga napadate posto covek nije rekao nista pogresno, samo je iskoristio drugaciji termin. Iako se tehnicki oni razlikuju, u praksi se sretnu oba izraza za obije radnje, i niko normalan nece praviti problem oko toga posto vecinom rade istu stvar.

A za one koji bi samo hteli da znaju
Sanacija je kada ispravljate input (ili neke druge stvari), ali se i vracanje greske nakon validacije moze reci da je sanacija. Posto u sustini posao saniranja inputa je da ne dozvolimo invalid vrednosti dalje u kodu.
Validacija je kada proveravate da li je input ocekivan, u formatu i obliku kojem treba da bude (i mozda vracamo gresku). Ukoliko se taj input ispravlja to je vec sanacija (nakon validacije).

Recimo kada ocekujete integer kao argument komande, a igrac unese float, mi ga validujemo, vidimo da je invalid i vracamo gresku, ili saniramo to tako sto pretvaramo float u integer (ukoliko je to moguce i ispunjava nase uslove ako ih imamo).

Zlatno pravilo je da svaka funkcija za sebe validuje i sanira input (argumente), pogotovo onaj od strane korisnika (i administratora - oni su takodje korisnici). Ako recimo imate funkciju koja ocekuje id igraca, a taj id igraca se koristi za pristup nizu, unos nepostojeceg id-a moze dovesti do overflow-a.
Recimo za primer imamo ovakav uprosteni slucaj:
[pawn]
new pAdminLevel[MAX_PLAYERS];
SerPlayerAdminLevel(playerid, level)
{
    pAdminLevel[playerid] = level;
}
[/pawn]

Niz nam je za MAX_PLAYERS (recimo 1000 koliko je najvise u samp-u).
Za primer ako ne saniramo input u komandi a pozovemo nasu funkciju, i recimo sscanf (ili strval ili nesto trece) nam vrati INVALID_PLAYER_ID (65535), ili cellmin/cellmax, funkcija ne proverava playerid da li je validan vec direktno pristupa nizu. Tu odmah imamo memory overflow (sto ce verovatno izazvati crash). Zato se input uvek treba sanirati, i sanirati sto pre.

[pawn]
SerPlayerAdminLevel(playerid, level)
{
    if(!IsPlayerConnected(playerid)) return 0;
    if(MAX_LEVEL < level < 0) return 0;
    pAdminLevel[playerid] = level;
    return 1;
}[/pawn]
Posto IsPlayerConnected funkcija proverava da li je igrac konektovan, a nepostojeci id (manji od 0 ili vec od broja slotova) nikad nemoze biti konektovan, onda ce ona da sanira playerid. Nakon sto smo to sanirali mozemo da saniramo level, s jedne strane je nepotrebno, ali sa druge stvari ako se taj level prosledi nekom nizu negde drugo, moze doci do overflow, zato se uvek sanira (pa cak i ako se ista stvar proverava u razlicitim funkcijama koje pozivaju jedna drugu). Ovakve sitne provere vam nikad nece usporiti server, makar ne da vi to osetite, i smatraju se dobrom praksom u programiranju.

Sanaciju inputa najvise mozemo da vidimo u komandama, i tu je ona najbitnija posto vecinom komande pozivaju igraci i prosledjuju svoj input (sto otvara vrata za svakakve bagove, greske i sigurnostne rizike).
Input se sanira sto pre je to moguce, i dobra praksa je da se odmah vraca greska cim se na nju naidje (i to je zdrav smisao da cim vidis da nesto nemozes, stanes i kazes nemoze to tako a ne da zavrsis sve pogresno i kazes nisam mogao to tako).
Recimo za primer ova komanda:
[pawn]
CMD:cc( playerid, params[], help) {
   if( PlayerInfo[ playerid ][ xAdmin ] >= 1 ) {
      for( new j; j < 110; j++ ) {
         SendClientMessageToAll( -1, "" );
      }
      SendClientMessageToAll(-1, ""blues"Clear Chat "pink"| "blues"Chat je ociscen od strane "pink"Admina.");
      SendClientMessageToAll(-1, ""pink"| "blues"www."pink"panama-roleplay"blues".info "pink"|");
   }
   else return SendErrorMessage( playerid, "Nemate dozvolu za koristcenje ove komandi!");
   return 1;
}
[/pawn]
Komanda je ispravna i nema greske u njoj. Ali ako pogledamo proveru za admina i kako se vraca greska, treba nam da procitamo citavu komandu da shvatimo da ce se vratiti poruka ako igrac nije admin. Zato je uvek pametnije da se greska odmah vrati:
[pawn]
CMD:cc( playerid, params[], help) {
   if( PlayerInfo[ playerid ][ xAdmin ] >= 1 ) return SendErrorMessage( playerid, "Nemate dozvolu za koristcenje ove komandi!");
   
   for( new j; j < 110; j++ ) {
      SendClientMessageToAll( -1, "" );
   }
   SendClientMessageToAll(-1, ""blues"Clear Chat "pink"| "blues"Chat je ociscen od strane "pink"Admina.");
   SendClientMessageToAll(-1, ""pink"| "blues"www."pink"panama-roleplay"blues".info "pink"|");
   return 1;
}
[/pawn]
Komanda je ista, nista nije promenjeno, samo je greska odmah vracena cim se na nju naislo. Nema nepotrebnog dodatnog bloka, i kada saniramo sve na pocetku onda bez brige mozemo da radimo u kodu dalje posto smo sigurni da nemamo nemogucih situacija (recimo kada bi igrac pozvao admin komandu).

Kao jos jedan primer komande:
[pawn]
CMD:disarm( playerid, params[] ) {
    if( PlayerInfo[ playerid ][ xAdmin ] >= 3 ) {
        if( !AdminDuty[ playerid ] ) return SendErrorMessage( playerid, "Da bi koristili ovu komandu morate biti Admin na duznosti" );
      new id;
       if( sscanf( params, "u", id ) ) return SendUsageMessage( playerid, "/disarm [ ID/Deo Imena ]");

       if( id != INVALID_PLAYER_ID && PlayerLogged[ id ] != false) {

          SendInfoMessage( id, "Admin %s ti je oduzeo oruzje", ImeIgraca( playerid ) );
         SendInfoMessage( playerid, "Oduzeo si oruzje igracu %s", ImeIgraca( id ) );
         ResetAllWeapons( id );
      }
   }
   else return SendErrorMessage( playerid, "Niste u mogucnosti koristiti ovu komandu." );
   return 1;
}
[/pawn]

Zasto ne ovako?
[pawn]
CMD:disarm( playerid, params[] )
{
   if( PlayerInfo[ playerid ][ xAdmin ] >= 3 ) return SendErrorMessage( playerid, "Niste u mogucnosti koristiti ovu komandu." );
   if( !AdminDuty[ playerid ] ) return SendErrorMessage( playerid, "Da bi koristili ovu komandu morate biti Admin na duznosti" );

   new id;
   if( sscanf( params, "u", id ) ) return SendUsageMessage( playerid, "/disarm [ ID/Deo Imena ]");
   if( !IsPlayerConnected(playerid)) return SendErrorMessage(playerid, "Nepostojeci igrac" );
   if(!PlayerLogged[ id ]) return SendErrorMessage(playerid, "Igrac nije ulogovan" );

   ResetAllWeapons( id );
   SendInfoMessage( id, "Admin %s ti je oduzeo oruzje", ImeIgraca( playerid ) );
   SendInfoMessage( playerid, "Oduzeo si oruzje igracu %s", ImeIgraca( id ) );
   return 1;
}
[/pawn]
Odmah smo sanirali sve sto pre, i nakon toga znamo da nemozemo imati sranja. Ako radimo na ovaj nacin, odmah primetimo da smo u prvom nacinu zaboravili da vratimo gresku ukoliko igrac ne postoji ili nije ulogovan, a ovako odmah vidimo da imamo nesto prazno. I kod nam je mnogo lakse citati. Ako su ovo kratke komande, i mozemo da vidimo koja se greska vraca nakon if-a, u duzim komandama gde se if naredba zavrsava mozda moramo i da scrolamo da bi videli, a do tad nam moze i pobeci iz glave sta smo hteli da vidimo ili prosto da se izgubimo u blokovima pa da trazimo onaj koji nam treba. Zato se sanacija i validacija radi sto pre.






Kratki zakljucci

Ukoliko vidite da cesto ponavljate neke nardbe, onda je to verovatno posao za funkciju.
Ako mozete nesto logicki da odvojite, odvojite (nekad je dobra ideja da neke stvari idu i u poseban fajl ne samo posebnu funkciju), zato je i nastalo Objektno Orijentisano Programiranje.

Ukoliko imate promenive ili nesto sto se ponavlja, pa stavljate 1,2,3...To odmah znaci da je to posao za niz i/ili loop.

Koristite engleski i pratite konvencije koristene do vas (ako radite na postojecem projektu) da se ne mesaju stilovi, formati i slicno. Najbitnije je da kod konstantno ima isti izgled i stil radi lakseg citanja i odrzavanja.

Uvek validujte input, nemojte se brinuti hoce li to opteretiti CPU (nije citanje/pisanje fajlova). I validujte ga sto pre i sto pre vratite gresku.

Definisite stvari u raznim formatima jednom pa konvertujte ili pazite da oba formata imaju iste vrednosti (u samp-u najvise se odnosi na boje).
Lepo
Sunce greje lagano šetam se po Voždovcu
Moja kučka i ja, furam je na povodcu
Zove se Lola i nije nešto lepa
Ali meni daće lapo kada dokopa se repa
Rođena na ulici, dolazi iz geta
I zato je gangsta kučka, a ne starleta
Zahvaljujući njoj meni dignuta je đoka
Krov je spušten, vožnjica od bloka do bloka
I ako glumiš mangupa jebaće ti mamicu
U tašni nema lak za nokte, ali ima palicu
Odrasla na Medaku, iz huda je sestra
Nijedna Snoopova kučka nije tol'ko gangsta
Za nju sam 'Pac, ona moja je Madonna
Ako znaš još neku takvu javi mi preko fona
I kada njesra izbije ona ne vata pištolja
Već nabada na prvu jer je gangsta drolja

Citat: stevich poslato Maj 14, 2021, 20:12:16 POSLE PODNE
Lepo
šta citiraš komplet post jebote idi na kraj teme i imaš box za brzi odgovor

sry
Sunce greje lagano šetam se po Voždovcu
Moja kučka i ja, furam je na povodcu
Zove se Lola i nije nešto lepa
Ali meni daće lapo kada dokopa se repa
Rođena na ulici, dolazi iz geta
I zato je gangsta kučka, a ne starleta
Zahvaljujući njoj meni dignuta je đoka
Krov je spušten, vožnjica od bloka do bloka
I ako glumiš mangupa jebaće ti mamicu
U tašni nema lak za nokte, ali ima palicu
Odrasla na Medaku, iz huda je sestra
Nijedna Snoopova kučka nije tol'ko gangsta
Za nju sam 'Pac, ona moja je Madonna
Ako znaš još neku takvu javi mi preko fona
I kada njesra izbije ona ne vata pištolja
Već nabada na prvu jer je gangsta drolja

Dosta opsirno, i verujem da ce da pomogne mnogima  :D