Zandronum Chat on our Discord Server Get the latest version: 3.2
Source Code

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0001831Zandronum[All Projects] Suggestionpublic2014-06-11 21:542018-09-30 21:35
ReporterWatermelon 
Assigned ToTorr Samaho 
PriorityhighSeverityfeatureReproducibilityN/A
StatusclosedResolutionfixed 
PlatformOSOS Version
Product Version1.2 
Target Version1.3Fixed in Version1.3 
Summary0001831: SQLite database for persistent data storage
DescriptionContinuation of the database discussion to move it out of the account system thread.
Attached Files

- Relationships
related to 0001176new Account system development/discussion 

-  Notes
User avatar (0009129)
Watermelon (developer)
2014-06-11 21:57

To continue this discussion:


Quote
That would somehow encourage mods not to interfere with each others data, but still doesn't enforce it. Adding the extra column won't be a problem, but then all of the ACS DB functions get an additional argument. What's the general opinion on this? Since a mod could just write "zanrun-time" (one argument, i.e. namespace) instead of "zanrun", "time" (two argument, i.e. namespace and section), we don't really get much out of the more complicated interface except of this slight encouragement to use a unique namespace. Another possibility would be renaming the namespace argument in the ACS functions to section and to force selecting a namespace via a new lump.


I don't think there is a way to enforce it.

The idea of a lump might work, though then it introduces a lot of overhead to deal with (ex: conflicts, who gets what priority, when...etc).
If we miss something here and find out later down the road, it could be very painful.



We also have to take into account hosting services like Best-Ever where people might maliciously try to write into other people's DB's.
How does one stop this?
User avatar (0009134)
AlexMax (developer)
2014-06-11 22:32

> That would somehow encourage mods not to interfere with each others data, but still doesn't enforce it. Adding the extra column won't be a problem, but then all of the ACS DB functions get an additional argument. What's the general opinion on this? Since a mod could just write "zanrun-time" (one argument, i.e. namespace) instead of "zanrun", "time" (two argument, i.e. namespace and section), we don't really get much out of the more complicated interface except of this slight encouragement to use a unique namespace. Another possibility would be renaming the namespace argument in the ACS functions to section and to force selecting a namespace via a new lump.

See that's the thing - I don't necessarily want to ENFORCE separation between mods. If one mod wants to peek into another mod's namespace, I'd like to leave that option open. I just feel like the EASY thing that most mod makers will end up doing is assuming that they have exclusive access to the database all to themselves.

However, part of me also agrees with you about the additional parameters to each DB command that would require. Perhaps the best thing we can do is document the use of these functions and ensure that the documentation has the mod-specific namespace as part of the documentation. That way, people who "learn" by copypasting will get this extra namepsacing "for free".

> I think technically strings in ACS are something like a "handle" internally, but I'm not fond of the idea of introducing more handle types. As far as I can tell ACS is not designed for these kind of things. IMO the Prepare and GetPrepared functions I proposed are more in line with the limited scope of ACS.

What I was going to suggest is not necessarily a new type, but merely returning an integer that acted like any other number from the point of view of ACS, but could be passed to functions that would do the handle lookup at the time of the call.

I can see where you're coming from in terms of complexity, but I am still not a fan of your use of global state. Especially considering the number of different scopes available in ACS, I feel like this will end up footgunning ACS coders in unexpected ways when they try to use these API's in more than one scope at the same time. Are there any other instances in the ZDoom ACS API that global state is used...other than perhaps setting the currently in-use font?

If I had handles, I feel like I could...say...run a couple of queries on OPEN and then merely keep the handles around in order to easily refer back to data as-needed without having to waste time copying the entire dataset out of the database by hand.

Also, just an unrelated side note, I feel like providing constants along with the API like DB_ORDER_ASC/DB_ORDER_DESC would be a good idea, to make the function calls more readable by sight.
User avatar (0009137)
Hypnotoad (reporter)
2014-06-12 00:06

I'd just like to say that it's definitely useful to have full control over both a keyname and namespace as I've seen some question whether we should be allowed to specify namespace as a parameter in each function. The namespace gives an extra degree of freedom which makes it much easier to filter out data without having to rely too much on constantly constructing unique strings using strparam, plus it's very useful for sorting; how are you supposed to tell the database to sort only keys with say a MAP01_ prefix?
User avatar (0009158)
Torr Samaho (administrator)
2014-06-12 20:16

Quote from Watermelon

We also have to take into account hosting services like Best-Ever where people might maliciously try to write into other people's DB's.
How does one stop this?

I don't see any feasible way to introduce a permission system, but Best-Ever simply could make each custom server to use its own database by forcing how the CVAR databasefile is set.

Quote from AlexMax

However, part of me also agrees with you about the additional parameters to each DB command that would require. Perhaps the best thing we can do is document the use of these functions and ensure that the documentation has the mod-specific namespace as part of the documentation. That way, people who "learn" by copypasting will get this extra namepsacing "for free".

So just let the documentation encourage to uses namespaces like "zanrun-time" and leave it at this?

Quote from AlexMax

Are there any other instances in the ZDoom ACS API that global state is used...other than perhaps setting the currently in-use font?

Not really, but there is nothing really comparable to the kind of handle you suggest either. Extending the global state idea to a kind of handle that is just an int to ACS shouldn't be that complicated though. The biggest issue is how to free handles. A mod would need to do this explicitly.

Quote from AlexMax

Also, just an unrelated side note, I feel like providing constants along with the API like DB_ORDER_ASC/DB_ORDER_DESC would be a good idea, to make the function calls more readable by sight.

Yes, we should do this.

Quote from Jroc

I'd just like to say that it's definitely useful to have full control over both a keyname and namespace as I've seen some question whether we should be allowed to specify namespace as a parameter in each function.

We will definitely keep both. The only question is whether it's really necessary to add a third thing, e.g. section. I think it's not as long as modders use namespaces like "zanrun-time".
User avatar (0009181)
Hypnotoad (reporter)
2014-06-13 08:19

Can I request an "ignore parameter case" bool parameter for retrieving and setting DB entries? When retrieving, if this bool is true it will ignore the case and only match the letters when looking up namespace and keyname strings, and when setting it will force the namespace and keyname strings to be upper case. This not being the case currently has caused me a bunch of problems, for instance if you try to use n:PRINTNAME_LEVEL as part of your namespace, this will sometimes set it with a lowercase map lump name and sometimes upper case (of the same map lump) depending on certain circumstances, and so will set and retrieve different records depending on arbitrary stuff (such as whether you did map map01 or map MAP01 to change to that map).
User avatar (0009271)
Watermelon (developer)
2014-06-14 03:30
edited on: 2014-06-14 05:12

EDIT: Do Dusk's one

User avatar (0009273)
Dusk (developer)
2014-06-14 04:59
edited on: 2014-06-14 05:00

maybe this? Didn't compile this either, but needs just one strparam call


#define CASE_DIFFERENCE ('a' - 'A')
int g_toLowerBuffer[1024];

function str toLower (str s)
{
    int i, c;

    for (i = 0; str[i] != '\0'; ++i)
    {
        c = GetChar (str, i);

        if (c >= 'A' && c <= 'Z')
            g_toLowerBuffer[i] = c + CASE_DIFFERENCE;
        else
            g_toLowerBuffer[i] = c;
    }

    g_toLowerBuffer[i] = '\0';
    return strparam (a:g_toLowerBuffer);
}


User avatar (0009275)
Hypnotoad (reporter)
2014-06-14 06:02

Thanks, positron provided me with a strtoupper function but it requires bcc ('http://zandronum.com/forum/showthread.php?tid=4864 [^]' ) so I'm aware this stuff is possible. However many people might have no idea that some of the strings they are sending have inconsistent casing, I didn't catch this for a couple of days and it caused a lot of problems.
User avatar (0009277)
Hypnotoad (reporter)
2014-06-14 06:23
edited on: 2014-06-14 06:25

Also, can I request the public beta on zandro.com be updated with the DB features? We've had a lot of fun populating the database with our personal bests on zanrun IDL flagrunning (see here:'http://modenov.com/hypnotoad/test/ [^]' , click on map names and player names for more stats ), but a lot of people are unable to join due to the difficulty for them of obtaining a cutting edge build and getting it to work with doomseeker/ide.

User avatar (0009278)
Torr Samaho (administrator)
2014-06-14 07:52

Quote from Jroc
Also, can I request the public beta on zandro.com be updated with the DB features?
Sure. Is a beta of the latest 1.3 version sufficient or do you need features from 2.0?
User avatar (0009279)
Hypnotoad (reporter)
2014-06-14 10:04
edited on: 2014-06-14 11:59

I'm not sure, could you provide a 1.3 build of the windows binary and files so I can see if zanrun works on it? Also, will account features be available for it yet? Otherwise, some way to get a player's ip address will probably be needed before it's public because at the moment I'm only using the player's name which could be changed or someone else could impersonate; Watermelon said he'd be making a function for getting the IP as an int so hopefully this can be put in first before the public beta is updated.

edit: come to think of it, the latest version of 2.0 should be fine, since 2.0 is what's linked under the development builds anyway.

User avatar (0009283)
Torr Samaho (administrator)
2014-06-14 13:21
edited on: 2014-06-14 13:35

The current 1.3 repository version has the same account and database support that 2.0 has. I'd prefer a 1.3 beta release since we plan to release this as stable rather soon and this would give us some extra testing for 1.3.

I'll upload a windows binary you can test.

EDIT: Here is the current 1.3 Windows build.

User avatar (0009285)
Hypnotoad (reporter)
2014-06-14 14:04
edited on: 2014-06-14 14:14

Testing at the moment, database doesn't seem to be working at all now, not reading from the database file, can't narrow down the problem at the moment, will update if I find out more info.

edit: problem seems to be the namespace, this function:

SetDBEntryInt (maplump, "MapRecordTime", timec );
Where str maplump = strtoupper(strparam(n:PRINTNAME_LEVEL));

for some reason stores to a namespace of the player's name, rather than the name of the map lump as it did before.

edit2: so looks like n:PRINTNAME_LEVEL doesn't exist in 1.3 only 2.0. Can that be backported, or another function to determine the maplumps name be added?

User avatar (0009287)
Torr Samaho (administrator)
2014-06-14 14:12

Did you print the result of strtoupper(strparam(n:PRINTNAME_LEVEL)) to the console to make sure that the problem is really in SetDBEntryInt?
User avatar (0009288)
Hypnotoad (reporter)
2014-06-14 14:14

See my edit
User avatar (0009290)
Torr Samaho (administrator)
2014-06-14 14:33

So far I didn't plan any ZDoom backports for 1.3. We'd like to finish this version as soon as possible.
User avatar (0009291)
Hypnotoad (reporter)
2014-06-14 14:38

Well there definitely needs to be some way to get the maplump name, otherwise how can you get or set map specific stats? Dusk mentioned using levelnum but this only works for MAPXX format maps or if it's defined in mapinfo, and many mapsets don't use this naming format at all (and zanrun is designed to work for almost any ctf or survival mapset).
User avatar (0009292)
Torr Samaho (administrator)
2014-06-14 14:50

I see and looked at the necessary changes. Fortunately, they are isolated. Please try this binary.
User avatar (0009293)
Hypnotoad (reporter)
2014-06-14 15:02

Thanks, seems to be working so far.
User avatar (0009294)
Dusk (developer)
2014-06-14 15:31

Ticket reformatted.
User avatar (0009416)
Hypnotoad (reporter)
2014-06-15 15:13

Since this says needs testing, thought I'd just further re-iterate that after some more hefty zanrun sessions there seems to have been absolutely no problems with the build provided.
User avatar (0009498)
Watermelon (developer)
2014-06-16 00:22

How is the DB working? All well?
User avatar (0009510)
Hypnotoad (reporter)
2014-06-16 07:41

All is working well, but of course I'm being held back by lack of sorting and no way to uniquely identify players regardless of their on screen nick yet.
User avatar (0009516)
Konar6 (reporter)
2014-06-16 09:08

You will have to wait for the account system being operational or use names for now. IP address is not a way to identify players across sessions, it only identifies the current connection. IP addresses of some players change daily.
User avatar (0009542)
Hypnotoad (reporter)
2014-06-17 11:21
edited on: 2014-06-17 14:44

Another useful feature would be to have a method to get and store the date. It can be useful to know when certain stats, records etc.. were set, and how long they have been this way. It may also help further sort top 10 lists, as if multiple people get the same score, the person who got it first should be given priority.

edit: oh and on the topic of new features - I think along with the sorting, something like GetDBSortedPosition will be needed. It's all very well to be able to sort and get the top x entries, but what if you want to find the position of a specific entry? For instance, what if I want to display someone's personal best time on a map, and also display what position that time is in the rankings (e.g. Personal Best: 15.34s (5th)). Something like GetDBSortedPosition(str namespace, str keyname, bool ascending) or however the formatting would be with the prepare functions would be nice. This would return an int that is the position in the namespace the entry appears when sorted.

User avatar (0009543)
Watermelon (developer)
2014-06-17 14:44

For the date:
'http://zandronum.com/tracker/view.php?id=1501 [^]'
User avatar (0009554)
Torr Samaho (administrator)
2014-06-17 18:44

Quote from Jroc

I think along with the sorting, something like GetDBSortedPosition will be needed. It's all very well to be able to sort and get the top x entries, but what if you want to find the position of a specific entry

I see why you want this kind of function, but things are starting to get quite specialized here. I'll think about whether we want an ACS interface for this kind of query. Technically, it should be doable in SQLite by checking the value of the entry and then counting how many entries are not lower (or not higher) than the given value.

Quote from StrikerMan780
So, the database system only uses SQLite? Or is it possible to connect to an external MySQL database server? I kind of need to be able to connect to an external server if I'm gonna do what I plan to do.

So far our backend uses a local SQLite database. As long as you implement the functions necessary for the ACS interface, you can use any other database backend though. We tried to make the API independent of the specific database choice. Given the limitations of ACS it is important that the functions used by ACS return instantly. You can't use any kind of threading in ACS.
User avatar (0009556)
Hypnotoad (reporter)
2014-06-17 19:28

Well, I wouldn't say it's that specialized. Player rankings will be one of the main uses of this database feature, there are already plans to add rankings for duel and ctf players.
User avatar (0009557)
Catastrophe (reporter)
2014-06-17 20:12

Hey, I've begun documenting some of the new functions, here:'http://wiki.zandronum.com/SetDBEntryInt, [^]' and here:'http://wiki.zandronum.com/GetDBEntryInt. [^]'

Is there documentation anywhere on the other commands? Also what should I add to my ACC to get this working?
User avatar (0009558)
Torr Samaho (administrator)
2014-06-17 20:17

The necessary zspecial.acs entries to add the new functions to ACC are

    -108:SetDBEntryInt(3),
    -109:GetDBEntryInt(2),
    -110:SetDBEntryString(3),
    -111:GetDBEntryString(2),
    -112:IncrementDBEntryInt(3),
    -113:PlayerIsLoggedIn(1),
    -114:GetPlayerAccountName(1),

The only documentation on them is what I wrote in the tracker tickets and the commit messages.
User avatar (0009559)
StrikerMan780 (reporter)
2014-06-17 20:50
edited on: 2014-06-17 20:54

@Torr: How would I connect it to a MySQL server then? Any examples?

About threading... Hate always using it to compare with, but SourcePawn cannot use threading either. However, they get around that by using Callbacks.

In that, you can call a function with several parameters. First being the database handle, the second being the query, and the third being what Script to use as a callback. The function returns immediately, and the rest of whatever script is running will continue to execute. Once the query is successfully completed, it executes another script (the one you defined in the function) where you can work with the results of the query.

Would a system like this work?

User avatar (0009562)
Torr Samaho (administrator)
2014-06-17 21:09

Quote from StrikerMan780
How would I connect it to a MySQL server then? Any examples?
I have never connected to a MySQL server with C/C++, so I don't know. To use MySQL, the following internal Zandronum functions need to be implemented based on MySQL:

void DATABASE_SaveSetEntry ( const char *Namespace, const char *EntryName, const char *EntryValue );
void DATABASE_SaveSetEntryInt ( const char *Namespace, const char *EntryName, int EntryValue );
FString DATABASE_SaveGetEntry ( const char *Namespace, const char *EntryName );
void DATABASE_SaveIncrementEntryInt ( const char *Namespace, const char *EntryName, int Increment );


Quote from StrikerMan780

About threading... Hate always using it to compare with, but SourcePawn cannot use threading either. However, they get around that by using Callbacks.

Well, in theory you could probably define an ACS script as callback, but then you'll still need to somehow make Zandronum execute the query in a thread that will call the ACS script once the query is finished. This is completely incompatible with our ACS database API though.
User avatar (0009564)
AlexMax (developer)
2014-06-17 22:52

Coming back to some database concerns.

> So just let the documentation encourage to uses namespaces like "zanrun-time" and leave it at this?

Yeah.

> Not really, but there is nothing really comparable to the kind of handle you suggest either. Extending the global state idea to a kind of handle that is just an int to ACS shouldn't be that complicated though. The biggest issue is how to free handles. A mod would need to do this explicitly.

Yeah, that's the downside of having to manage handles yourself. On the other hand, with the global function if you want data to stick around you have to copy it out of the returned dataset by hand.

> Well, in theory you could probably define an ACS script as callback, but then you'll still need to somehow make Zandronum execute the query in a thread that will call the ACS script once the query is finished. This is completely incompatible with our ACS database API though.

Also, as soon as you start adding other databases to the mix, there's the possibility that the connection to them might be interrupted. What happens then...does the gamesim stop the world while your database query tries to execute?

Sigh, one of the big reasons I wanted to keep a simple API is because trying to get a real database API working means you have to solve all of these hard problems with a scripting language like ACS.

Does anybody have a list of things that coders want out of the database API that they're not getting as it stands? Not function names, but actual functionality? I know that Hypnotoad wants to be able to retrieve a sorted list of data and to be able to pull individual rows out of that sorted list...is there anything else that I missed in the gigantic thread?
User avatar (0009565)
StrikerMan780 (reporter)
2014-06-17 23:16
edited on: 2014-06-18 19:05

Sourcemod made calls to a seperate DLL module (dbi.mysql.ext.dll, which handles everything MySQL.), which would tell the core to run the script.

With Zandronum on the other hand, Is there a way to get an external library like that to tell Zandro to execute an ACS Script? or is it not capable of that whatsoever?

(I keep bringing Sourcemod up because it's familiar to me, it's very similar to ACS, and I can explain things easier through example. It has a very good system for handling Databases, and think it would be ideal to follow similar concepts.)

> Also, as soon as you start adding other databases to the mix, there's the possibility that the connection to them might be interrupted. What happens then...does the gamesim stop the world while your database query tries to execute?

Probably return blank query results, and parameter used to determine whether or not it failed, allowing the modder to choose how to react to the situation. The game would keep going as it's trying to connect. That's the whole idea of using a callback instead of just waiting for the function to return.


The only other thing I can think of... is some form of MySQL proxy. Having all stuff done via SQLite, and then having some sort of external system that synchronizes the databases periodically, merging changes/differences.

User avatar (0009569)
Hypnotoad (reporter)
2014-06-18 09:27

>is there anything else that I missed in the gigantic thread?

Well, I think someone mentioned a way to get the keyname from an entry value and namespace, which would save you from having to do some silly stuff.

GetDBEntryKeyfromString(str namespace, str entry), GetDBEntryKeyfromInt(str namespace, int entry) ?
User avatar (0009590)
AlexMax (developer)
2014-06-19 06:51
edited on: 2014-06-19 06:51

> Well, I think someone mentioned a way to get the keyname from an entry value and namespace, which would save you from having to do some silly stuff.

Keys are designed to be unique. Values are not. If you want a reverse mapping, just put the key/value pair in the database twice - once one way and once the other.

User avatar (0009591)
Hypnotoad (reporter)
2014-06-19 07:30

That's what I'm doing currently it just seems a bit silly. Nevermind.
User avatar (0009595)
Torr Samaho (administrator)
2014-06-19 17:51

Quote from Jroc
Player rankings will be one of the main uses of this database feature, there are already plans to add rankings for duel and ctf players.

Alright, I added the internal function DATABASE_GetEntryRank and will expose this via ACS.

Quote from StrikerMan780
With Zandronum on the other hand, Is there a way to get an external library like that to tell Zandro to execute an ACS Script? or is it not capable of that whatsoever?

The only way to communicate with Zandronum from the outside so far is via RCON.

Quote from StrikerMan780
Probably return blank query results, and parameter used to determine whether or not it failed, allowing the modder to choose how to react to the situation. The game would keep going as it's trying to connect. That's the whole idea of using a callback instead of just waiting for the function to return.

As I said, callbacks would require a complete redesign of the ACS database interface and would make the whole thing more complicated to use. Not to mention that ACS is not designed for this kind of approach.

Quote from StrikerMan780

The only other thing I can think of... is some form of MySQL proxy. Having all stuff done via SQLite, and then having some sort of external system that synchronizes the databases periodically, merging changes/differences.

I think that this is the most feasible approach. If one would accept that the "get" functions sometimes return outdated values, Zandronum could keep a local copy of the external database, use that to handle "get" and send all "set" and "increments" to the external database. The fact that "get" may return an outdated value is a pretty bad design though.

Quote from AlexMax
Keys are designed to be unique. Values are not. If you want a reverse mapping, just put the key/value pair in the database twice - once one way and once the other.

Which will also only work if the value is unique.

Anyway, since Zandronum will not assume the uniqueness of values, GetDBEntryKeyfromString/Int doesn't make sense (unless you want a list of all entries with that value).
User avatar (0009601)
Hypnotoad (reporter)
2014-06-20 11:48

Quote
Alright, I added the internal function DATABASE_GetEntryRank and will expose this via ACS.


Thanks, so assuming the prepare sort functions or something similar will also be implemented(?) all we need now is accounts and then as far as I'm concerned we have all that's needed.
User avatar (0009602)
Hypnotoad (reporter)
2014-06-20 11:50
edited on: 2014-06-20 11:51

Oh I forgot to mention I had another idea. While the devs are against exposing the time/date to acs, I think what you could safely do is save a timestamp every time an entry is written which is stored in a new column in the database, the column would be inaccessible via acs, but would exist in the database so that websites and other things can make use of it.

User avatar (0009627)
StrikerMan780 (reporter)
2014-06-21 18:17
edited on: 2014-06-21 18:23

"As I said, callbacks would require a complete redesign of the ACS database interface and would make the whole thing more complicated to use. Not to mention that ACS is not designed for this kind of approach."

No way to get ACS Functions to call things that don't reside in the ACS System in and of itself to break free of whatever limitations ACS may have? (Be it threading, or w/e.) Also, how would it be that much more complicated, if at all? I'm kind of modeling my approach after this:'https://wiki.alliedmods.net/SQL_%28SourceMod_Scripting%29 [^]'

"I think that this is the most feasible approach. If one would accept that the "get" functions sometimes return outdated values, Zandronum could keep a local copy of the external database, use that to handle "get" and send all "set" and "increments" to the external database. The fact that "get" may return an outdated value is a pretty bad design though."

Perhaps there's a way to get a timestamp / last modified time of sorts? There must be some way to determine if the values in the database are outdated.

User avatar (0009629)
Torr Samaho (administrator)
2014-06-21 19:08
edited on: 2014-06-21 19:09

Quote from StrikerMan780
No way to get ACS Functions to call things that don't reside in the ACS System in and of itself to break free of whatever limitations ACS may have?

There are a lot of things you could hack into ACS, but that doesn't necessarily mean that it's a good idea in the long run.

Quote from StrikerMan780
Perhaps there's a way to get a timestamp / last modified time of sorts? There must be some way to determine if the values in the database are outdated.

I think it should be no problem to add a fourth column to the table with the time the entry was last changed. This would also resolve Jroc's request: 0001831:0009602

The timestamp may not be exposed via ACS though, otherwise we run into 0001501:0009497. Nevertheless, you could have another program monitor the database and do things based on the timestamp (like synchronizing the data with another db).

User avatar (0009648)
Torr Samaho (administrator)
2014-06-22 11:43

I continued working on the database support. In addition to some changes in how Zandronum internally queries the SQLite database (increment is a single query now instead of a get / set query pair that is marked as exclusive transaction), Zandronum now maintains a fourth column in the database table that stores the unix epoch in seconds (including the fractional part) the last time the entry was changed. This column is inaccessible via ACS and meant to be used by external programs that access the SQLite database directly.

Furthermore, I finished a first version of the sorting related database ACS functions and added the following new functions:

    int QuerySortedDBEntries ( string Namespace, int N, int Offset, bool Ascending ),
    int GetDBResultSize ( int Handle ),
    void ClearDBResultHandle ( int Handle ),
    string GetDBResultEntryNameString ( int Handle, int ResultsIndex ),
    string GetDBResultEntryValueString ( int Handle, int ResultsIndex ),
    int GetDBEntryRank ( string Namespace, string EntryName, bool Ascending ),

Here is a binary that supports them. You'll need to put

    -115:QuerySortedDBEntries(4),
    -116:GetDBResultSize(1),
    -117:ClearDBResultHandle(1),
    -118:GetDBResultEntryNameString(2),
    -119:GetDBResultEntryValueString(2),
    -120:GetDBEntryRank(3),

into zspecial.acs to use them. This is all very experimental and needs to be tested thoroughly.
User avatar (0009649)
Hypnotoad (reporter)
2014-06-22 12:16

This is great thanks! I'm just wondering though if you could help me with understanding how these new functions work.
The first function, what does the offset argument do exactly?
Where exactly do you get the handle from, is the handle created when you assign the first function to a variable?
Does "EntryName" in the 4th and 6th functions refer to the keyname?
Does the 5th function return a string from a sorted set of strings? How did you sort them if so?
User avatar (0009650)
Torr Samaho (administrator)
2014-06-22 12:30

The int returned by QuerySortedDBEntries is the handle you need to access the results of the query. Here is an example that gets and prints the top 3 entries (with ascending order, i.e. lowest is best) of the namespace named "Random":

int handle = QuerySortedDBEntries ( "Random", 3, 0, 1 );
int numEntries = GetDBResultSize(handle);
Log (s:"Query returned entries: ", d:numEntries );
for ( int i = 0; i < numEntries; ++i )
{
    Log ( s:GetDBResultEntryNameString(handle, i), s:" ", s:GetDBResultEntryValueString(handle, i) );
}
ClearDBResultHandle ( handle );

The offset parameter allows you to start with a different entry than the top entry, i.e. with an offset QuerySortedDBEntries will not return entries 1,...N, but 1+offset,...,N+offset.
Yes, EntryName means keyname.

I hope the example also answers your question regarding the 5th function. If you have more questions, feel free to ask.
User avatar (0009651)
Hypnotoad (reporter)
2014-06-22 13:13

Thanks, this basically answers all of my questions, but why isn't there a function to return an entry value as an int rather than a string? True I can just use GetDBResultEntryNameString in GetDBEntryInt, but I can do that with GetDBEntryString too, so how come there is a function specifically to get the string?
User avatar (0009652)
Dusk (developer)
2014-06-22 13:16

Looks like we could use an ACS constant for query sort order? That is, if I'm reading the QuerySortedDBEntries call correctly.
User avatar (0009653)
Torr Samaho (administrator)
2014-06-22 13:24
edited on: 2014-06-22 13:31

Quote from Jroc
Thanks, this basically answers all of my questions, but why isn't there a function to return an entry value as an int rather than a string? True I can just use GetDBResultEntryNameString in GetDBEntryInt, but I can do that with GetDBEntryString too, so how come there is a function specifically to get the string?
I added the string version because the value is stored internally as string and thus the string version is more general. It would be no problem to add GetDBResultEntryNameInt though.

Quote from Dusk
Looks like we could use an ACS constant for query sort order? That is, if I'm reading the QuerySortedDBEntries call correctly.

Yes, we should add a constant for the search order. AlexMax proposed DB_ORDER_ASC/DB_ORDER_DESC in 0001831:0009134.

EDIT: I added

// Zandronum database additions ---------------------------------------------
#define DB_ORDER_DESC 0
#define DB_ORDER_ASC 1

to zdefs.acs.

User avatar (0009656)
Hypnotoad (reporter)
2014-06-22 14:01

Just wondering, what exactly are the consequences if I fail to do ClearDBResultHandle.
User avatar (0009657)
Torr Samaho (administrator)
2014-06-22 14:05

The results of the query will stick around in memory. I added code that is supposed to clear them when moving to the next map, but didn't test yet if it works.
User avatar (0009658)
Dusk (developer)
2014-06-22 14:07
edited on: 2014-06-22 14:08

Perhaps the query could be automatically cleared when the script terminates?

User avatar (0009659)
Torr Samaho (administrator)
2014-06-22 14:16

The handle is in no way tied to the script that called the function creating the handle. Nobody stops you from storing the handle in a global var or from handing it to other scripts. AFAIK the general practice for handles is the same as for pointers: The one who created it is responsible for freeing it.
User avatar (0009660)
Hypnotoad (reporter)
2014-06-22 14:44
edited on: 2014-06-22 14:45

I'm getting errors now when attempting to set new database values, previously it worked fine in the old binary:

Could not prepare statement. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not execute statement. Error: table Zandronum has 3 columns but 4 values were supplied
Could not prepare statement. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not bind text. Error: table Zandronum has 3 columns but 4 values were supplied
Could not execute statement. Error: table Zandronum has 3 columns but 4 values were supplied

Do I need to start with a brand new database? Can the old one be modified to add a 4th column?

User avatar (0009661)
Torr Samaho (administrator)
2014-06-22 14:52

Did you create the table with the old binary? Then it lacks the timestamp column and thus only has three columns. You need to delete the database. Zandronum will then automatically create a new table with four columns.
User avatar (0009662)
Hypnotoad (reporter)
2014-06-22 14:55

I've managed to add the fourth column to the existing database myself, it seems to work for now.
User avatar (0009663)
Torr Samaho (administrator)
2014-06-22 15:24

BTW: The interface I created so far (0001831:0009648) is not necessarily final. If anybody has suggestions on how to improve it, I'm all ears.

One I idea I had about the handles: Instead of having QuerySortedDBEntries (or similar functions we may add in the future) create a new handle every time it is called, we could separate the allocation into a new function, e.g. NewDBResultHandle and supply the handle as argument to QuerySortedDBEntries. While this makes the code a little longer, it would make more clear that you are allocating something that has to be freed.
User avatar (0009664)
Hypnotoad (reporter)
2014-06-22 15:32
edited on: 2014-06-22 15:34

Regarding GetDBEntryRank, how does it sort among entries that have exactly the same value? Suppose the 2nd lowest value is achieved twice by two entries, will it consider them "joint 2nd" for instance so return both of them as 2? If not, how does it decide which is 2nd and which is 3rd, is it based on the timestamp? Would it be possible to add a parameter which allows you to specify what to do in this situation, whether to rank by timestamp (or whatever is currently default), giving them the same lowest possible rank (e.g. both 2) or giving them both the highest possible rank (e.g. both 3)?

So the function could be changed to:

GetDBEntryRank ( string Namespace, string EntryName, bool Ascending, int ranktype)

And you could add constants for the last parameter corresponding to 3 options, e.g:

#define DB_RANK_TIES_BY_TIMESTAMP 0
#define DB_RANK_TIES_AS_LOWEST_POSSIBLE 1
#define DB_RANK_TIES_AS_HIGHEST_POSSIBLE 2

User avatar (0009665)
Torr Samaho (administrator)
2014-06-22 15:40

GetDBEntryRank counts how many entries have a strictly lower (ascending case, strictly higher for descending) value and returns that number + 1. So, in your case, if multiple entries have the second lowest value, they are considered "join 2nd". In theory, we could add another parameter to configure how ties are handled, but here we are starting to get too specialized for my taste.
User avatar (0009667)
Hypnotoad (reporter)
2014-06-22 15:47

So just to be clear, if you have 11 entries, one with value 10, and the remaining 10 with a value of 20, all of those remaining 10 entries will return a rank of 2 (assuming ascending)?
User avatar (0009668)
Torr Samaho (administrator)
2014-06-22 15:51

At least that what's supposed to happen. Considering how little testing the ranking code received so far, I don't give any guarantees that it actually does happen ;).
User avatar (0009669)
Hypnotoad (reporter)
2014-06-22 16:11

Well, that behavior suits my needs for now fortunately.

As for the interface, I personally think it's fine how it is now, but a function to get an int rather than string would probably be convenient.
User avatar (0009672)
AlexMax (developer)
2014-06-22 17:22

> The results of the query will stick around in memory. I added code that is supposed to clear them when moving to the next map, but didn't test yet if it works.

How does this work with global ACS scripts?
User avatar (0009673)
Torr Samaho (administrator)
2014-06-22 17:29

Good question. Now that I think about it, if you store the handle in a global var it would become invalid. I'll change it so that the handles are only cleared when the global ACS vars are reset.
User avatar (0009674)
AlexMax (developer)
2014-06-22 17:46

I've gone over the new ACS functions and tried to normalize and simplify their names:

void SetDBEntry(string namespace, string key, int value);
int GetDBEntry(string namespace, string key);
void IncrementDBEntry(string namespace, string key);
void SetDBEntryString(string namespace, string key, string value);
string GetDBEntryString(string namespace, string key);

int SortDBEntries(string namespace, int limit, int offset, bool descending);
int CountDBResults(int handle);
void FreeDBResults(int handle);
int GetDBResultKey(int handle, int index);
int GetDBResultValue(int handle, int index);
string GetDBResultKeyString(int handle, int index);
string GetDBResultValueString(int handle, int index);

int SortedDBEntryPosition(string namespace, string key, bool descending);

A few notes:
- I have removed the "Int" from any integer-related functions, in order to be consistent with the CVar functions.
- Ascending sorting is the "default" i.e. if you use 0 instead of a named constant. This is in line with how SQL works.
User avatar (0009717)
Torr Samaho (administrator)
2014-06-23 19:13

The int returned by SortedDBEntryPosition is not the same as the position in the results returned by SortDBEntries, so I'd rather keep a bigger difference in the name of the function. IMO "Rank" should be in the name.
User avatar (0009794)
Hypnotoad (reporter)
2014-06-29 20:17

Could there be a flag that can enable or disable the database features, and can it be off by default? I think a malicious wad could fill up a database with thousands of entries of gibberish every tic as soon as you run it which would be harmful. Something like sv_enabledatabase.
User avatar (0009798)
ZzZombo (reporter)
2014-06-30 01:24

sv_db_maxsize <0 for unlimited storage | positive integer otherwise>
User avatar (0009801)
Torr Samaho (administrator)
2014-06-30 06:06
edited on: 2014-06-30 06:06

Quote from Jroc

Could there be a flag that can enable or disable the database features, and can it be off by default?
To prevent this the DB already defaults to volatile in-memory storage. Isn't this enough? Sure, a mod could clog the RAM, but there are already many ways for a mod to make the game unplayable.

User avatar (0009803)
Hypnotoad (reporter)
2014-06-30 14:01

It seems like it archives the value whenever you set it. If I host a server and specify a database file on that, then if I run a wad in singleplayer afterwards that will still be the database file.
User avatar (0009804)
Torr Samaho (administrator)
2014-06-30 17:35

Yes, the CVAR databasefile is archived intentionally.
User avatar (0009822)
Hypnotoad (reporter)
2014-07-02 18:29

Okay, just seems a bit dangerous if a user forgets to change it back after hosting a server.

Anyway, since it seems as if the accounts system will take longer than thought, is it possible the public beta of 1.3/new 2.0 can be updated without it for now? I can just keep using player names for the time being.
User avatar (0009823)
Catastrophe (reporter)
2014-07-02 18:39

No, please release 1.3 with the account system. I am willing to wait.
User avatar (0009824)
Torr Samaho (administrator)
2014-07-02 20:07

AlexMax and I got the planned changes on the account protocol done on Sunday evening. I'm just polishing a few things and plan to release the 1.3 beta soon (probably tomorrow).
User avatar (0009933)
Hypnotoad (reporter)
2014-07-11 13:19

It doesn't look like any server is using the current 1.3 beta at the moment. I don't know about other modders, but if a version of 1.3 beta with sorting function is released I'll be able to get a Zanrun (and possibly another mod I'm working on) server up and running so that the new account/db features can get a more thorough testing.
User avatar (0009944)
Torr Samaho (administrator)
2014-07-12 14:33
edited on: 2014-07-12 14:33

Made a bunch of changes and ported them to 1.3:
- Added new CVAR database_maxpagecount (defaults to 32768) that controls the maximal allowed size in pages of the database.
- Added new ACS functions SortDBEntries, CountDBResults, FreeDBResults, GetDBResultKeyString, GetDBResultValueString, GetDBResultValue and GetDBEntryRank.

This covers the renaming and the bool changes AlexMax suggested, except that I kept the name GetDBEntryRank (see 0001831:0009717) and didn't add GetDBResultKey. The key is supposed to be a string (all functions only allow a string argument for the key), so I don't think that it would make sense to return the key as int.

User avatar (0009950)
Hypnotoad (reporter)
2014-07-12 16:33

Awesome, what do I need to add to my zspecial.acs? Are the numbers the same as last time?
User avatar (0009951)
Torr Samaho (administrator)
2014-07-12 16:57

zspecials.acs

    -115:SortDBEntries(4),
    -116:CountDBResults(1),
    -117:FreeDBResults(1),
    -118:GetDBResultKeyString(2),
    -119:GetDBResultValueString(2),
    -120:GetDBResultValue(2),
    -121:GetDBEntryRank(3),

zdefs.acs

#define DB_ORDER_ASC 0
#define DB_ORDER_DESC 1
User avatar (0009955)
Hypnotoad (reporter)
2014-07-12 20:10
edited on: 2014-07-12 22:15

Thanks, everything seems to work fine.

edit: will a new public beta be released with this stuff?

User avatar (0010074)
Hypnotoad (reporter)
2014-07-29 08:31
edited on: 2014-07-29 08:51

So it appears too much database activity can cause major cpu load, causing the server to hang and player's ping to skyrocket temporarily. This is currently very difficult to reproduce, as this seems to only happen sometimes. I'm looking for ways to optimize the script at the moment, I'm pretty sure this is the script which causes the occasional cpu load:'http://pastebin.com/syicm01N [^]' , however not that much is being written to the database, one entry per player in the database so in the test we jut did there was only 3 people in the database so it only wrote 3 times. The loop is mostsly just checking the DB entries, and I don't see why that should cause so much cpu load.

edit: this loop is another candidate'http://pastebin.com/2b5MEgmq [^]' but I'm pretty sure from the testing I've done that it's not this.

User avatar (0010091)
Torr Samaho (administrator)
2014-07-29 21:05

For instance, calling GetDBEntryString ("MapNames", mapid ) thrice instead of only once and saving the result in script 463 certainly doesn't improve performance ;).

What exactly is script 463 supposed to do? Since I have no idea what you are saving, I don't know what kind of logic you are following. I just noticed that you have playerid, playerid2 and playerid3 and all seem to be exactly the same.
User avatar (0010092)
Hypnotoad (reporter)
2014-07-29 21:24
edited on: 2014-07-29 21:27

Playerid, playerid2 and playerid3 are the same because they occur at different times, and strparam (really really annoyingly) only lasts for one tic. There is a specific reason that they had to be 3 unique variables for it to work properly, rather than constantly reassigning the same variable, but I can't recall now, but some kind of ACS limitation or bug forced me to I believe, it shouldn't really affect performance regardless though.

This is what the script does, it accumulates all the points for a player. Every time someone gets a personal best time, they may improve their rank on that map, you are awarded points depending on your rank for that map; if your rank goes up you earn more points, if someone else overtakes you causing your rank to go down you then lose points. The latter script in the edit checks whether each player in the database has changed in rank for that map, then changes their point score for that map, it then executes 463 which tally's up all the points for each player by adding all of their points from each map to provide a total points score by which players are globally ranked.

I've managed to completely bypass this script for now, what happens instead now is that when a player's point score for one map is changed, their previous score is taken away from their totalpoints and then the new score is added, rather than having to tally up all the points each time. I think this has improved performance somewhat but it's hard to tell due to the rarity of performance drops, I have noticed the occasional ping spike still which I cannot rule out as coincidental but has happened occasionally enough (this may change when lots of people play at the same time) that it's hard to know for sure exactly what causes it.

Perhaps some guidance on how many times Zandro can query the database in one tic before performance is affected would be helpful.

User avatar (0010098)
Torr Samaho (administrator)
2014-07-30 21:21

Quote from Jroc

Perhaps some guidance on how many times Zandro can query the database in one tic before performance is affected would be helpful.
AFAIK nobody has ever used Zandronum's database backend for anything performance critical, so there is no experience with this yet.

What I see from your script though is a limitation of the current database interface that makes you spam the database with unnecessary calls. For instance, the outer loop is supposed to iterate over all entries in the namespace "PlayerNames", isn't it? Ignoring the inner loop completely, the outer loop leads to 2*MAXIDS database queries (with my comment above you can cut this down to MAXIDS queries) no matter how many entries are in the namespace. It would be much better to add a database function that just iterates over all entries in a namespace directly (like the function that iterates over the top N entries).
User avatar (0010099)
Hypnotoad (reporter)
2014-07-30 23:11

FYI it doesn't iterate over all of MAXIDS, only over the amount of playerids in the PlayerNames namespace, and will break out of the loop as soon as it encounters no player name recorded for the id. Since there were about 3 or 4 players in the database at the time of that initial test, this would have been 8 queries in the outer loop.

But yes, being able to iterate over all entries in a namespace would be handy.
User avatar (0010223)
Hypnotoad (reporter)
2014-08-23 20:18
edited on: 2014-08-23 20:19

Sorry for yet another feature request but I thought it'd be worth a shot:

I was looking into using the database to make wads that record players wins and losses, particularly in gamemodes like duel, but also in ctf, and then use these stats to create rankings/league tables. But I've ran into a snag, in order to properly rank players in a gamemode that doesn't have a fixed amount of matches/players you generally need some kind of special algorithm to award or reduce a player's points. Most notable is the Elo ranking, which is used in chess, as well as other sports and some video games. Ranking players by the number of wins, or the ratio of wins to losses for instance, is simply not suitable as it doesn't take into account the quality of players' opponents, or the differences in quantity of games played. Therefore it seems that it is impossible to properly rank players without using an algorithm like this.

The problem is, algorithms like Elo are a bit complex mathematically, enough to basically be impossible or extremely difficult to implement in acs. So far the only way one would be able to do a proper ranking would be to make some external application to perform calculations on the database.

What would be extremely great is if a new function could be created for Zandronum, you supply it with three numbers and a bool (the rating of the player, the rating of his opponent, the K-factor, and whether the player won or lost), and it then internally calculates how many points to award or take away from the player using the elo algorithm (or something similar), which should be much more straightforward to implement using a proper programing language, and returns that number. This would be really great for Zandronum as it would enable people to easily add player rankings to their mod if it involves competitive PvP play.

User avatar (0010224)
AlexMax (developer)
2014-08-23 21:52

IMHO if the math to do a particular ranking system is too complicated to implement in ACS, then it's probably a better idea to leave the idea of a ranking system alone and try an alternative, or simply rank them by number of wins or something simple.
User avatar (0010225)
Hypnotoad (reporter)
2014-08-23 22:35
edited on: 2014-08-23 23:02

It is hard in acs because acs does not have floating point, but I believe it would not be difficult to hardcode into Zandronum. Ranking simply by number of wins does not mean much, it gives an advantage to people who have played longer, and it does not take into account the quality of the opposition. I could just play a clone of myself, or a friend, hundreds of times and lead the ranking in number of wins without having to face difficult opposition, it wouldn't be a true ranking.

edit: to be clear, it's not difficult or complicated in the sense that it requires a lot of number crunching or to sift through lots of data, it's only "difficult" because doing something like A' = A + K*(S - ( 1/(1 + 10^((x-y)/400)))) (this is the elo formula itself) is really hard with fixed point only.

User avatar (0010226)
Torr Samaho (administrator)
2014-08-24 16:32

I agree that it would be good if mods could do Elo like ranking. Is it already possible to do the necessary calculations in ACS with the latest ZDoom version? If so, we can talk about backporting the necessary ACS additions.
User avatar (0010227)
Hypnotoad (reporter)
2014-08-24 16:53

It wouldn't be any easier to do the calculations in acs using the latest zdoom, as far as i know, I didn't see any new mathematical functions on the wiki that would be of use.
User avatar (0010228)
Torr Samaho (administrator)
2014-08-24 18:48

In that case, I wouldn't mind adding one such function. We have to make sure though that we chose a rating system the majority of the players accepts.
User avatar (0010229)
Hypnotoad (reporter)
2014-08-24 18:57
edited on: 2014-08-24 19:09

I think the standard Elo is probably the simplest/easiest to implement, but apparently there are better systems, like Microsoft's TrueSkill:'http://research.microsoft.com/en-us/projects/trueskill/details.aspx [^]'

edit: nevermind trueskill, it's closed source

edit2: on the other hand many people have implemented their own verion:'https://github.com/search?utf8=%E2%9C%93&q=trueskill [^]'

User avatar (0010231)
Hypnotoad (reporter)
2014-08-24 20:03
edited on: 2014-08-24 20:56

I just noticed Quake Live Ranks uses ELO'http://www.qlranks.com/ [^]' and this seems to be a popular resource, so since Elo is well accepted by the quake community I don't see why it wouldn't be accepted by the Zandro community.

User avatar (0010234)
Blzut3 (administrator)
2014-08-25 07:00

Jroc:'http://pastebin.com/SdzVXgrF [^]'

Give that a try. The MAXELO constant isn't quite right due to potential signed integer overflow I think. But the general idea there is sacrificing a little bit of range gives a few more bits of precision allowing it to be done entirely in ACS. From what I understand elo typically ranges between 0-3000.
User avatar (0010235)
Hypnotoad (reporter)
2014-08-25 12:16

Wow, thanks a lot for this, this is really great! So far it seems to be working fairly well, the only problem is it seems to always round down the result, but then this may not be a bad thing if you want to keep Elo changes slightly conservative? Do you know if there is an optimal K-value and/or starting values that this library will work best with?
User avatar (0010237)
Hypnotoad (reporter)
2014-08-25 17:34

Actually this rounding is more serious than I thought, it seems that you will often (practically always) lose more points than you will gain. If I gain 2, the other person will usually lose 3 for instance.
User avatar (0010238)
Blzut3 (administrator)
2014-08-25 19:06

I believe you can fix that with: return ra + ((k*((w<<16) - e) + 0.5)>>16);

If negative values need special handling, remember you can ultimately replace that right shift 16 with a rounding function of your choice. At the typical k values, I do not believe any possible loss of fractional precision for e makes any practical difference so it's just a matter of how you decide to do the conversion to a normal integer (in the code I pastebin'd it's simple truncation).

I believe the safe input range for r is [0-3130] assuming my thoughts about an overflow going to 3251 is correct. For k it should work up to 32767, but that would be useless.
User avatar (0010245)
Torr Samaho (administrator)
2014-08-31 20:05
edited on: 2014-08-31 20:05

For the record, I added the new ACS functions BeginDBTransaction, EndDBTransaction and GetDBEntries.

User avatar (0010347)
Dusk (developer)
2014-10-05 22:22

Marking as resolved as 1.3 is now released.

Issue Community Support
This issue is already marked as resolved.
If you feel that is not the case, please reopen it and explain why.
Supporters: No one explicitly supports this issue yet.
Opponents: No one explicitly opposes this issue yet.

- Issue History
Date Modified Username Field Change
2014-06-11 21:54 Watermelon New Issue
2014-06-11 21:54 Watermelon Relationship added related to 0001176
2014-06-11 21:57 Watermelon Note Added: 0009129
2014-06-11 22:32 AlexMax Note Added: 0009134
2014-06-12 00:06 Hypnotoad Note Added: 0009137
2014-06-12 20:16 Torr Samaho Note Added: 0009158
2014-06-13 08:19 Hypnotoad Note Added: 0009181
2014-06-14 03:30 Watermelon Note Added: 0009271
2014-06-14 03:31 Watermelon Note Edited: 0009271 View Revisions
2014-06-14 03:31 Watermelon Note Edited: 0009271 View Revisions
2014-06-14 03:32 Watermelon Note Edited: 0009271 View Revisions
2014-06-14 04:59 Dusk Note Added: 0009273
2014-06-14 05:00 Dusk Note Edited: 0009273 View Revisions
2014-06-14 05:12 Watermelon Note Edited: 0009271 View Revisions
2014-06-14 06:02 Hypnotoad Note Added: 0009275
2014-06-14 06:23 Hypnotoad Note Added: 0009277
2014-06-14 06:24 Hypnotoad Note Edited: 0009277 View Revisions
2014-06-14 06:25 Hypnotoad Note Edited: 0009277 View Revisions
2014-06-14 06:25 Hypnotoad Note Edited: 0009277 View Revisions
2014-06-14 07:52 Torr Samaho Note Added: 0009278
2014-06-14 10:04 Hypnotoad Note Added: 0009279
2014-06-14 11:59 Hypnotoad Note Edited: 0009279 View Revisions
2014-06-14 13:21 Torr Samaho Note Added: 0009283
2014-06-14 13:35 Torr Samaho Note Edited: 0009283 View Revisions
2014-06-14 14:04 Hypnotoad Note Added: 0009285
2014-06-14 14:09 Hypnotoad Note Edited: 0009285 View Revisions
2014-06-14 14:12 Torr Samaho Note Added: 0009287
2014-06-14 14:14 Hypnotoad Note Edited: 0009285 View Revisions
2014-06-14 14:14 Hypnotoad Note Added: 0009288
2014-06-14 14:33 Torr Samaho Note Added: 0009290
2014-06-14 14:38 Hypnotoad Note Added: 0009291
2014-06-14 14:50 Torr Samaho Note Added: 0009292
2014-06-14 15:02 Hypnotoad Note Added: 0009293
2014-06-14 15:31 Dusk Note Added: 0009294
2014-06-14 15:31 Dusk Assigned To => Torr Samaho
2014-06-14 15:31 Dusk Status new => needs testing
2014-06-14 15:31 Dusk Summary Database discussion => SQLite database for persistent data storage
2014-06-15 15:13 Hypnotoad Note Added: 0009416
2014-06-16 00:22 Watermelon Note Added: 0009498
2014-06-16 07:41 Hypnotoad Note Added: 0009510
2014-06-16 09:08 Konar6 Note Added: 0009516
2014-06-17 11:21 Hypnotoad Note Added: 0009542
2014-06-17 14:44 Hypnotoad Note Edited: 0009542 View Revisions
2014-06-17 14:44 Watermelon Note Added: 0009543
2014-06-17 18:44 Torr Samaho Note Added: 0009554
2014-06-17 19:28 Hypnotoad Note Added: 0009556
2014-06-17 20:12 Catastrophe Note Added: 0009557
2014-06-17 20:17 Torr Samaho Note Added: 0009558
2014-06-17 20:50 StrikerMan780 Note Added: 0009559
2014-06-17 20:50 StrikerMan780 Note Edited: 0009559 View Revisions
2014-06-17 20:51 StrikerMan780 Note Edited: 0009559 View Revisions
2014-06-17 20:54 StrikerMan780 Note Edited: 0009559 View Revisions
2014-06-17 21:09 Torr Samaho Note Added: 0009562
2014-06-17 22:52 AlexMax Note Added: 0009564
2014-06-17 23:16 StrikerMan780 Note Added: 0009565
2014-06-17 23:17 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-17 23:18 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-17 23:21 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-17 23:22 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-17 23:23 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-18 09:27 Hypnotoad Note Added: 0009569
2014-06-18 19:05 StrikerMan780 Note Edited: 0009565 View Revisions
2014-06-19 06:51 AlexMax Note Added: 0009590
2014-06-19 06:51 AlexMax Note Edited: 0009590 View Revisions
2014-06-19 07:30 Hypnotoad Note Added: 0009591
2014-06-19 17:51 Torr Samaho Note Added: 0009595
2014-06-20 11:48 Hypnotoad Note Added: 0009601
2014-06-20 11:50 Hypnotoad Note Added: 0009602
2014-06-20 11:51 Hypnotoad Note Edited: 0009602 View Revisions
2014-06-20 11:51 Hypnotoad Note Edited: 0009602 View Revisions
2014-06-21 18:17 StrikerMan780 Note Added: 0009627
2014-06-21 18:18 StrikerMan780 Note Edited: 0009627 View Revisions
2014-06-21 18:22 StrikerMan780 Note Edited: 0009627 View Revisions
2014-06-21 18:23 StrikerMan780 Note Edited: 0009627 View Revisions
2014-06-21 19:08 Torr Samaho Note Added: 0009629
2014-06-21 19:08 Torr Samaho Note Edited: 0009629
2014-06-21 19:09 Torr Samaho Note Edited: 0009629 View Revisions
2014-06-21 19:09 Torr Samaho Note Revision Dropped: 9629: 0005101
2014-06-21 19:09 Torr Samaho Note Revision Dropped: 9629: 0005102
2014-06-22 11:43 Torr Samaho Note Added: 0009648
2014-06-22 12:16 Hypnotoad Note Added: 0009649
2014-06-22 12:30 Torr Samaho Note Added: 0009650
2014-06-22 13:13 Hypnotoad Note Added: 0009651
2014-06-22 13:16 Dusk Note Added: 0009652
2014-06-22 13:24 Torr Samaho Note Added: 0009653
2014-06-22 13:31 Torr Samaho Note Edited: 0009653 View Revisions
2014-06-22 14:01 Hypnotoad Note Added: 0009656
2014-06-22 14:05 Torr Samaho Note Added: 0009657
2014-06-22 14:07 Dusk Note Added: 0009658
2014-06-22 14:08 Dusk Note Edited: 0009658 View Revisions
2014-06-22 14:16 Torr Samaho Note Added: 0009659
2014-06-22 14:44 Hypnotoad Note Added: 0009660
2014-06-22 14:45 Hypnotoad Note Edited: 0009660 View Revisions
2014-06-22 14:52 Torr Samaho Note Added: 0009661
2014-06-22 14:55 Hypnotoad Note Added: 0009662
2014-06-22 15:24 Torr Samaho Note Added: 0009663
2014-06-22 15:32 Hypnotoad Note Added: 0009664
2014-06-22 15:33 Hypnotoad Note Edited: 0009664 View Revisions
2014-06-22 15:34 Hypnotoad Note Edited: 0009664 View Revisions
2014-06-22 15:40 Torr Samaho Note Added: 0009665
2014-06-22 15:47 Hypnotoad Note Added: 0009667
2014-06-22 15:51 Torr Samaho Note Added: 0009668
2014-06-22 16:11 Hypnotoad Note Added: 0009669
2014-06-22 17:22 AlexMax Note Added: 0009672
2014-06-22 17:29 Torr Samaho Note Added: 0009673
2014-06-22 17:46 AlexMax Note Added: 0009674
2014-06-23 19:13 Torr Samaho Note Added: 0009717
2014-06-29 20:17 Hypnotoad Note Added: 0009794
2014-06-30 01:24 ZzZombo Note Added: 0009798
2014-06-30 06:03 Torr Samaho Product Version 1.3 => 1.2
2014-06-30 06:06 Torr Samaho Note Added: 0009801
2014-06-30 06:06 Torr Samaho Note Edited: 0009801 View Revisions
2014-06-30 06:07 Torr Samaho Note Revision Dropped: 9801: 0005240
2014-06-30 14:01 Hypnotoad Note Added: 0009803
2014-06-30 17:35 Torr Samaho Note Added: 0009804
2014-07-02 18:29 Hypnotoad Note Added: 0009822
2014-07-02 18:39 Catastrophe Note Added: 0009823
2014-07-02 20:07 Torr Samaho Note Added: 0009824
2014-07-11 13:19 Hypnotoad Note Added: 0009933
2014-07-12 14:33 Torr Samaho Note Added: 0009944
2014-07-12 14:33 Torr Samaho Note Edited: 0009944 View Revisions
2014-07-12 16:33 Hypnotoad Note Added: 0009950
2014-07-12 16:57 Torr Samaho Note Added: 0009951
2014-07-12 20:10 Hypnotoad Note Added: 0009955
2014-07-12 22:15 Hypnotoad Note Edited: 0009955 View Revisions
2014-07-29 08:31 Hypnotoad Note Added: 0010074
2014-07-29 08:32 Hypnotoad Note Edited: 0010074 View Revisions
2014-07-29 08:37 Hypnotoad Note Edited: 0010074 View Revisions
2014-07-29 08:41 Hypnotoad Note Edited: 0010074 View Revisions
2014-07-29 08:51 Hypnotoad Note Edited: 0010074 View Revisions
2014-07-29 21:05 Torr Samaho Note Added: 0010091
2014-07-29 21:24 Hypnotoad Note Added: 0010092
2014-07-29 21:26 Hypnotoad Note Edited: 0010092 View Revisions
2014-07-29 21:27 Hypnotoad Note Edited: 0010092 View Revisions
2014-07-30 21:21 Torr Samaho Note Added: 0010098
2014-07-30 23:11 Hypnotoad Note Added: 0010099
2014-08-23 20:18 Hypnotoad Note Added: 0010223
2014-08-23 20:19 Hypnotoad Note Edited: 0010223 View Revisions
2014-08-23 20:19 Hypnotoad Note Edited: 0010223 View Revisions
2014-08-23 21:52 AlexMax Note Added: 0010224
2014-08-23 22:35 Hypnotoad Note Added: 0010225
2014-08-23 22:35 Hypnotoad Note Edited: 0010225 View Revisions
2014-08-23 22:59 Hypnotoad Note Edited: 0010225 View Revisions
2014-08-23 23:02 Hypnotoad Note Edited: 0010225 View Revisions
2014-08-23 23:02 Hypnotoad Note Edited: 0010225 View Revisions
2014-08-24 16:32 Torr Samaho Note Added: 0010226
2014-08-24 16:53 Hypnotoad Note Added: 0010227
2014-08-24 18:48 Torr Samaho Note Added: 0010228
2014-08-24 18:57 Hypnotoad Note Added: 0010229
2014-08-24 19:05 Hypnotoad Note Edited: 0010229 View Revisions
2014-08-24 19:09 Hypnotoad Note Edited: 0010229 View Revisions
2014-08-24 20:03 Hypnotoad Note Added: 0010231
2014-08-24 20:56 Hypnotoad Note Edited: 0010231 View Revisions
2014-08-25 07:00 Blzut3 Note Added: 0010234
2014-08-25 12:16 Hypnotoad Note Added: 0010235
2014-08-25 17:34 Hypnotoad Note Added: 0010237
2014-08-25 19:06 Blzut3 Note Added: 0010238
2014-08-31 20:05 Torr Samaho Note Added: 0010245
2014-08-31 20:05 Torr Samaho Note Edited: 0010245 View Revisions
2014-10-05 22:20 Dusk Fixed in Version => 1.3
2014-10-05 22:22 Dusk Note Added: 0010347
2014-10-05 22:22 Dusk Status needs testing => resolved
2014-10-05 22:22 Dusk Resolution open => fixed
2018-09-30 21:35 Blzut3 Status resolved => closed






Questions or other issues? Contact Us.

Links


Copyright © 2000 - 2025 MantisBT Team
Powered by Mantis Bugtracker