MAGNET 5 Napisano 31 Sierpnia 2018 Czasami zdarza się, że nie mamy pojęcia z jakimi ilościami elementów będziemy mieli do czynienia (np. ile klas w CodMod'ie musimy załadować do pamięci). I owszem, możemy wówczas sprowadzić to do prostego ograniczenia, np.: #define MAX_ILOSC_KLAS 100 int ClassID[MAX_ILOSC_KLAS]; //... Niestety, rodzi to dwa zasadnicze problemy: 1. Jeśli aktualnie nie wykorzystujemy całej zaalokowanej pamięci, marnujemy bez potrzeby miejsce 2. Zawsze jesteśmy ograniczeni przez limit, który sami na siebie nałożyliśmy Rozwiązaniem naszego problemu są tablice dynamiczneHej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami! Mimo, że działają one nieco wolniej od zwykłych tablic, pozwalają na całkowicie dynamiczną alokację pamięci - oznacza to, że w trakcie trwania programu możemy dowolnie manipulować ilością zarezerwowanej pamięci - zarówno alokując, jak i zwalniając zasoby Korzystanie z nich jest naprawdę proste i wymaga jedynie odrobinę wprawy Na samym początku tworzymy uchwyt (najczęściej globalnie): ArrayList MojUchwyt Do poprawnego działania konieczne jest jeszcze jego zainicjowanie - bez tego niemożliwe jest jego działanie, a serwer będzie sypał errorami. Najczęściej piszemy w OnPluginStart: MojUchwyt = CreateArray(block_size, init_size) Teraz omówię parametry block_size oraz init_size, posługując się strukturą tablicy dynamicznej. Możemy sobie ją przyrównać do takiej oto szafeczki: block_size mówi nam ile komórek zarezerwowanych jest dla jednego poziomu. Wartość raz tam podana pozostanie niezmienna dla danej tablicy (chyba, że użyjemy ResizeArrayHej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami!). Na naszym przykładzie dla każdego poziomu zarezerwowane zostaną 3 komórki. init_size określa ile poziomów zostanie zaalokowane na samym początku. Jego wartość ulega zmianie na przestrzeni trwania programu. Nasza szafeczka ma na start zaalokowane 12 poziomów, jednak w praktyce tego parametru najczęścieć nie stosuje się wogóle - wówczas na starcie nie ma żadnego poziomu i zostaną one dodane dopiero później. Jaką wartość podawać w block_size? Jeżeli wiemy, że w danej tablicy umieszczone zostaną jakieś ciągi znaków, które de facto wymagają więcej niż jednej komórki, wówczas podajemy tam wartość, która pozwoli na zapewnienie odpowiedniej ilości miejsca. Przykładowa inicjalizacja tablicy na imiona graczy może wyglądać na przykład tak: MojUchwyt = CreateArray(MAX_NAME_LENGTH+1) Z wykorzystaniem methodmap: MojUchwyt = new ArrayList(MAX_NAME_LENGTH+1); Polecenie zainicjuje pustą tablicę, która będzie posiadała na każdym poziomie ilość komórek równą wartość stałej MAX_NAME_LENGTH + 1 Niedobrze jest ograniczać się do konkretnych ilości danych (np. klas), jednak już dobrą praktyką jest ograniczanie dopuszczalnej ilości znaków, jak w przypadku limitu w nazwie gracza, zdefiniowanego przez podaną wyżej stałą. Gdyby zdarzyło się, że przekroczymy te ograniczenie, nadmiarowe znaki zostają usuwane... Skoro znamy już podstawy, możemy zapoznać się z procedurą zarządzania tablicą dynamiczną: 1/4. Dodawanie Każdorazowe dodanie elementu powoduje utworzenie się na samej górze nowego poziomu (patrz: przykład z półką) z zaalokowanymi komórkami w ilości block_size. Możemy tam wrzucić pojedyńczą komórkę, ciąg znaków, bądź tablicę. Przykład: Spoiler Klasycznie: int liczba = 5; char text = "moj tekst"; int tablica[] = {1, 2, 3}; PushArrayCell(MojUchwyt, liczba); PushArrayString(MojUchwyt, text); PushArrayArray(MojUchwyt, tablica, sizeof(tablica)); Methodmap: MojUchwyt.Push(liczba); MojUchwyt.PushString(text); MojUchwyt.PushArray(tablica, sizeof(tablica)); 2/4. Modyfikowanie Każdy poziom możemy dowolnie edytować. Musimy jedynie wiedzieć na jakiej wysokości się on znajduje. Ważne jest, że poziomy numerujemy od zera! (dół - zero). Przykład: Spoiler Klasycznie: char inny_text[] = "moj nowy tekst"; int inna_tablica[] {4, 5, 6}; SetArrayCell(MojUchwyt, 0, 3); SetArrayString(MojUchwyt, 1, inny_text); SetArrayArray(MojUchwyt, 2, inna_tablica, sizeof(inna_tablica)); Methodmap: MojUchwyt.Set(0, 3); MojUchwyt.SetString(1, inny_text); MojUchwyt.SetArray(2, inna_tablica, sizeof(inna_tablica)); 3/4. Pobieranie Jeżeli pobieramy komórkę ( GetArrayCellHej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami! ), zostanie ona zwrócona przez return. W przypadku stringa i tablicy, wartości zostaną skopiowane do podanych przez nas lokalizacji. Przykład: Spoiler Klasycznie: char pusta_tablica[32]; int druga_tablica[3]; int wartosc = GetArrayCell(MojUchwyt, 0); GetArrayString(MojUchwyt, 1, pusta_tablica, sizeof(pusta_tablica)); GetArrayArray(MojUchwyt, 2, druga_tablica, sizeof(druga_tablica)); Methodmap: int wartosc = MojUchwyt.Get(0); MojUchwyt.GetString(1, pusta_tablica, sizeof(pusta_tablica)); MojUchwyt.GetArray(2, druga_tablica, sizeof(druga_tablica)); 4/4. Usuwanie Nic prostszego - wystarczy index poziomu. Po usunięciu wszystkie wyższe poziomy spadają o jeden w dół (troche jak ściąganie obrusu z zastawionego stołu ?) Spoiler Klasycznie: RemoveFromArray(MojUchwyt, 0); // w naszym przykladzie usunie integera Methodmap: MojUchwyt.Erase(0); 5/4. Reszta SourceMod posiada dużo więcej funkcji obsługujących tablicy dynamiczne. Zapraszam tutaj: https://go-code.pl/dokumentacja-sourcemod/adt_array/Hej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami! Przykład Poniższy kod dodaje do tablic dynamicznych nazwę i UserIdHej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami! każdego gracza, który wejdzie na serwer. Ponadto, komenda !show pozwala na wyświetlenie zawartości owych tablic, a po wybraniu gracza z menu plugin wypisuje jego status - czy gracz jest połączony, czy też nie (lub wykonał reconnect): Spoiler #pragma semicolon 1 #include <sourcemod> #define NOT_CONNECTED 0 public Plugin myinfo = { name = "Dynamic arrays example", author = "MAGNET | YouTube: Koduj z Magnetem", description = "Krotki przyklad zastosowania tablicy dynamicznej", version = "0.9", url = "http://go-code.pl/" }; // tworzymy uchwyty na tablice ArrayList PlayerName; ArrayList PlayerUserID; public OnPluginStart() { RegConsoleCmd("sm_show", ShowPlayerMenu); // inicjalizacja PlayerName = new ArrayList(MAX_NAME_LENGTH+1); PlayerUserID = new ArrayList(); } // przy laczeniu sie gracza pobieramy jego imie i wpychamy do tablic wraz z UserId public void OnClientAuthorized(int client, const char[] auth) { char NameBuffer[MAX_NAME_LENGTH]; GetClientName(client, NameBuffer, sizeof(NameBuffer)); PlayerName.PushString(NameBuffer); PlayerUserID.Push(GetClientUserId(client)); } public Action ShowPlayerMenu(int client, int args) { // tworze menu i wskazuje funkcje, będącą jego handlerem - tam będzie odbywała się obsługa tego, co wybrał gracz Menu menu = new Menu(ShowPlayerMenu_Handler); // ustawiam tytuł menu.SetTitle("Lista graczy:"); // alokuje tablice, które będą służyły do budowania menu char MenuOptionBuffer[MAX_NAME_LENGTH + 32]; char InfoBuffer[5]; char NameBuffer[MAX_NAME_LENGTH]; // lece pętlą po wszystkich elementach tablicy... for (int i = 0; i < PlayerName.Length;i++) { // pobieram nick do tymczasowej tablicy, aby możliwe było sformatowanie stringa, jako pozycji w menu PlayerName.GetString(i, NameBuffer, sizeof(NameBuffer)); // formatuje stringa, ktory stanie sie moją pozycją w menu. Jak widać, komórka może zostać pobrana bez potrzeby korzystania z tablicy (PlayerUserID.Get) Format(MenuOptionBuffer, sizeof(MenuOptionBuffer), "%s (%d)", NameBuffer, PlayerUserID.Get(i)); // pierwszy argument menu.AddItem to "identyfikator pozycji", który musi być stringiem. Z tego względu formatuje InfoBuffer Format(InfoBuffer, sizeof(InfoBuffer), "%d", UserID); // dodaje nowy wiersz do menu menu.AddItem(InfoBuffer, MenuOptionBuffer); } menu.ExitButton=true; // pokazanie menu graczowi o index'ie client menu.Display(client, 30); } // uchwyt do menu public int ShowPlayerMenu_Handler(Menu menu, MenuAction action, int client, int item) { // wyłapuje, czy gracz wybrał którąś opcję if(action == MenuAction_Select) { // Pobieram owy "identyfikator pozycji" - w naszym przypadku jest to UserId wybranego gracza char InfoBuffer[5]; menu.GetItem(item, InfoBuffer, sizeof(InfoBuffer)); // identyfikator jest stringiem, więc musimy przerobić go na integera... int UserID = StringToInt(InfoBuffer); // na jego podstawie szukamy gracza... int clientID = GetClientOfUserId(UserID); // jeśli gracz już się rozłączył, wartość clientID będzie wynosiła 0. Dodałem prostą makre (#define NOT_CONNECTED 0) dla poprawy czytelności if(clientID == NOT_CONNECTED) { PrintToChat(client, "Gracz juz wyszedl!"); } else { PrintToChat(client, "Ten gracz jest w grze!"); } } } To by było na tyle ? Jeżeli pojawią się jakieś pytania z radością na nie odpowiem, a w razie potrzeby zaktualizuję poradnik Pozdrawiam! Cytuj Przez MAGNET, 31 Sierpnia 2018 A jak ktoś jest już kozak, to może tablice dynamiczne 2D? https://github.com/MAGNET1/SourceMod/tree/master/Array2D Udostępnij tę odpowiedź Odnośnik do odpowiedzi Udostępnij na innych stronach
plx211 2 1 Napisano 1 Września 2018 PushArrayString oraz SetArrayString przyjmuja 2 parametry nie 3 ( https://sm.alliedmods.net/new-api/adt_array/PushArrayString )Hej! Skorzystałeś z linku lub pobrałeś załącznik? Uhonoruj naszą pracę poprzez rejestrację na forum i rośnij razem z nami! Dodaj odpowiednik z uzyciem methodmap: 2 godziny temu, MAGNET napisał: MojUchwyt = CreateArray(MAX_NAME_LENGTH+1) MojUchwyt = new ArrayList(MAX_NAME_LENGTH+1); 2 godziny temu, MAGNET napisał: PushArrayCell(MojUchwyt, liczba); PushArrayString(MojUchwyt, text, sizeof(text)); PushArrayArray(MojUchwyt, tablica, sizeof(tablica)); MojUchwyt.Push(liczba); MojUchwyt.PushString(text); MojUchwyt.PushArray(tablica, sizeof(tablica)); 2 godziny temu, MAGNET napisał: SetArrayCell(MojUchwyt, 0, 3); SetArrayString(MojUchwyt, 1, inny_text, sizeof(inny_text)); SetArrayArray(MojUchwyt, 2, inna_tablica, sizeof(inna_tablica)); MojUchwyt.Set(0, 3); MojUchwyt.SetString(1, inny_text); MojUchwyt.SetArray(2, inna_tablica, sizeof(inna_tablica)); 2 godziny temu, MAGNET napisał: int wartosc = GetArrayCell(MojUchwyt, 0); GetArrayString(MojUchwyt, 1, pusta_tablica, sizeof(pusta_tablica)); GetArrayArray(MojUchwyt, 2, druga_tablica, sizeof(druga_tablica)); int wartosc = MojUchwyt.Get(0); MojUchwyt.GetString(1, pusta_tablica, sizeof(pusta_tablica)); MojUchwyt.GetArray(2, druga_tablica, sizeof(druga_tablica)); 2 godziny temu, MAGNET napisał: RemoveFromArray(MojUchwyt, 0); MojUchwyt.Erase(0); Cytuj Przez MAGNET, 1 Września 2018 Done Udostępnij tę odpowiedź Odnośnik do odpowiedzi Udostępnij na innych stronach
MAGNET Napisano 2 Września 2018 Dodano krótki przykład zastosowania tablic Cytuj Udostępnij tę odpowiedź Odnośnik do odpowiedzi Udostępnij na innych stronach