View Single Post
Author Message
F2dk
Junior Member
Join Date: Mar 2009
Old 01-14-2014 , 20:00   [INC] Kvizzle - KeyValues made easy
Reply With Quote #1

Introduction

When working with KeyValues, it can sometimes be frustratingly complex to do seemingly simple tasks.
Therefore I made something similar to CSS Selectors, just for KeyValues. I call it Kvizzle.

The idea is that, instead of manually navigating up and down in the KeyValues structure, you simply describe the path with a string. In the string, a dot represents a child.

Let us look at this sample KeyValues file.
Code:
"Root"
{
	"Arena1"
	{
		"Name"     "Arena #1"
		"Players"  "10"
		"Author"
		{
			"Name"	"F2"
			"Email"	"[email protected]"
		}
	}
}
Let us say you want to fetch the value of "Players".
The path would be: Arena1.Players

If you want to get the name of the author, the path is: Arena1.Author.Name

The code would look like this:
PHP Code:
new players KvizGetNum(kv0"Arena1.Players");

decl String:authorName[64];
KvizGetString(kvauthorNamesizeof(authorName), """Arena1.Author.Name"); 
  • kv is the handle to the KeyValues structure
  • 0 and "" are the default values to be returned if path is not found
  • "Arena1.Players" and "Arena1.Author.Name" are the paths to the values in the KeyValues structure

Okay, so far it is pretty simple. Let us step it up a notch.

Kvizzle supports something called "pseudo classes".
We can use them to describe a path that is not simply given by a name. For example, "first child" is a pseudo class.

Pseudo classes are indicated by using a colon instead of a dot.

This path would get us to the Author's name: Arena1.Author:first-child
To get the value of Players, we could use: Arena1:nth-child(2)

Kvizzle also supports different "actions".
For example, if you want to count how many children Arena1 has, you could use: Arena1:count
This would return 3.

Finally, you can also use "checks".
Let us say you want to find the child to Arena1 that has the value 10.
To do this you can use
  • The pseudo class :any-child, which will loop through all the children until the given check has been fulfilled
  • The check :has-value(text), which will check if the current node has the given value text
  • The action :key, which will return the key-name of the current node.
So the path will be: Arena1:any-child:has-value(10):key
This will return "Players".
PHP Code:
decl String:keyName[64];
KvizGetString(kvkeyNamesizeof(keyName), """Arena1:any-child:has-value(10):key"); 
Documentation

Pseudo Classes
  • :first-child - navigates to the first child
  • :last-child - navigates to the last child
  • :nth-child(n) - navigates to the nth child (specified in the parentheses)
  • :any-child - will look through all children, until the given check has been fulfilled
  • :parent or :up - navigates up to the parent

Checks
  • :has-value - checks if the current node has a value (ie. is not a section)
  • :has-value(text) - checks if the current node has the value given in text (case sensitive)
  • :has-value-ci(text) - checks if the current node has the value given in text (case insensitive)

Actions (can only be used once at the end of the path)
  • :count - Returns the number of children
  • :key or :section-name - Returns the key name (aka section name)
  • :value - Returns the value (this is the default action and is therefore normally omitted)
  • :value-or-section - If the node has a value, that is returned. Otherwise the section name is returned.

Functions
PHP Code:
KvizEscape(String:dest[], destLen, const String:src[])

Handle:KvizCreate(const String:name[])
Handle:KvizCreateFromFile(const String:name[], const String:file[])
bool:KvizToFile(Handle:kv, const String:file[], const String:kvizPath[] = ""any:...)
bool:KvizClose(Handle:kv) {

bool:KvizGetStringExact(Handle:kvString:value[], valueLen, const String:path[], any:...)
bool:KvizGetString(Handle:kvString:value[], valueLen, const String:defVal[], const String:path[], any:...)
bool:KvizSetString(Handle:kv, const String:value[], const String:path[], any:...)

bool:KvizGetNumExact(Handle:kv, &value, const String:path[], any:...)
KvizGetNum(Handle:kvdefVal, const String:path[], any:...)
bool:KvizSetNum(Handle:kvvalue, const String:path[], any:...)

bool:KvizGetFloatExact(Handle:kv, &Float:value, const String:path[], any:...)
Float:KvizGetFloat(Handle:kvFloat:defVal, const String:path[], any:...)
bool:KvizSetFloat(Handle:kvFloat:value, const String:path[], any:...)

bool:KvizGetVectorExact(Handle:kvFloat:value[3], const String:path[], any:...)
bool:KvizGetVector(Handle:kvFloat:vec[3], const Float:defVal[3], const String:path[], any:...)
bool:KvizSetVector(Handle:kvFloat:vec[3], const String:path[], any:...)

bool:KvizGetColorExact(Handle:kv, &r, &g, &b, &a, const String:path[], any:...)
bool:KvizGetColor(Handle:kv, &r, &g, &b, &adefRdefGdefBdefA, const String:path[], any:...)
bool:KvizSetColor(Handle:kvrgba, const String:path[], any:...)

bool:KvizGetUInt64Exact(Handle:kvvalue[2], const String:path[], any:...)
bool:KvizGetUInt64(Handle:kvvalue[2], const defVal[2], const String:path[], any:...)
bool:KvizSetUInt64(Handle:kvvalue[2], const String:path[], any:...)

bool:KvizDelete(Handle:kv, const String:path[], any:...)

bool:KvizExists(Handle:kv, const String:path[], any:...)

bool:KvizJumpToKey(Handle:kvbool:create, const String:path[], any:...)
bool:KvizGoBack(Handle:kv)
bool:KvizRewind(Handle:kv
A description of each function and parameter can be found in the include.

Quick Guide

To use Kvizzle, it is important that you only use the Kviz* functions. Using the normal Kv* functions will mess up Kvizzle's functionality.

You must always begin with KvizCreate/KvizCreateFromFile, and you must always end with KvizClose.

Here is a quick example that you can try out:
Code:
new Handle:kv = KvizCreate("Test");
KvizSetNum(kv, 10, "Top.Key1");
KvizSetNum(kv, 20, "Top.Key2");
KvizSetNum(kv, 30, "Top.Key3");

PrintToChatAll("Key2: %i", KvizGetNum(kv, 0, "Top:nth-child(%i)", 2)); // Prints 20

KvizClose(kv);
It is important that you always use the KvizCreate()/KvizCreateFromFile() and KvizClose(kv) functions when working with Kvizzle. You should never use CreateKeyValues()/FileToKeyValues() or CloseHandle(kv) together with Kvizzle.

Performance

I made some performance measurements. I will post them here soon but until then, the conclusion is:
  • If you are using KeyValues in OnPluginStart, OnMapStart or some other place where performance is not critical, then Kvizzle's performance is good enough for you. (If you are reading from a file, then it will be about 10% slower in total.)
  • If you are using KeyValues in OnGameFrame or some other performance-critical place, then you should not use Kvizzle. (If you are not reading from a file, it will be about 50-150% slower in total.)

Changelog
  • v1.0.0 (15/01/2014)
    • Supports having several KVs open simultaneously
    • Pseudo-Classes (like :first-child, :nth-child(n), :any-child)
    • Checks (like :has-value(val))
    • Actions (like :count, :section-name)
    • A traversal stack (with KvizJumpToKey, KvizGoBack and KvizRewind)
    • Escaping in path (with backslash)
    • Read from file and write to file

-> Download Kvizzle <-

If you are interested, you can also download a unit test of Kvizzle with a lot of examples.

Feedback

Since this is the first release, there is plenty of room for improvement.
So if you have any comments, questions, or suggestion, feel free to write them here.

Last edited by F2dk; 10-04-2014 at 08:39.
F2dk is offline