Scripting:Data Storage
From STNE Wiki
Understanding script data (STNE Tables)
Data Tables in STNE are not the same as tables in a RDBMS, they don't have direct indexes, they are cascading, and understanding them can be a bit confusing at times. Essentially, there is a parent object, called CDataNodeStorage, and children, called CDataNode and CDataNodeList. The parent only has directly the child CDataNode. CDataNode has the child CDataNodeList, and CDataNodeList has the child CDataNode. I little confusing, I know. So here's a mapping:
CDataNodeStorage->CDataNod
CDataNode-> CDataNodeList
CDataNodeList->CDataNode
All CDataNodes are locatable from CDataNodeStorage
All single values are accessed via a CDataNode
All multiple values are accessed from a CDataNodeList
This is important to remember, because each has a unique function. CDataNodeList is a list of items, like sectors or colonies, and CDataNode has only one item. If you have a need for a list, but not assignment, you can use the CDataNodeList to do this.
Let's take a small example and break it down:
TableVersion=10.1; Colonies { Earth='500|400'; Mars='500|401'; Venus='400|402'; } Allies { 12345; 67890; } Production { Earth { Photons=2; Duranium=8; } }
First, you need some way to access the table. This is done with CDataNodeStorage. The way to open the pointer depends on if it is local or remote(on another account). For local storage,
Var db As New CDataNodeStorage(String, Boolean);
Where the TableName is a string of the name, and the boolean is table creation (true to create, false to fail instead if the table doesn't exit). For remote storage,
Var db As CDataNodeStorage.LoadFromUser(String, Integer);
Where String is the name of the table, and Integer is the user ID (the table does not open cross server, so there is no designator for EN1 vs DE3 etc)
Next is accessing the data. The first entry, TableVersion, has the value of 10.1, a double. The value would be accessed as such:
Var value As Double = db.Items.Item('TableVersion').asDouble;
Note that we told the Item method to retrieve the value as a double. STNE via VB .NET has the tendency of casting doubles to ints not in a truncate fashion, but as a function of ceiling, and to drop the decimal when casting to a String. Therefore, when possible, always be specific on the type.
The next entry is Colonies, which is actually a multiple value. First, you access the key, then the list below it, like this:
Var Location As CDataNodeList = db.Items.Item('Colonies').Items;
This is a direct assignment, rather than creating a CDataNode and the accessing the list from there. If you prefer or need to access the node independently, you can do it in multiple steps:
Var Node As CDataNode = db.Items.Item('Colonies'); Var NodeList As CDataNodeList = Node.Items; Var Location As String = NodeList.Item('Earth').AsString;
Normally, the only reason to access the list from the parent node is to pass it to another function or method. To access a value from the list:
Var Location As String = db.Items.Item('Colonies').Items.Item('Earth').AsString;
Since the value is a string (denoted by the single quotes), I specified the type.
The next entry, Allies has no values, just a list of keys. These are useful for items that you need to maintain a list of, but not values. Access is the same as a list of keys and values, but since you aren't retrieving the values, you would usually use ContainsKey to check it it's in the list:
If (db.Items.Item('Allies').Items.ContainsKey(12345)) { // Do something }
Production is a cascaded value. This one is slightly more complex, but from what you have learned above, it is fairly straightforward:
Var Photons As Integer = db.Items.Item('Production').Items.Item('Earth').Items.Item('Photons').asInteger;
As a multiple statement, it looks like this:
Var NodeList As CDataNodeList = db.Items.Item('Production').Items; Var NodeListColony As CDataNodeList = NodeList.item('Earth').Items; Var Photons As Integer = NodeListColony.Item('Photons').asInteger;
Node that .Items was called twice (well, 3 times but the first is for any access). We don't actually care what the value of the key is, since it's an index. Then we get the subindex for a particular colony (Earth), then a particular production (Photons).
Writing to the keys is comparable.
db.Items.AddDouble(' TableVersion', 10.1); If (NOT db.Items.ContainsKey('Colonies')) { db.Items.Add('Colonies'); } db.Items.Item('Colonies').Items.AddString('Earth', “500|400”); If (NOT db.Items.ContainsKey('Allies')) { db.Items.Add('Allies'); } db.Items.Item('Allies').Items.Add(12345); If (NOT db.Items.ContainsKey('Production')) { db.Items.Add('Production'); } If (NOT db.Items.Item('Production').Items.ContainsKey('Earth')) { db.Items.Item('Production').Items.ContainsKey('Earth'); } db.Items.Item('Production').Items.Item('Earth').Items.AddInteger('Photons', 2);
For Allies, since there is no value, so you need to use the generic .Add method. The code above will overwrite the values if they already exist, it's simple code so it doesn't check for the previous existence of anything but the index path, and creates them if the indexes don't exist.
To delete a value, use the .Remove() method. It has a comparable form as .Add:
db.Items.Remove('TableVersion');
-
Some caveats for script storage
- Local tables can not be renamed or deleted.
- Remote tables can not be created
- LoadFromUser() can open local tables, but seems to abort the script unexpectedly from time to time, with no error.
- Multiple access code is weak. At least from an extension. I hit 'Exceded table limit' errors from time to time, and I am under quota. I simply open the table, hit edit, and save, then the deactivate and re execute the script and it works as expected again.
Here is a script with the above snippets in a complete form:
Var Node As CDataNode; Var NodeList As CDataNodeList; Var NodeListColony As CDataNodeList; Var Location As String; Var Value As Double; Var Photons As Integer; Var Plasma As Integer; Var db As New CDataNodeStorage("9046", True); // Get some current values, as one line fetches Value = db.Items.Item('TableVersion').asDouble; WriteLine(value); Location = db.Items.Item('Colonies').Items.Item('Earth').asString; WriteLine(Location); If (db.Items.Item('Allies').Items.ContainsKey(12345)) { WriteLine("Found 12345") } Photons = db.Items.Item('Production').Items.Item('Earth').Items.Item('Photons').asInteger; WriteLine(Photons); // Now create and alter some keys db.Items.AddDouble('TableVersion', 10.2); If (NOT db.Items.ContainsKey('Colonies')) { db.Items.Add('Colonies'); } db.Items.Item('Colonies').Items.AddString('Mercury', '400|400'); If (NOT db.Items.ContainsKey('Allies')) { db.Items.Add('Allies'); } db.Items.Item('Allies').Items.Add(23456); If (NOT db.Items.ContainsKey('Production')) { db.Items.Add('Production'); } If (NOT db.Items.Item('Production').Items.ContainsKey('Earth')) { db.Items.Item('Production').Items.ContainsKey('Earth'); } db.Items.Item('Production').Items.Item('Earth').Items.AddInteger('Plasma', 4); // Get the added/changes values, as multi line fetches Value = db.Items.Item('TableVersion').asDouble; WriteLine(value); Node = db.Items.Item('Colonies'); NodeList = Node.Items; Location = NodeList.Item('Mercury').asString; WriteLine(Location); If (db.Items.Item('Allies').Items.ContainsKey(23456)) { WriteLine("Found 23456") } NodeList = db.Items.Item('Production').Items; NodeListColony = NodeList.item('Earth').Items; Plasma = NodeListColony.Item('Plasma').asInteger; WriteLine(Plasma); // Remove the added keys db.Items.Item('Colonies').Items.Remove('Mercury'); db.Items.Item('Allies').Items.Remove(23456); db.Items.Item('Production').Items.Item('Earth').Items.Remove('Plasma');
--Miltiades (En-1:56599) 07:27, 10 October 2011 (CEST)