Raised This Month: $32 Target: $400
 8% 

[INC] Kvizzle - KeyValues made easy


Post New Thread Reply   
 
Thread Tools Display Modes
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
F2dk
Junior Member
Join Date: Mar 2009
Old 01-14-2014 , 20:01   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #2

Examples

I have a couple of examples from real projects. Here you can see the difference between using the normal Kv-functions, and using Kvizzle. This is just to give you an idea about how much time you can save by using Kvizzle.

Example 1

Without Kvizzle:
PHP Code:
AddSpawnToArena(Handle:kv, const String:map[], arena, const String:role[], Float:origin[3], Float:angles[3]) {
    if (
KvJumpToKey(kvmaptrue) == false)
        
SetFailState("No arenas for this map");
    
    if (
KvGotoFirstSubKey(kv) == false
        
SetFailState("Missing arenas for this map");
    
    new 
carena 1;
    do {
        if (
carena != arena) {
            
KvJumpToKey(kvroletrue);
            
KvJumpToKey(kv"Spawns"true);
            
            new 
spawnid 1;
            if (
KvGotoFirstSubKey(kv)) {
                do {
                    
spawnid++;
                } while (
KvGotoNextKey(kv));
                
KvGoBack(kv);
            }
            
            
decl String:spawnidstr[8];
            
IntToString(spawnidspawnidstrsizeof(spawnidstr));
            
            if (
KvJumpToKey(kvspawnidstrtrue)) {
                
KvSetVector(kv"origin"origin);
                
KvSetVector(kv"angles"angles);
                
KvGoBack(kv);
            }
            
            
KvGoBack(kv); // "Spawns"
            
            
KvGoBack(kv); // role
            
break;
        }
        
        
carena++;
    } while (
KvGotoNextKey(kv));
    
KvGoBack(kv); // List arenas
    
    
KvGoBack(kv); // map

With Kvizzle:
PHP Code:
AddSpawnToArena(Handle:kv, const String:map[], arena, const String:role[], Float:origin[3], Float:angles[3]) {
    
decl String:escapedMap[64];
    
KvizEscape(escapedMapsizeof(escapedMap), map);
    new 
spawnCount KvizGetNum(kv0"%s:nth-child(%i).%s.Spawns:count"maparenarole);
    
KvizSetVector(kvorigin"%s:nth-child(%i).%s.Spawns.%i.origin"escapedMaparenarolespawnCount+1);
    
KvizSetVector(kvangles"%s:nth-child(%i).%s.Spawns.%i.angles"escapedMaparenarolespawnCount+1);

Example 2

Without Kvizzle:
PHP Code:
bool:FindAttribVal(Handle:kv, const String:prefab[], const String:attrib[], String:value[], valueLen) {
    new 
bool:found false;
    if (
KvJumpToKey(kv"prefabs"false)) {
        if (
KvJumpToKey(kvprefabfalse)) {
            if (
KvJumpToKey(kv"attributes"false)) {
                if (
KvGotoFirstSubKey(kv)) {
                    do {
                        
KvGetString(kv"attribute_class"curAttrsizeof(curAttr));
                        if (
StrEqual(curAttrattribfalse)) {
                            
KvGetString(kv"value"valuevalueLen);
                            
found true;
                            break;
                        }
                    } while (
KvGotoNextKey(kv));
                    
KvGoBack(kv);
                }
                
KvGoBack(kv);
            }
            
KvGoBack(kv);
        }
        
KvGoBack(kv);
    }
    return 
found;

With Kvizzle:
PHP Code:
bool:FindAttribVal(Handle:kv, const String:prefab[], const String:attrib[], String:value[], valueLen) {
    
decl String:escapedPrefab[64], String:escapedAttrib[64];
    
KvizEscape(escapedPrefabsizeof(escapedPrefab), prefab);
    
KvizEscape(escapedAttribsizeof(escapedAttrib), attrib);
    return 
KvizGetStringExact(kvvaluevalueLen"prefabs.%s.attributes:any-child.attribute_class:has-value(%s):parent.value"escapedPrefabescapedAttrib);


Last edited by F2dk; 01-14-2014 at 20:05.
F2dk is offline
F2dk
Junior Member
Join Date: Mar 2009
Old 01-14-2014 , 20:02   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #3

reserved

Last edited by F2dk; 01-14-2014 at 20:02.
F2dk is offline
Root_
Veteran Member
Join Date: Jan 2012
Location: ryssland
Old 01-14-2014 , 20:34   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #4

Wow this is really awesome.
__________________


dodsplugins.com - Plugins and Resources for Day of Defeat
http://twitch.tv/zadroot
Root_ is offline
TnTSCS
AlliedModders Donor
Join Date: Oct 2010
Location: Undisclosed...
Old 01-15-2014 , 13:58   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #5

Very clean. This will help a lot of people save a lot of time and headache.
__________________
View my Plugins | Donate
TnTSCS is offline
Powerlord
AlliedModders Donor
Join Date: Jun 2008
Location: Seduce Me!
Old 01-15-2014 , 14:29   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #6

I'd be tempted to use this, but most of the time when I'm parsing KeyValues files I'm going to be looping over a section. Something that the default KeyValues implementation does pretty well and would incur heavier costs to do with Kvizzle.

Also, should I assume the name Kvizzle is a reference to Sizzle, one of jQuery's core parts?
__________________
Not currently working on SourceMod plugin development.

Last edited by Powerlord; 01-15-2014 at 14:41.
Powerlord is offline
dordnung
Veteran Member
Join Date: Apr 2010
Old 01-15-2014 , 15:27   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #7

This looks really awesome! Great job!
__________________
dordnung is offline
friagram
Veteran Member
Join Date: Sep 2012
Location: Silicon Valley
Old 01-15-2014 , 15:58   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #8

Quote:
Originally Posted by Powerlord View Post
I'd be tempted to use this, but most of the time when I'm parsing KeyValues files I'm going to be looping over a section. Something that the default KeyValues implementation does pretty well and would incur heavier costs to do with Kvizzle.

Also, should I assume the name Kvizzle is a reference to Sizzle, one of jQuery's core parts?
Not to mention if you have to do other things in said loops, like filter certain values, or do any task other than just fetch a value. It's better to just learn how to do it right, which doesn't take that long anyway.
__________________
Profile - Plugins
Add me on steam if you are seeking sp/map/model commissions.
friagram is offline
F2dk
Junior Member
Join Date: Mar 2009
Old 01-15-2014 , 16:12   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #9

Quote:
Originally Posted by Powerlord View Post
I'd be tempted to use this, but most of the time when I'm parsing KeyValues files I'm going to be looping over a section. Something that the default KeyValues implementation does pretty well and would incur heavier costs to do with Kvizzle.

Also, should I assume the name Kvizzle is a reference to Sizzle, one of jQuery's core parts?
Well observed. It is indeed a reference to Sizzle.

Actually, looping through a section is quite easy with Kvizzle.

Let's say you have a structure like this:
Code:
"Root"
{
	"Admins"
	{
		"F2"
		{
			"SteamID"	"STEAM_0:0:100"
			"Access"	"z"
		}
		"Powerlord"
		{
			"SteamID"	"STEAM_0:0:200"
			"Access"	"x"
		}
		"Black-Rabbit"
		{
			"SteamID"	"STEAM_0:0:300"
			"Access"	"y"
		}
	}
}
To loop through each of the admins, you could do:
PHP Code:
for (new 1KvizExists(kv"Admins:nth-child(%i)"i); i++) {
    
decl String:admin[64], String:access[32];
    
KvizGetStringExact(kvadminsizeof(admin), "Admins:nth-child(%i):key"i);
    
KvizGetStringExact(kvaccesssizeof(access), "Admins:nth-child(%i).Access"i);
    
PrintToChatAll(kv"Admin '%s' has access '%s'"adminaccess);

I have optimized this kind of access, so if there are 10 children, internally KvGotoFirstSubKey is called once and KvGotoNextKey is called 10 times, just like when you use the normal Kv functions. So the overhead is mostly the parsing of the string, and that's really not something you should be worried about when using it OnPluginStart, OnMapStart, etc.

If you are doing nested loops, you will end up with very long paths, like: "Admins:nth-child(%i):nth-child(%i):nth-child(%i)". To avoid these long strings, you can do something like this:
PHP Code:
for (new 1KvizJumpToKey(kv"Admins:nth-child(%i)"i); KvizGoBack(kv), i++) {
    
decl String:admin[64], String:access[32];
    
KvizGetStringExact(kvadminsizeof(admin), ":key");
    
KvizGetStringExact(kvaccesssizeof(access), "Access");
    
PrintToChatAll(kv"Admin '%s' has access '%s'"adminaccess);

Also, sometimes when you loop through children, it is because you are searching for something.
For example, imagine that you have a SteamID and you want to check if that SteamID belongs to an admin. Normally you would loop through all admins, and check their SteamID. With Kvizzle you can avoid that, and write a one-liner:
PHP Code:
KvizExists(kv"Admins:any-child.SteamID:has-value(%s)""STEAM_0:0:200"); 

Last edited by F2dk; 01-16-2014 at 01:01.
F2dk is offline
Mathias.
Veteran Member
Join Date: Aug 2010
Location: Canada is my city
Old 01-15-2014 , 16:38   Re: [INC] Kvizzle - KeyValues made easy
Reply With Quote #10

Nice job, I love it. I have an idea to fix the issue of "To use Kvizzle, it is important that you only use the Kviz* functions. Using the normal Kv* functions will mess up Kvizzle's functionality.", maybe clone the handle on your side so people can't mess up the functionality of Kvizzle?
Mathias. is offline
Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 04:28.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode