Jump to content
  • Chmurka
  • Boróweczka
  • Jabłuszko
  • Limonka
  • Czekoladka
  • Węgielek

Recommended Posts

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 dynamiczne
Hej! 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:

cd-shelf-es-ab-00.jpg.c6c30800b39dbb2fc981622ab1fadd7c.jpg

 

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 ResizeArray
Hej! 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ę ( GetArrayCell
Hej! 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 UserId
Hej! 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!

 

  • Lubię to! 6
By MAGNET,

A jak ktoś jest już kozak, to może tablice dynamiczne 2D? https://github.com/MAGNET1/SourceMod/tree/master/Array2D

Share this post


Link to post
Share on other sites

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);

 

  • Lubię to! 2
  • Dobry pomysł! 1
By MAGNET,

Done

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

Nasza historia

Na początku byliśmy małą grupą internetowych znajomych, którzy stwierdzili, że potrzebne jest solidne forum, na którym znajdą się ludzie z dużą wiedzą programistyczną ukierunkowaną na CS:GO. Pomysł powstał na początku 2018 roku, a parę miesięcy później, 19 kwietnia, powstała ta strona internetowa. Jako alternatywna odpowiedź na inne tego typu miejsca, poważnie podeszliśmy do tematu, najpierw tłumacząc angielską dokumentację SourceMod'a na język polski, a potem pisząc rozległe poradniki i wypełniając forum najpotrzebniejszymi rzeczami dla właścicieli serwerów i programistów. Cała nasza Ekipa jest dumna z pracy jaką w to włożyliśmy i cieszymy się że zbierają się wokół nas zarówno ludzie znający tematy sourcepawn'a i konfiguracji, jak i również nowe twarze w tym "biznesie", którym z chęcią niesiemy wiedzę oraz pomoc w rozwiązywaniu problemów.

Największe modyfikacje serwerowe

×
×
  • Create New...