Difference between revisions of "Ru:KeyValues (SourceMod Scripting)"

From AlliedModders Wiki
Jump to: navigation, search
(Introduction)
Line 1: Line 1:
KeyValues are simple, tree-based structures used for storing nested sections containing key/value pairsDetailed information on KeyValues can be seen at the [http://developer.valvesoftware.com/wiki/KeyValues_class Valve Developer Wiki].   
+
KeyValues это простая древовидная структура используемая для хранения вложенных разделов состоящих из пар ключ/значениеДетальная информация про KeyValues может быть найдена на [http://developer.valvesoftware.com/wiki/KeyValues_class Valve Developer Wiki].   
  
All KeyValues specific functions in this document are from <tt>public/include/keyvalues.inc</tt>.
+
Все KeyValues функции в этом документе находятся в <tt>public/include/keyvalues.inc</tt>.
  
 
=Введение=
 
=Введение=
Line 14: Line 14:
 
KeyValues структуры создаются с помощью <tt>CreateKeyValues()</tt>.  Handle должен быть освобожден после завершения работы для устранения утечки памяти.
 
KeyValues структуры создаются с помощью <tt>CreateKeyValues()</tt>.  Handle должен быть освобожден после завершения работы для устранения утечки памяти.
  
=Files=
+
=Файлы=
KeyValues can be exported and imported via KeyValues filesThese files always consist of a root node and any number of sub-keys or sub-sectionsFor example, a file might look like this:
+
KeyValues могут быть экспортированы и импортированы с помощью KeyValues файловЭти файлы всегда состоят из главного узла и множества под-ключей или подразделовНапример файл может выглядеть вот так:
 
<pre>"MyFile"
 
<pre>"MyFile"
 
{
 
{
Line 24: Line 24:
 
}</pre>
 
}</pre>
  
In this example, <tt>STEAM_0:0:7</tt> is a section under <tt>MyFile</tt>, the root node.   
+
В этом примере <tt>STEAM_0:0:7</tt> это раздел под главным узлом <tt>MyFile</tt>.   
  
To load KeyValues from a file, use <tt>FileToKeyValues</tt>  To save KeyValues to a file, use <tt>KeyValuesFromFile</tt>.  For both cases, you must already have a Handle to a KeyValues structure.
+
Для загрузки KeyValues из файла используйте <tt>FileToKeyValues</tt>. Для сохранения KeyValues в файл используйте <tt>KeyValuesFromFile</tt>.  Для обоих случае вы всегда должны иметь Handle ссылающийся на KeyValues структуру.
  
=Traversal=
+
=Перемещение=
SourceMod provides natives for traversing over a KeyValues structureHowever, it is important to understand how this traversal worksInternally, SourceMod keeps track of two pieces of information:
+
SourceMod обеспечивает нативное перемещение по KeyValues структуреОднако очень важно понимать как происходит это перемещениеВнутренне, SourceMod отслеживает из двух частей информации:
*The root node
+
*Главный узел
*A traversal stack
+
*Стек перемещения
  
Since a KeyValues structure is inherently recursive (it's a tree), the ''traversal stack'' is used to save a history of each ''traversal'' made (a traversal being a recursive dive into the tree, where the current nesting level deepens by one section).  The ''top'' of this stack is where all operations take place.   
+
Поскольку структура KeyValues изначально рекурсивная (это дерево), ''стек перемещения'' используется для сохранения истории каждого ''перемещения'' (перемещение начинается рекурсивно погружаться в дерево, где текущий уровень вложенности углубляется на одну секцию).  The ''top'' of this stack is where all operations take place.   
  
 
For example, <tt>KvJumpToKey</tt> will attempt to find a sub-key under the current section.  If it succeeds, the position in the tree has changed by moving down one level, and thus this position is pushed onto the traversal stack.  Now, operations such as <tt>KvGetString</tt> will use this new position.
 
For example, <tt>KvJumpToKey</tt> will attempt to find a sub-key under the current section.  If it succeeds, the position in the tree has changed by moving down one level, and thus this position is pushed onto the traversal stack.  Now, operations such as <tt>KvGetString</tt> will use this new position.

Revision as of 22:44, 24 December 2010

KeyValues это простая древовидная структура используемая для хранения вложенных разделов состоящих из пар ключ/значение. Детальная информация про KeyValues может быть найдена на Valve Developer Wiki.

Все KeyValues функции в этом документе находятся в public/include/keyvalues.inc.

Введение

KeyValues состоит из узлов и разделов, которые содержат пары ключей и значений. Разделы выглядят примерно так:

"раздел"
{
	"ключ"	"значение"
}

Сторка "раздел" является именем раздела. Строка "ключ" это имя ключа и строка "значение"" является значением.

KeyValues структуры создаются с помощью CreateKeyValues(). Handle должен быть освобожден после завершения работы для устранения утечки памяти.

Файлы

KeyValues могут быть экспортированы и импортированы с помощью KeyValues файлов. Эти файлы всегда состоят из главного узла и множества под-ключей или подразделов. Например файл может выглядеть вот так:

"MyFile"
{
	"STEAM_0:0:7"
	{
		"name"		"crab"
	}
}

В этом примере STEAM_0:0:7 это раздел под главным узлом MyFile.

Для загрузки KeyValues из файла используйте FileToKeyValues. Для сохранения KeyValues в файл используйте KeyValuesFromFile. Для обоих случае вы всегда должны иметь Handle ссылающийся на KeyValues структуру.

Перемещение

SourceMod обеспечивает нативное перемещение по KeyValues структуре. Однако очень важно понимать как происходит это перемещение. Внутренне, SourceMod отслеживает из двух частей информации:

  • Главный узел
  • Стек перемещения

Поскольку структура KeyValues изначально рекурсивная (это дерево), стек перемещения используется для сохранения истории каждого перемещения (перемещение начинается рекурсивно погружаться в дерево, где текущий уровень вложенности углубляется на одну секцию). The top of this stack is where all operations take place.

For example, KvJumpToKey will attempt to find a sub-key under the current section. If it succeeds, the position in the tree has changed by moving down one level, and thus this position is pushed onto the traversal stack. Now, operations such as KvGetString will use this new position.

There are natives which change the position but do not change the traversal stack. For example, KvGotoNextKey will change the current section being viewed, but there are no push/pop operations to the traversal stack. This is because the nesting level did not change; it advances to the next section at the same level, rather than finding a section a level deeper.

More traversal natives:

  • KvGotoFirstSubKey - Finds the first sub-section under the current section. This pushes the section onto the traversal stack.
  • KvGoBack - Pops the traversal stack (moves up one level).
  • KvRewind - Clears the traversal stack so the current position is the root node.

Basic Lookup

Let's take our MyFile example from above. How could we retrieve the name "crab" given the Steam ID?

bool:GetNameFromSteamID(const String:steamid[], String:name[], maxlength)
{
	new Handle:kv = CreateKeyValues("MyFile");
	FileToKeyValues(kv, "myfile.txt");
	if (!KvJumpToKey(kv, steamid))
	{
		return false;
	}
	KvGetString(kv, "name", name, maxlength);
	CloseHandle(kv);
	return true;
}

Note: KvJumpToKey is a traversal native that changes the nesting level of the traversal stack. However, CloseHandle will not accidentally close only the current level of the KeyValues structure. It will close the entire thing.

Iterative Lookup

Let's modify our previous example to use iteration. This has the same functionality, but demonstrates how to browse over many sections.

bool:GetNameFromSteamID(const String:steamid[], String:name[], maxlength)
{
	new Handle:kv = CreateKeyValues("MyFile");
	FileToKeyValues(kv, "myfile.txt");
 
	if (!KvGotoFirstSubKey(kv))
	{
		return false;
	}
 
	decl String:buffer[255];
	do
	{
		KvGetSectionName(kv, buffer, sizeof(buffer));
		if (StrEqual(buffer, steamid))
		{
			KvGetString(kv, "name", name, maxlength);
			CloseHandle(kv);
			return true;
		}
	} while (KvGotoNextKey(kv));
 
	CloseHandle(kv)
	return false
}

Note: In this example, note that KvGotoNextKey is an iterative function, not a traversal one. Since it does not change the nesting level, we don't need to call KvGoBack to return and continue iterating.

Full Traversal

Let's say we wanted to browse every section of a KeyValues file. An example of this might look like:

BrowseKeyValues(Handle:kv)
{
	do
	{
		if (KvGotoFirstSubKey(kv))
		{
			BrowseKeyValues(kv);
			KvGoBack(kv);
		}
	} while (KvGotoNextKey(kv));
}

This function will browse an entire KeyValues structure. Note that every successful call to KvGotoFirstSubKey is paired with a call to KvGoBack. This is because the former pushes onto the traversal stack. We must pop the position off so we can continue browsing from our old position.

Deletion

There are two ways to delete sections from a KeyValues structure:

  • KvDeleteKey - Safely removes a named sub-section and any of its child sections/keys.
  • KvDeleteThis - Safely removes the current position if possible.

Simple Deletion

First, let's use our "basic lookup" example to delete a Steam ID section:

RemoveSteamID(const String:steamid[])
{
	new Handle:kv = CreateKeyValues("MyFile")
	FileToKeyValues(kv, "myfile.txt")
	if (!KvGotoFirstSubKey(kv))
	{
		return
	}
	KvDeleteKey(kv, steamid)
	KvRewind(kv)
	KeyValuesToFile(kv, "myfile.txt")
	CloseHandle(kv)
}

Note: We called KvRewind so the file would be dumped from the root position, instead of the current one.

Iterative Deletion

Likewise, let's show how our earlier iterative example could be adapted for deleting a section. For this we can use KvDeleteThis, which deletes the current position from the previous position in the traversal stack.

RemoveSteamID(const String:steamid[])
{
	new Handle:kv = CreateKeyValues("MyFile")
	FileToKeyValues(kv, "myfile.txt")
 
	if (!KvGotoFirstSubKey(kv))
	{
		return
	}
 
	decl String:buffer[255]
	do
	{
		KvGetSectionName(kv, buffer, sizeof(buffer))
		if (StrEqual(buffer, steamid))
		{
			KvDeleteThis(kv)
			CloseHandle(kv)
			return
		}
	} while (KvGotoNextKey(kv))
 
	CloseHandle(kv)
}

Full Deletion

Lastly, let's take a look at how we would delete all (or some) keys. KvDeleteThis has the special property that it can act as an automatic iterator. When it deletes a key, it automatically attempts to advance to the next one, as KvGotoNextKey would. If it can't find any more keys, it simply pops the traversal stack.

An example of deleting all SteamIDs that have blank names:

DeleteAll(Handle:kv)
{
	if (!KvGotoFirstSubKey(kv))
	{
		return
	}
 
	for (;;)
	{
		decl String:name[4]
		KvGetString(kv, name, sizeof(name))
		if (name[0] == '\0')
		{
			if (KvDeleteThis(kv) < 1)
			{
				break
			}
		} else if (!KvGotoNextKey(kv)) {
			break
		}	
	}
}

While at first this loop looks infinite, it is not. If KvDeleteThis fails to find a subsequent key, it will break out of the loop. Similarly, if KvGotoNextKey fails, the loop will end.