Anonymous | Login | Signup for a new account | 2025-01-24 17:39 UTC |
My View | View Issues | Change Log | Roadmap | Zandronum Issue Support Ranking | Rules | My Account |
View Issue Details [ Jump to Notes ] | [ Issue History ] [ Print ] | ||||||||||||
ID | Project | Category | View Status | Date Submitted | Last Update | ||||||||
0001176 | Zandronum | [All Projects] Suggestion | public | 2012-11-07 21:26 | 2024-12-03 22:07 | ||||||||
Reporter | Watermelon | ||||||||||||
Assigned To | |||||||||||||
Priority | normal | Severity | feature | Reproducibility | always | ||||||||
Status | new | Resolution | open | ||||||||||
Platform | OS | OS Version | |||||||||||
Product Version | |||||||||||||
Target Version | Fixed in Version | ||||||||||||
Summary | 0001176: Account system development/discussion | ||||||||||||
Description | This is for discussion of this thread: 'http://zandronum.com/forum/showthread.php?tid=1434 [^]' I figure now we can get a bit more technical here. An account system I believe would need the following: - Space for approximately 13 or so characters, I figure we do not intend on having tag names in account names (like [TAG]Name would be sort of pointless) since we can set our tags in game - Would need to deliver a ticket of 0 = not logged in 1 = logged in anonymously (using a verified account) 2 = logged in account + name The client would need to transmit the information upon joining. I figure the code could be copied from joinpassword/password and used to log in from there. The password would ideally be asterisk'd out when you view it in Options should the value be in there. Upon attempting to log into the master server, the client sends some kind of encryption, maybe an MD5 hash or whatever the same protocol that the rcon utility uses. Since all the methods are already implemented in the project it should be easy to do so. For in game determination, we would need a new ACS function that would return the following - ACS string of the player - Integer of the player index in the database The second one seems more reasonable without having to do on-the-fly string construction. The first method (string) would allow players to actually see the names in ACS and display them/store them (could store each letter as a number if need be). All that would be needed on the master is less than a meg of space, I figure a text file that isn't encrypted for 1000 or so accounts would be (10 char name + 10 char password on average = 20 bytes approx per player = 20,000 bytes). That is relatively small. Parsing a file for authentication would be really quick and easy. How would we create accounts? Would they be based off of account names? Is this a feasible idea? This ticket is mainly for discussion. | ||||||||||||
Attached Files | |||||||||||||
Relationships | |||||||||||||||||||||||||||||||
|
Notes | |
(0005330) Dusk (developer) 2012-11-07 21:34 edited on: 2012-11-07 22:03 |
A length of 13 characters sounds kind of.. non standard and maybe a little short. I'd suggest 16. What does "1 = logged in anonymously (using a verified account)" mean? EDIT: nevermind, just saw Torr's post The client also should not be telling which account it is by (for obvious reasons), it should log into the master server, give a hash to the server. The master server tells which hash is whose. Unless I'm misreading things here. As for ACS interfacing, I'd prefer identifiers as well as a function that converts said identifiers to account name strings. If anyhow possible, link these accounts with forum accounts. Keep things easy for the users. Though I guess that's something for Blzut3 to consider. :P |
(0005331) Qent (updater) 2012-11-07 23:09 |
Why even 16 characters? The account name need not -- and should not IMHO -- be tied to their in-game name. People just see '><MLK><Qent has connected (logged in as "Qent").' I think it would be great if accounts were by default tied to forum+tracker accounts, and possibly requiring the user to click a button on the User Control Panel to opt in to the in-game account. It might also be useful for servers to specify a different login server, but would that open the possibility of phishing? |
(0005332) Torr Samaho (administrator) 2012-11-08 06:22 |
Quote from Watermelon The secure way would be to let the client send the password over an encrypted connection (the clear password, not any hash of it) and let the server do the hashing (and let the serve only store hashes, never clear passwords). AFAIK this is how all proper password login systems work. Otherwise the hashes actually become the real passwords and stolen hashes could be used directly to login. Note that the RCON utility / joinpassword implementation is unsecure and should not be copied for the new system. |
(0005334) Watermelon (developer) 2012-11-08 13:49 edited on: 2012-11-08 14:30 |
A few things need to be done for this to become a feasible idea: [Plan for the code] - Would it be difficult adding encryption for sending the password? If not it sounds like the transmission method is ready to go - Do we want it so that the player has to log in before hand, or store their name/password in the .ini and then this is sent upon connecting (is this similar to how join/passwords work?) - Needs to be an option that say "log in always" or "log in anonymously if pass failure" or "pass failed, stop connecting to server" or "never log in" somewhere for the client to decide - Are we doing player ID's (like index from the database)? If so do we attach it to the players information when they connect so all clients have the information? - How is it for limiting the name to 16 chars? Or see the additional section [Whitelisting] - Need to add support for account names to access a server - Same for adminlist/banlist - Master ban list support for account names [Plan for the master server] - Do we have enough space/access for parsing? [Additional] - Can we connect to forum accounts? Then we can limit it based on the forum account length, and everyone will have a registered accounts [Handling bans] - Can we tie a long (or whatever the 64 bit number is called in C++, long long?) to when the account was created? The only feasible thing I could think of here would be to deny access to newer accounts if someone starts creating accounts and trolling. However this will not prevent someone from making 5 accounts and just letting them wait. Though the forum admins should be able to detect this with IP stuff. Again if done with a proxy it could be a headache, though we're pretty much dealing with this crap anyways from brazil players who troll and have ridiculous IP changes EDIT: Would the login be when connecting, or like ZD where launching a browser causes a login screen to pop up? |
(0005340) Torr Samaho (administrator) 2012-11-08 20:31 |
For now I can just comment on a few things:Quote from Watermelon For proper encryption handling we need to use a professional library designed for this purpose like OpenSSL. Quote from Watermelon Accounts should not be banned on the master server by adding them to some special list, but by flagging the account itself as banned in the account database (ever account needs to have certain properties like name, password hash, "is-banned",...). Though I agree that adding account id support to the adminlist/banlist of a game server is useful. Also in case a server wants to keep out everybody, except for a few selected people, adding account whitelist support is good. |
(0005412) Torr Samaho (administrator) 2012-11-18 10:33 |
Any volunteer to see if OpenSSL can be used easily? I'd say the first thing we need is to establish a secure way for the client to communicate with the login/master server. |
(0005417) Dusk (developer) 2012-11-18 18:39 |
OpenSSL appears to apply both Apache license's and 4-clause BSD's terms. Are we in the clear to use it? |
(0005418) Torr Samaho (administrator) 2012-11-18 19:42 |
Very good question. I just read through the license and don't see any apparent problems, but I'm no license expert by any means. Can anybody with more license experience comment on this? |
(0005420) ZzZombo (reporter) 2012-11-19 02:16 |
I've read the license throughly and found no problems. I'm not an license expert too, but I'm experienced Russian Wikipedia user, and have dealt with licenses. |
(0005423) Blzut3 (administrator) 2012-11-21 18:11 |
Now that I think about it some more. Why do we need to use OpenSSL for communication? If we tie the accounts to the central account system here, there's not a whole lot of point to SSL since we don't use https (so a man in the middle attack would still be possible through the website). I would think the best thing to do is just hash the password with a salt like what is done for the rcon protocol. There's really no reason to ever need the plain text password on the account server. |
(0005424) Torr Samaho (administrator) 2012-11-21 20:17 edited on: 2012-11-21 20:19 |
Quote from Blzut3 How do you propose the login to work then? From the top of my head I don't see any way to login that doesn't either require the server to store the plain text password (or something equivalent whose knowledge allows anybody to login to the account) or to have the client sent the plain text password. The former is apparently a no-go and the latter requires a properly secured connection between server and client (that's why I suggested OpenSSL). The RCON protocol approach is of the "store the plain text password" type and thus can't be used. Possibly I'm overlooking something here. Quote from Blzut3On a side note: IMHO this is a pretty bad thing and the reason I hate to login to the forum / tracker over open public wireless. |
(0005425) Blzut3 (administrator) 2012-11-21 20:39 |
IIRC doesn't the rcon protocol send a salt on initial connection? The password is then sent back to the server as md5(salt + password) which is then used for verification. For this, the salt and final hash would be stored in the database so that even if the db gets compromised the plain text passwords aren't there (which, sadly, is probably more likely than a man in the middle attack although I'm not a security expert). Given that md5 collisions can be generated fairly quickly I would also look into other algorithms. (Blowfish?) I guess this really doesn't matter to the protocol since the passwords could be stored hashed at the server, but I think hashing them client side is a more secure option. SSL could be used on top of this if man in the middle attacks are a real concern. But I would think being able to impersonate someone is a small reward for all that effort. |
(0005431) Torr Samaho (administrator) 2012-11-24 12:49 |
Quote from Blzut3Yes, the RCON protocol uses this trick to avoid sending the plain text password. The price as I already mentioned above is that you have to store the plain text password on the server for this to work (or something that is equivalent, see below). Quote from Blzut3Doing so makes the final hash equivalent to the password in the sense that you can pass the authentication with knowledge of just the final hash. So if somebody steals the hashes from the server without being noticed, this person can authenticate indistinguishable from a legit client (it would only need a small modification of the client binary to ignore the salt and send the final hash directly). Quote from Blzut3As I said above, the login sever should definitely not store the plain text passwords. Quote from Blzut3SSL not only prevents man in the middle attacks but also others from sniffing your packets on an open wireless. |
(0005476) Watermelon (developer) 2012-12-05 20:40 |
What am I to look for currently? Are we deciding on checking OpenSSL only to see if it will 'collide' with any of our licenses, or are we looking at how easy/hard it will be to integrate into Zandronum? |
(0005491) Torr Samaho (administrator) 2012-12-09 09:52 |
Quote from Watermelon This (although we should confirm that the licenses are compatible beforehand). |
(0005542) Watermelon (developer) 2012-12-22 05:20 |
I tried getting OpenSSL to work but these intense C++ programs are just beyond my knowledge of getting compiled and going. If it was Java I could but C++ isn't my expertise. I didn't see any possible license collisions, but again that would be for Infurnus to determine (or someone knowledgeable). If encryption can get done or at least tested, I could assist with the stuff afterwards. |
(0005570) Torr Samaho (administrator) 2012-12-26 17:47 |
Any other volunteers to look at OpenSSL? |
(0005875) Torr Samaho (administrator) 2013-01-30 20:30 |
I thought a bit about alternatives to OpenSSL: The only things that needs protection are the client's login credentials. Instead of using OpenSSL to secure the whole communication between client and login server, we could use a public-key cryptography approach (like PGP): The login server has a key pair (public and secret) and all clients get the public key. When the login server requests the client to send its credentials it also sends a random hash (or something like this, one could also use the current time). The client then takes the random hash and the credentials, encrypts it as single message with the public key and sends this to the login server. The login server then can decrypt it with its secret key. Intercepting the encrypted message doesn't help an attacker, because it only allows to login if the server should request a login with the same randon hash. |
(0005877) Watermelon (developer) 2013-01-30 21:11 |
Is there any way the above can be tested? Like should I or someone download/run PGP? |
(0005879) Torr Samaho (administrator) 2013-01-30 22:22 |
To get started we need a library that handles the encryption and decryption based on a public/private key approach. Perhaps Libgcrypt or BeeCrypt are suitable candidates. |
(0005880) Watermelon (developer) 2013-01-30 22:46 |
Is there anything I can do? Like download, compile, test and see how it works... possibly even attempt to integrate it into zandronum in a fork? |
(0005888) Torr Samaho (administrator) 2013-01-31 06:29 |
Sure. See if you can compile one of the two libraries and link it to Zandronum like it's already done with hqnx, GeoIP and some others. |
(0005951) Watermelon (developer) 2013-02-06 23:35 edited on: 2013-02-06 23:36 |
Do we want to pursue an additional library or possibly use a better algorithm with hashing? We have to decide how we want to store passwords and retrieve them. If we go the public/private key way, then we'll need a library and someone who knows this stuff who is willing to implement such things. If we don't mind just storing raw text in a secure database then we can just send the time or salt to the client, have them hash it and send it back. While a new full blown cryptography library would be pretty cool, account systems will not be super critical to game play because it will just return a true/false if the account is logged in and what database index their account is if logged in from the master. I've worked with the zandronum rcon password salt procedure before and it's easy enough to understand. Maybe this should be separate from the forums? The reason I say that is because it makes password cracking through the client less serious. Also, how is the password going to be stored on the client's side of things? EDIT: Also by doing our UDP way, we need not go the TCP/IP route which most of these libraries seem to require. I don't know if Zandronum has TCP/IP support as much as UDP... and if not, it would then require even more time setting up such a system to be used for this feature. |
(0005958) AlexMax (developer) 2013-02-07 03:34 |
After much research, I think I have found a good library: Botan 'http://botan.randombit.net/ [^]' - Supports public key cryptography with padding and randomness:'http://botan.randombit.net/pubkey.html [^]' - Focuses on the algorithms themselves instead of the transport mechanism. - Has been around the block, and continues to be updated. Not a fly-by-night student project. - BSD licensed, so very license-compatible with Zandronum. - SUPPORTS AMALGAMATION USAGE!!! Big win here! Botan can be told to turn into one gigantic .cpp and .h file that can be directly included in the project, no fussing about with external libraries! |
(0005961) ZzZombo (reporter) 2013-02-07 04:56 |
Did you look at Steam Works?'http://www.steampowered.com/steamworks/ [^]' If we could integrate our game into Steam, then we would get account system implemented automatically, we only would need to do a few adjustments. And we would get much more, for example, friends list, updates and possibly Steam server browser. |
(0005966) AlexMax (developer) 2013-02-08 04:00 |
I do not steam would allow a game on their service that relies heavily on the intellectual property of another game unless they got permission first. Back to the question of login servers, after further pondering, I remembered that I had proposed the concept of a third party login server a few months ago. With the third party login server, the password is not being sent over the client <-> server connection, and thus we are not shackled to UDP or the Zandronum protocol when it comes to communication with the login server (at least the client wouldn't when it tries to log in, the game server would simply treat communication with the login server like a server query response). I'll need to do some more pondering, but I think i will dedicate this weekend to getting at least a partly functioning login server working that could in theory authenticate both Odamex and Zandronum users. I'm actually leaning towards the client login action being handled through a web API accessed via HTTPS, since the login server would need a web interface anyway. |
(0005967) Dusk (developer) 2013-02-08 05:51 |
I'm not quite sure how well would the third-party-login-server system be interfaced to modding unless we expose the full user@host ident.. which doesn't actually sound as a very bad idea to me. I also think that maybe the launcher could store a list of known login servers and allow quick registering. |
(0005978) ZzZombo (reporter) 2013-02-10 10:57 |
Uh, and what? We don't distribute any illegal copy of IWADs, so we are clear here. |
(0006097) AlexMax (developer) 2013-03-07 02:29 |
I had in mind an "easy encryption" library for a long time that I could not remember the name of. I just now remembered what it was called: NaCl. There is a more flexible and API compatible alternative to it called Sodium, located here: 'https://github.com/jedisct1/libsodium [^]' |
(0006179) Watermelon (developer) 2013-03-28 17:13 edited on: 2013-03-28 17:15 |
libsodium looks great I tried adding it to the project and compiling but it spits out a ton of errors. Apparently either I'm missing some basic C++ files from C99 (how?) or this has issues compiling on 2005. I saw somewhere that it may be due to using old visual studio version (< 2010) which sucks because IIRC we can't compile on 2010. Can anyone download libsodium, add it to the zandro project, include the 'include/sodium' and compile? I'm getting this stuff: Cannot open include file: 'version.h': No such file or directory I just want to see if it's only my computer or not: DL link:'https://github.com/jedisct1/libsodium/archive/master.zip [^]' |
(0006185) ZzZombo (reporter) 2013-03-29 10:58 |
I could compile Zandronum on VS 2010 just fine although I had to make some changes to the project files. |
(0006187) Watermelon (developer) 2013-03-29 16:07 |
What did you have to change? I'd like to try and see if I could get it working on VS 2010 |
(0006195) ZzZombo (reporter) 2013-03-30 02:43 |
First of all, I had the Express Edition, so I had to fix the missing Windows x64 platform. There are two ways: either copy the x86 version as x64 (just copy the folder and rename it accordingly) or manually (in any text editor) remove all references to it from the solution file (much harder if you don't use automated replaces). Next, I couldn't make the svnrevision utility to work because the path to it was fucked up somehow (evaluated path included a space, but then it has to be surrounded in quote marks, too hard to explain in detail). Either change the project settings for "zandronum" so pre-build step for the utility would use the full path or disable that step. Oddly enough, sometimes this problem doesn't occur (I have up to 5 different local Zandronum repositories, on only on 2 or 3 I had this issue). Seems all. |
(0008033) AlexMax (developer) 2014-01-16 23:41 |
I believe I have come to a workable solution for how authenticating to a server by a client could work. SRP is what we're looking for: 'https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol [^]' SRP does not rely on any particular transport, which is fantastic. It also does not rely on a third party certificate store, double fantastic. It also is impervious to man in the middle attacks, the game server can either elect to handle authenticating the player itself or proxy the request to a third-party server of its choosing - as long as the verifier of the account is kept safe, a malicious server doesn't get anything out of trying to monitor or manipulate the authentication attempt. There is one downside. SRP describes how to authenticate, but the process of getting the password to the authentication server in the first place (signing up) is left as an exercise to the reader. In the cases where authentication is handled by a third party, I believe that third party should run a website where accounts are created through a signup webpage guarded by TLS. So, how do we implement SRP? OpenSSL does have an SRP implementation as of 1.0.1, but unfortunately to conform with some SRP RFC it does not allow you to change the hashing function to something more secure than SHA1. Fortunately, someone else has done the legwork for us and created a third party library called csrp: 'https://github.com/cocagne/csrp [^]' Unlike OpenSSL, this library (file, really) allows us to pick from many different flavors of SHA, including the more secure ones that we're probably interested in. And it has one dependency - OpenSSL itself. |
(0008036) Torr Samaho (administrator) 2014-01-17 07:15 |
That looks very promising for our purpose! Is somebody interested in setting up a testing website for this purpose? I could then work on the necessary client and server extensions in Zandronum. |
(0008043) AlexMax (developer) 2014-01-17 17:56 |
I will do the test server. However, in order to do the test server, we need to agree on a protocol: Terminology: Client: The end user that is connecting to the game server Server: The game server Auth Server: The authentication server Transport: I am assuming UDP? Or are you going to integrate TCP especially for this? Message: I'll tell you what I need on my end, you tell me how you want to structure the packets. Keep in mind that there is the possibility that Odamex might want to use the same auth server protocol, so if it would be easy to avoid any Zandronum-specific bits (like zan's huffman coding in the messages), that would be preferred. Three message pairs are needed between the server and auth server. - Protocol version agreement - SRP steps 1+2 - SRP steps 3+4 The protocol agreement needs to contain the username of the authenticating client going from server -> auth server, and a successful auth server response to the server will contain a protocol version. The protocol version agreement is not part of SRP per-se, but is an important step. The reason is that the stored verifier is dependant on both the large prime N and the hash function H, and we need a mechanism for giving us headroom to upgrade without invalidating the older verifiers outright. The SRP steps are essentially proxied verbatim from the client to the auth server and vice versa. I say essentially because the server might elect to do some sanity checking (on length, message frequency, and such) to make sure that evil clients can't use the server as a proxy to attack the auth server. That's all I can think of off the top of my head. There are probably more decisions to be made (when should auth happen, should we allow servers to have anonymous players, should we allow server or server-cluster local auth without a third party server, should the auth server be able to ask the server to prevent a client from connecting, how should cross-server state like experience be handled), but I think answering the above questions is enough to get us off on the right foot. |
(0008048) Blzut3 (administrator) 2014-01-17 21:56 |
Definitely agree on avoiding huffman for the protocol so this can be shared. Since I assume Odamex would want it's own auth server. For handling many servers I propose usernames be in the format of user@host. I would recommend doing like XMPP and require that the host have a SRV record (_doomsrp._udp.example.com). Effectively this requires the server owner to have a domain (making ban evading costly at best) on top of SRV being a more advanced DNS feature. (Our registar Namecheap supports them and they provide a free DNS service for everyone.) What this means is it seems like it would be reasonable to accept any server and simply ban whole domain names should a rogue server come about. It also means that the auth server doesn't necessarily need to be on the same server as the second level domain (SRV records can point to any host) while not requiring a subdomain in the host name (e.g. user@zanauth.example.com). To simply the interface Zandronum would of course use zandronum.com if no @hostname is given. 'http://en.wikipedia.org/wiki/SRV_record [^]' |
(0008055) Torr Samaho (administrator) 2014-01-18 12:38 |
Quote from AlexMax Do you have time to make a draft for the protocol? As long as we use UDP for the transport, I'm very flexible with what exactly client and server put into the UDP packets (and I still have to read through the SRP documentation before I know what exactly needs to be in there). It should be feasible to avoid Huffman coding of packets to and from the auth server, as long as it's fine that all packets to and from the IP+port of the auth are never Huffman encoded, i.e. if Zandronum can decide whether to use Huffman coding or not based on the address of the packets. |
(0008118) AlexMax (developer) 2014-01-20 03:35 |
How about this? 'https://docs.google.com/document/d/1qMNklLhESKnqq9HU445s-6KGdJUkC1ovNb4ZAL5Nbw4/edit?usp=sharing [^]' Not final, of course, but should be enough to get us going... |
(0008124) Torr Samaho (administrator) 2014-01-20 20:05 |
Thanks! Seems to be enough to get us going. Meanwhile I noticed that it's not completely trivial to set up OpenSSL under Windows and that csrp needs small changes to make it compile under VC++ 2005. Will need to take care of these issues before I can start with the implementation of the protocol. |
(0008125) AlexMax (developer) 2014-01-21 23:20 |
One particular question I have is that after some thought I am not so sure about features such as the "Punishment" response (6b). I believe that such things are out of the scope of an authentication protocol and such things, while desirable, could perhaps be handled through some sort of official extension mechanism to the protocol. An off-the-cuff example might be that Odamex could be unlikely to be interested in authentication server punishments. Another might be that the auth server could implement ZDaemon-style "global experience" somehow, which would require messages from trusted servers. However, I'm not exactly sure what an extension mechanism in the protocol would look like. I'm not even sure that using a hard-coded packet scheme would be appropriate for it, perhaps the messages could take the form of some serialized protocol such as BSON or Protocol Buffers. I will continue with implementing the protocol as agreed-upon aside from the 6b packet. |
(0008129) Torr Samaho (administrator) 2014-01-23 20:36 |
I agree, let's not care about the 6b packet or an extension mechanism for now. Meanwhile csrp and OpenSSL seem to be running with VC++ in Zandronum and I can finally actually start implementing the protocol. |
(0008132) Torr Samaho (administrator) 2014-01-25 18:43 |
I have implemented the server side of the "server <-> auth communication" (the whole logic is still missing though). We still need a draft for the "client <-> auth" and the "client <-> server" communication. I can take care of the "client <-> server" stuff, all that will only happen in the Zandronum binary anyway. Did you already have time to start working on the auth server? |
(0008133) AlexMax (developer) 2014-01-26 02:12 edited on: 2014-01-26 02:37 |
I don't have anything I can push to github yet. What exactly would you expect the client <-> auth protocol to do and look like? The whole reason we're tunneling auth through the server is to get rid of the three way dance. EDIT: Also, did we ever agree on the hash/prime? If not, do you have a preference? |
(0008135) Torr Samaho (administrator) 2014-01-26 20:26 |
Quote from AlexMax You're right. After re-reading your protocol draft I realized that the client shouldn't need to talk to the auth server directly. I'll take care of the client <-> server part and report back. Quote from AlexMax No, feel free to propose something. |
(0008145) AlexMax (developer) 2014-01-29 00:03 |
How about the large prime N and generator g in the 2048 slot, using a hash of SHA256? 'https://github.com/cocagne/csrp/blob/master/srp.c#L71 [^]' Also, would it be possible for you to use the rfc5054_compat head of the CSRP project? I am investigating alternatives to having to create a module that wraps CSRP myself, and I figure that being RFC compliant would make potential interoperability easier. |
(0008147) Torr Samaho (administrator) 2014-01-29 20:53 |
Quote from AlexMax I didn't check how secure this is and will just assume that you picked something reasonable :). Can you update your protocol draft with the CSRP code the client is supposed to call to select this? Quote from AlexMax Done. Since I didn't use the code yet, there was not much to change. |
(0008149) AlexMax (developer) 2014-01-30 23:54 edited on: 2014-01-31 05:10 |
Done. I have also adjusted and clarified the protocol a teeny bit and moved the communication of the salt variable up to packet 2. A library that I was going to use on the auth server had the server's salt as a requirement for construction of the Client object for some reason. Seeing as I'd like to mock the Client for unit tests, I did a little digging and read that in cases where there is an initial protocol negotiation (like we have in packets 1 and 2), it is safe for the server to pass the salt back to the client in the Server's first response to the Client, and that you don't have to wait until the ephemeral value exchange (packets 3 and 4). So I moved the communication of the salt to the second packet. EDIT: As I implement the protocol, I might also decide that some of the 'length' fields in the protocol are not long for this world. Probably depends on how many I can guarantee a length on. |
(0008154) Torr Samaho (administrator) 2014-02-02 13:30 |
I finished a first (still completely untested) implementation of the Zandronum side of the protocol. Once we have a first version of the auth server, we can start testing. |
(0008155) Torr Samaho (administrator) 2014-02-02 17:16 |
For testing purposes, I implemented the SRP code that should be run on the auth server locally on the server. Using this the client <-> server communication and the login process seem to work fine. I had to downgrade the csrp source to the master branch of the csrp repository though. The rfc5054_compat branch version didn't seem to work. |
(0008156) Torr Samaho (administrator) 2014-02-02 18:45 |
Looks like there was a bug in the rfc5054_compat branch. 'https://bitbucket.org/Torr_Samaho/zandronum-accounts/commits/57561b3d690068cc8b738c608add7a18bb4dd61f [^]' fixes the problem on my end. |
(0008161) AlexMax (developer) 2014-02-02 20:56 |
I have e-mailed the author of the CSRP library, asking for clarification on his real intention of that bit of code. I am currently continuing work on the auth server. Currently the first two protocol messages are done after going back and forth in regards to libraries, but I was able to mock those without a working database, so database functionality is coming next. |
(0008162) AlexMax (developer) 2014-02-02 21:30 |
Just got an email response from Tom. Turns out that his cast addition was correct (was used to fix a warning with GCC 4.7+), but his logic was backwards. This is the corrected line of code: 'https://github.com/cocagne/csrp/commit/4c8b21f11c07ef2d2cfd9edef5f89e0207e53ff2 [^]' |
(0008511) AlienOverlord (reporter) 2014-04-07 16:52 |
Oh btw. About size of players' names, the limit should be 32 as that's the ingame limit. My name, for example, didn't fit into 15 characters limit currently active in Water's priv account system. |
(0008584) AlexMax (developer) 2014-04-16 02:26 edited on: 2014-04-16 02:27 |
FYI, there was some discussion about the login server on issue 295 as well - I accidentally started responding to the "wrong" (old, really) discussion. Either way, for those of you who didn't already know, Test Login Server <-> Zandronum communication is working, so the first big hurdle has been cleared Now to discuss fun things like: - An ACS API for recognizing unique users. - How to handle it from an interface point of view. |
(0008585) Torr Samaho (administrator) 2014-04-16 18:57 |
Wonderful!Quote from AlexMax An ACS function that returns the account name would be very easy to add. But if we think in the direction of stats tracking, where to store the stats and how to handle reading and writing? Quote from AlexMax I think Zandronum itself should not store the credentials, but the launcher should supply them. There was the idea of letting the launcher hand them over to the client via an rsp file. |
(0008593) AlexMax (developer) 2014-04-17 01:37 |
Okay, how about this... 1. ACS player account interface. There are actually a few things we can do here. Names are a good idea, but I also think that it would be a good idea to have an interface that can grab a "unique ID" as well - a unique ID for the account server and a unique ID for each player. That would make dealing with individual players easier (comparisons can be equality instead of a strcmp. 2. ACS persistence. I've been thinking about this. I am disinclined to give ACS a full SQL abstraction for two reasons: - ACS is not a language suited for the complexities for SQL. The API would probably have to be a thin wrapper over the native C API and who knows what other weird issues might come up. - SQL queries of any complexity would be SLOW. If the queries can't be fast enough to complete in a timely manner, suddenly we now have to write an asynchronous API. In ACS. Please no. To that end, I think that a simple key/value store would be preferred. I think that we should still stick with SQLite because it's widely available, performant, easy to inspect by third party tools, and a few nice extras like the ability to limit the size of the database on disk. How about this for a start: (t = table name, k = key name, v = value) - CreateTable(t) // Create a new table - TruncateTable(t) // Delete all records in the table - DropTable(t) // Drop the table - Get(t, k) // Get a specific key that contains an integer - GetString(t, k) // Get a specific key that contains a string - Set(t, k, v) // Set a specific key to a specific integer - SetString(t, k, v) // Set a specific key to a specific string - Delete(t, k) // Delete a key - Begin() // Start a transaction - Commit() // Commit the transaction Obviously these function names are a little over-generalized, but hopefully it's effective at conveying where my head is at. 3. Credentials interface I think that it should be possible to log in either from Doomseeker or from Zandronum itself. Username and password just could be cvars like the server password or join password are - does ZDoom support disallowing a cvar from saving like Odamex does? If so, you could also have a "Login menu" that pops up a username and password cvar setter if the server requires authentication and the variables aren't set. |
(0008594) Blzut3 (administrator) 2014-04-17 01:52 |
While a GetPlayerSessionID() or something to associate a player with some unique ID might be useful for clearing out or reassigning data to players in an actively running server. Note that this pretty much implies that authentication must be done before joining and can't be done mid game without reconnecting. (Otherwise the valid of that function would change and we have no way of notifying the ACS script that it has changed.) I've voiced my opinion against given any sort of persistent database functionality in the past. Eventually we'll have ZDoom's cvar store which is about all I'd be willing to trust users with (and personally I think that's too much, but oh well it's not my decision). Manipulating a persistent store across multiple processes is something that is no doubt way over the heads of the typical ACS coder. Those who understand all the issues that go with it would have no problems patching the server to collect the data outright and it'd be infinitely more flexible. Proper stat tracking is really something that should be done in the server code itself and not by mods. Since we're looking at a potentially distributed account system, the stats would have to be local to a server cluster at best any way since there'd be nothing stopping cheaters from sending in bogus stats if centralized. |
(0008598) Torr Samaho (administrator) 2014-04-17 17:38 |
Some quick comments: Unique ID sounds good, but then we should extend the server <> auth server protocol so that the auth server includes the ID in the AUTH_SERVER_SRP_STEP_FOUR packet.Quote from Blzut3 Authentication in this case only needs to be done before joining the game as player, being connected as spectator is fine since those don't trigger ENTER scripts. If you are already an in-game player, one would need to spectate, authenticate and re-join as player. |
(0008603) Blzut3 (administrator) 2014-04-17 19:11 edited on: 2014-04-17 19:17 |
Actually, if we distribute authentication servers, returning a unique ID in the protocol won't be entirely possible. Hashing the user name would probably be a feasible choice, but we might need a system to account for hash collisions. The solution is easy if we don't try to do persistent stuff with it (have a map of everyone who has logged on in the server), but could be complicated if we do indeed try to implement persistence. (Note that my position on that issue is still we should not.) Edit: Oh, and I forgot that the function needs to work for anonymous players. |
(0008605) Torr Samaho (administrator) 2014-04-17 20:22 |
Quote from Blzut3 Can you elaborate? If it's the same account, one should be able to store / transmit / sync the ID together with the password hash. If the authentication servers are not synced, then the same username may be taken by different users on different servers, so not even the username would be unique. |
(0008606) Blzut3 (administrator) 2014-04-17 20:24 |
Username in that case means user@host (@zandronum.com being implicit if not provided) so they would be unique. |
(0008607) AlexMax (developer) 2014-04-18 00:45 |
> While a GetPlayerSessionID() or something to associate a player with some unique ID might be useful for clearing out or reassigning data to players in an actively running server. Note that this pretty much implies that authentication must be done before joining and can't be done mid game without reconnecting. (Otherwise the valid of that function would change and we have no way of notifying the ACS script that it has changed.) I don't think a session id is necessary. You can simply use their player number and the unique id of the player for this. As far as when logging in happens, I actually envisioned four possibilities. - Server does not require authentication and server has no auth server set so server is 100% anonymous. - Server does not require authentication and auth server is set. This allows users to log in anytime they wish (perhaps via the join menu). - Server requires users to log in before joining the game, but they can spectate all they like. - Server requires users to log in before joining the server. Pre-connection authentication I imagine would be handled by a client sending a special "I want to connect and oh by the way I have authentication information to show you" packet, which is set anytime the client has any auth information stored. > I've voiced my opinion against given any sort of persistent database functionality in the past. Eventually we'll have ZDoom's cvar store which is about all I'd be willing to trust users with (and personally I think that's too much, but oh well it's not my decision). > Manipulating a persistent store across multiple processes is something that is no doubt way over the heads of the typical ACS coder. Those who understand all the issues that go with it would have no problems patching the server to collect the data outright and it'd be infinitely more flexible. I apologize, but I disagree vehemently. Your approach has several issues: - The implementer must know how to link the server against a database library. Watermelon got hung up on this with his accounts implementation and had to resort to dumping data to a text file so a Java process could pick up on it. - The implementer must know how to use the native database API correctly. ZDoom is single-threaded, and naieve implementations of database calls will freeze the entire server. - I as the server owner now have to compile this myself and use it JUST for one or two servers, because naturally only some servers will have stat tracking. - The implementer must not make any implementation mistakes in their C++ code, or else I as the server owner am suddenly possibly vulnerable to exploitation. Sorry, but I'll take an ACS implementation of a simple key/value store any day of the week over C++ written by ACS or dynamic scripting language jockeys, myself included. > Proper stat tracking is really something that should be done in the server code itself and not by mods. I also disagree with this. One of the reasons ZDoom is so popular is that it's easy to make modifications for. Jumpmaze already uses a key/value store to store server records, and it really should NOT have to use a patched server or an external purpose-made RCON daemon to do so. > Actually, if we distribute authentication servers, returning a unique ID in the protocol won't be entirely possible. I was thinking perhaps something along the lines of SVN's UUID, something that would stay the same even if the auth server moved. |
(0008608) Torr Samaho (administrator) 2014-04-19 07:57 edited on: 2014-04-19 08:07 |
Quote from Blzut3 And the value for host would be determined by whatever the server used as "authhostname"? I also think that Zandronum itself should offer some kind of optional persistent data storage mechanism. Some people for sure will want to have this (and there are already some rather crude ways used for this purpose) and it doesn't make sense if each server admin has to alter the server source to get something like this. EDIT: Does anybody know if MySQL++ is any good and suitable for our needs? |
(0008609) AlexMax (developer) 2014-04-20 00:17 edited on: 2014-04-20 00:19 |
> I also think that Zandronum itself should offer some kind of optional persistent data storage mechanism. What is your opinion of the key-value system I proposed above? I was going to suggest backing it using SQlite3, as it is a very good 'least common denominator' which requires no setup on the part of the administrator and gives us some nice features out-of-the-box, such as preventing the size of the database from going over a certain amount of disk space. 'https://sqlite.org/download.html [^]' |
(0008611) jam (reporter) 2014-04-20 02:29 |
^SQlite3 may be appropriate for zandronum, but it doesn't allow too many concurrent accesses. PostGreSQL is my recommendation. Easy to setup and is authenticated :) |
(0008616) Torr Samaho (administrator) 2014-04-20 13:02 |
SQlite3 looks very suitable for us indeed. I made some quick tests and at least under VC++ it seems to be very easy to make use of SQlite3 code inside Zandronum. The key-values also sounds suitable for our purposes. |
(0008620) Torr Samaho (administrator) 2014-04-20 17:18 |
I started to work on the key-values database. The following internal functions
seem to be working so far. Adding integer versions of DATABASE_AddEntry and DATABASE_GetEntry should be trivial as long everything is stored as string internally. BTW: I think we don't need to expose the Begin() / Commit() stuff. |
(0008622) AlexMax (developer) 2014-04-20 21:33 edited on: 2014-04-20 22:22 |
> ^SQlite3 may be appropriate for zandronum, but it doesn't allow too many concurrent accesses. PostGreSQL is my recommendation. Easy to setup and is authenticated :) SQlite3 is easy to embed and requires no setup on the part of server owners other than setting a cvar where they want their datastore to be. If server admins need something that is bigger, we can cross that bridge when we come to it - probably with a database-agnostic library of some kind that allows you to replace the datastore cvar with a connection string. > BTW: I think we don't need to expose the Begin() / Commit() stuff. I wasn't so sure about including them, but the performance gains you can get from transactions are too significant to be ignored imho. 'http://stackoverflow.com/questions/1711631/how-do-i-improve-the-performance-of-sqlite [^]' Without transactions, this test only measured 85 inserts a second. At 28ms per gametic, that means that you only have time for 2 database writes per gametic. The jump up to transactions allowed for 23,000 inserts a second! Using a prepared statement in your C++ code doubles that again to 53,000 inserts a second - almost enough for anybody, at least if you ask Bill Gates. Begin() and Commit() should be optional (and are, in fact, optional at the SQL level), but the sheer jump in the number of records you can write per tic using transactions seems to me like it's too big to ignore. As for what their actual ACS names should be: - CreateTable(t) // Create a new table - TruncateTable(t) // Delete all records in the table - DropTable(t) // Drop the table - GetRecord(t, k) // Get a specific key that contains an integer - GetRecordString(t, k) // Get a specific key that contains a string - SetRecord(t, k, v) // Set a specific key to a specific integer - SetRecordString(t, k, v) // Set a specific key to a specific string - DeleteRecord(t, k) // Delete a key - BeginTransaction() // Start a transaction - CommitTransaction() // Commit the transaction - RollbackTransaction() // Throw out the transaction Sorta bringing it in-line with the CVar functionality. Maybe "Record" is too wordy, but a word like "Key" only really refers to the key in the record. |
(0008623) Torr Samaho (administrator) 2014-04-21 10:31 |
I made a few tests with the Begin() / Commit() stuff: For inserts it actually matters much, even with only 10 inserts I saw a performance increase of a factor of about 10 and a factor of more than 50 for 100 inserts, but with updates the performance gains are much smaller. Updating 10 entries is about 2 times faster, updating 100 is about 3 times faster. Especially because of the update performance, I'm still not completely convinced that this should be exposed via ACS. Is there any reasonably scenario where a mod should be spamming inserts (except for an initial database setup)? |
(0008627) AlexMax (developer) 2014-04-23 00:32 |
I'm sure I could come up with a potential usecase if I thought of one, but if you are especially opinionated about not exposing the transaction commands, I think we can skip them I suppose. They would be a cinch to expose later anyway. Just make sure you're using bound parameters and not concatenated strings in the implementation of the API, because that is a speed-up, a security-up and good practice in general. |
(0008643) AlexMax (developer) 2014-04-25 12:07 |
Something that key-value databases also tend to have are atomic add/subtract functions: 'http://redis.io/commands/incrby [^]' 'http://redis.io/commands/decrby [^]' The reason these functions exist is because if you do something like: int x = GetRecord("Experience", GetPlayerID()); x = x + 1; SetRecord("Experience", GetPlayerID(), x); ...what happens if both processes try to write to the database at the same time? Nevermind the fact that one player is unlikely to be on two servers at the same time, there are other examples that are probably better (two jumpmaze servers where somebody sets a record on the same map at the same time, you will want to save their time and the player ID, possibly in two separate rows). There's a few questions with non-obvious answers floating around: - If two servers are writing to the same database at the same time, how do we ensure their operations are atomic? - If two servers are writing to the same database at the same time, how do we handle cases where the database is locked out by one process? Or is this even a problem in real world usage? |
(0008644) Torr Samaho (administrator) 2014-04-26 16:05 edited on: 2014-04-26 16:06 |
Quote from AlexMax This sounds like a good idea, but it looks like I can't supply the table name as bound parameter to get rid of this concatenated string construct: "CREATE TABLE %s(KeyName text primary key, Value text)" I didn't find a variant of "CREATE TABLE ?(KeyName text primary key, Value text)" that sqlite3_prepare doesn't immediately refuse to accept. Any ideas? |
(0008645) AlexMax (developer) 2014-04-26 20:41 edited on: 2014-04-26 20:53 |
Unfortunately, you cannot bind a table name parameter. EDIT: Ignore everything I previously said here. I brought this problem up with the denizens of #sqlite, and they protested the entire concept of using tables - instead saying that we should simply add a 'namespace' column and put everything in one gigantic table. This fixes the problem of not being able to bind the table name, and the only real cost is that if you look at the database with a third party tool it'll be kinda confusing with all data in one gigantic table until you SELECT * FROM data WHERE namespace = "Authentication" or whatever. |
(0008647) Torr Samaho (administrator) 2014-04-27 15:29 edited on: 2014-04-27 16:26 |
Makes sense. I rewrote the code accordingly. The functions
could be exported then. To properly use the DATABASE_*Entry functions, we'd also need something like DATABASE_HasEntry. EDIT: Since the database is optional, I added DATABASE_IsAvailable that we should also export via ACS. |
(0008648) Torr Samaho (administrator) 2014-04-27 20:39 |
In #zadev we came up with the following: We don't want to expose the difference between Add and Set to the user, so we will just add the functions SetEntry and GetEntry (both as string and int versions) and DeleteEntry. |
(0008658) jam (reporter) 2014-05-01 10:54 edited on: 2014-05-01 11:08 |
I saw the code, looks good :D once it's in working condition then you can also make sure to prevent SQL injections (since you're sending raw SQL commands to sqlite3), though it's being prevented by binding values to the statement string, there are still ways (http://www.sqlite.org/limits.html). Also if we're gonna have a registration page for the account system, that brings out further security concerns (CSRF, XSS etc) |
(0008659) Torr Samaho (administrator) 2014-05-01 11:23 |
I recently rewrote the code to use sqlite3_bind_XXXX for all user supplied parameters. If you have specific suggestions on how to further improve the code just let me know. |
(0008661) Torr Samaho (administrator) 2014-05-01 16:07 edited on: 2014-05-01 16:08 |
I finished a first version of the ACS interface. The following functions are supported at the moment:
There are three new cvars - databasefile controls where the database is saved (defaults to ":memory:", i.e. volatile in-memory database) - authhostname is the masterhostname analog (port can be specified with ":port") - sv_forcelogintojoin prevents unauthenticated player from joining the game (can still connect as spectators) The console command login allows the client to authenticate with the auth server the server has selected with its authhostname setting. |
(0008665) AlexMax (developer) 2014-05-01 22:30 |
Qent brought up an interesting question a while ago: > I think it would be great if accounts were by default tied to forum+tracker accounts, and possibly requiring the user to click a button on the User Control Panel to opt in to the in-game account. Do you guys still want this? Odamex has indicated that they would prefer integrated accounts that work on forums, the bugtracker, and so on, but I am actually working on a simplistic web interface for the auth server in instances where integration is not desired. Important additional question - if implemented, how would this apply to players who are banned from the forums but not the master? |
(0008666) Catastrophe (reporter) 2014-05-01 22:40 edited on: 2014-05-01 22:57 |
No. Using the user CP on the forums is a terrible and obscure way to opt in for accounts. Personally I say that it should ask you to log in if you log into a server that takes advantage of accounts and it should have an option to automatically log in; of course only if this doesn't slow down progress. |
(0008667) AlexMax (developer) 2014-05-02 01:54 |
Yeah the method of opting in that was suggested was kinda eh, I was more talking about is integration with the zandronum forums/tracker a good thing in the first place. |
(0008668) Blzut3 (administrator) 2014-05-02 02:48 |
Yeah, if it's implemented the central account system would create and authenticate forum and tracker accounts, not vice versa. |
(0008671) jam (reporter) 2014-05-02 19:59 |
Maybe we should allow registering via the console? This would be the least intrusive for players who join servers which force registrations. |
(0008676) Torr Samaho (administrator) 2014-05-03 08:46 |
I think we should not force users to use an account for all our services (forum, tracker, in-game accounts), but of course having it as an option would be fine. Registering via the console is an interesting idea, but for this the client would need to talk to the auth server directly. Quote from AlexMax I think that's exactly what we need now to get things started. |
(0008694) AlexMax (developer) 2014-05-04 18:55 |
> Maybe we should allow registering via the console? This would be the least intrusive for players who join servers which force registrations. That's not a good idea, imho. How do you securely transport the desired password to the auth server without something like HTTPS? Web registration via HTTPS is the way to go, imho, even if it's not quite as convenient as ingame registration. |
(0008701) Watermelon (developer) 2014-05-05 17:28 |
Is there a reason SQLite is not part of the zan-accounts repo? Not that adding it is any problem by DL'ing the source, just it's convenient if it's supposed to be in there (unless there's a license issue?) |
(0008718) Torr Samaho (administrator) 2014-05-06 06:12 |
I just didn't add the SQLite source because it's rather big and can be dropped in easily. |
(0008879) Watermelon (developer) 2014-06-07 20:53 edited on: 2014-06-07 20:54 |
Nice to see the database in 2.0. Now just to wait for the account systems. |
(0008951) Hypnotoad (reporter) 2014-06-09 08:53 edited on: 2014-06-09 09:01 |
I've been redesigning zanrun to work with the database feature and so far so good, but I've run into an issue. What, if anything, does GetDBEntryString return if the entryname you specify doesn't exist in the table? When I try and check if it's equal to "" or 0 it evaluates to false. How can I check if a value for the entry exists or not when I'm not sure of its contents. edit: zzombo's suggestion of using strlen() fixed this for now |
(0008952) Torr Samaho (administrator) 2014-06-09 09:11 |
FYI, GetDBEntryString returns an empty string in case the entry does not exist. |
(0008955) Hypnotoad (reporter) 2014-06-09 11:19 edited on: 2014-06-09 11:56 |
Can I possibly request some kind of sorting feature? I'd like to be able to sort all of the personal best times on a map of each player's "account" (for now I associate an ID with a player's name and used this for referencing data). I've tried just copying all of the entries into a large array in acs (I gave it a large size like 2000 to capture the amount of names a server might see in its life time), and then using a bubble sorting algorithm to sort it but this quickly results in runaway script terminated. If I stick a delay in there every time the loop count approaches its limit it takes a very long time to sort so there seems to be no good way of sorting arrays in acs at the moment; it seems arrays larger than around 100 entries cannot be effectively sorted. So ideally I'd like to be able to sort the entries in a namespace in the database itself, and say be able to pick the first 5 entries from this namespace so I can get a top 5 highscores list. edit: so the commands could look something like: SortDBNameSpace( string namespace, bool ascending) and GetDBValueAtIndex(string namespace, int index) |
(0008956) Torr Samaho (administrator) 2014-06-09 11:52 |
Having the engine do some kind of sorting shouldn't be that difficult, but what kind of ACS interface do you have in mind? |
(0008957) Hypnotoad (reporter) 2014-06-09 11:53 |
See my edit, edited just as you posted. |
(0008960) Torr Samaho (administrator) 2014-06-09 14:48 edited on: 2014-06-09 15:16 |
You'd also need something like GetDBNameAtIndex, wouldn't you? EDIT: Explicit sorting (and thus SortDBNameSpace) is unnecessary. With
we can just internally ask the database for the entry with the n-th highest (counting starts at 0) value explicitly. |
(0008969) Torr Samaho (administrator) 2014-06-09 17:24 |
I implemented the necessary internal functions to get the n-th highest / lowest entry (see DATABASE_GetSortedEntryAsPair in the accounts fork). So we just need to agree on the ACS interface to let mods make use of it. |
(0008970) Watermelon (developer) 2014-06-09 17:31 |
One issue I see being run into: Inexperienced modders calling the sort function many times over on a database. Does the get n-th entry require a full sort every time it's called? If so, could get messy. Assuming it does, maybe remember when a sort was called and just poll from that until the user calls a new sort request? If not, disregard this post |
(0008971) Torr Samaho (administrator) 2014-06-09 17:38 edited on: 2014-06-09 17:40 |
Quote from Watermelon DATABASE_GetSortedEntryAsPair internally just uses a single SQL query, see 0001176:0008960. I didn't check what exactly SQLite is doing to execute the query, but I assume SQLite is clever enough to handle the query efficiently. In particular, I'm pretty sure that it won't do a full sort for each request, that would be ridiculously inefficient. |
(0008977) Hypnotoad (reporter) 2014-06-09 21:09 |
Quote How about GetDBSortedEntryAtIndex(str namespace, bool ascending, int index) |
(0008978) AlexMax (developer) 2014-06-09 22:25 |
> SortDBNameSpace() > GetDBValueAtIndex() > GetDBSortedEntryAtIndex() I'm sorry, but I'm going to have to disagree with this interface. - Right now, the database is using sqlite3 because it's easy to embed. I do not think that it is outside the realm of possibility that in the future, there will be support for other types of databases. With the key/value store API, not only does it keep our options open for SQL databases, but for key/value stores like Redis, Memcached, LevelDB, BDB and others. In those databases, the sort function you implemented would either be inefficient (since you would need to scan the entire database looking for every key that has the namespace name) or literally impossible (some key/value stores don't even keep track of every key in the database). The key/value API was deliberately kept minimal in order to eventually cover both types of databases. - But let's say we don't care about key/value store databases. Those functions have extremely narrow scopes, and I think that having a bunch of narrowly-focused functions tacked onto a key/value API is bad API design no matter how you slice it. I would much rather see a "beginners" key/value API paired with an "expert" SQL API that allows arbitrary table creation, statement binding and query execution. I'm attempting to think of an alternative - perhaps something along the lines of Redis' Sorted Sets - that allows us to keep using the key/value paradigm without these helper functions and without having to design an SQL API as well. I'll let you know once I have something concrete that satisfies Hypnotoad's needs. |
(0008982) Torr Samaho (administrator) 2014-06-10 06:12 edited on: 2014-06-10 06:52 |
IMO the query "give me the entry with the n-th highest value" is a valid request and useful in a key/value db environment (at least if all the values are integers). Considering the limitations of ACS (for instance, functions only have one simple return value), this could be exposed with a function likeGetDBEntryNameWithNThHighestValue ( str namespace, bool ascending, int N ) Then, one can use GetDBEntryInt with the returned name to also get the value if necessary. How the n-th highest value is actually calculated, cached, etc. internally can still be handled however we desire and in line with the database framework we use. EDIT: Scratch GetDBEntryNameWithNThHighestValue. After thinking more about this, I agree that its scope is too limited and also think that it's too fragile (what happens if the db is changed if you use this function to get the top n entries?). How about this idea: We introduce "prepare" functions that act like a db query that can return multiple element, e.g. "PrepareTopNEntries". It will return the number of entries that matched the request and internally store the resulting entries. To access the prepared entries, we add functions GetPreparedEntryName(int) and GetPreparedEntryValue(int). |
(0008983) Watermelon (developer) 2014-06-10 14:54 edited on: 2014-06-10 14:55 |
1) Will there be string sorting (via natural order)? Albeit not very useful, I'm throwing random ideas out 2) What about PrepareBottomNEntries? 3) What about prepareNEntriesFrom(..., int cutoff); Ex: I want to find everyone above 1000 points, prepareNEntriesFrom(..., 1000); // Inclusive 4) Is there a DB_KeyExists(String table, String key); ? If not we'd probably need that. |
(0009009) AlexMax (developer) 2014-06-10 16:42 |
> IMO the query "give me the entry with the n-th highest value" is a valid request and useful in a key/value db environment Actually, in most key/value databases, this is not possible and is not a valid request that you can send a database. The API I suggested was designed to be portable to key/value stores as well as SQL databases that emulate one. |
(0009010) Torr Samaho (administrator) 2014-06-10 17:21 edited on: 2014-06-10 17:23 |
Quote from Watermelon That would be easily possible, but can you come up with an example where you want this? Quote from Watermelon I intended to have a bool argument "ascending" so that you can select the sorting direction. Quote from Watermelon One could easily add an offset, i.e. instead of top 1 to N you get top 1+offset to N+offset. Quote from Watermelon This doesn't exist so far and we shouldn't need it. Non-existing entries are simply considered to have "" as string value or 0 as integer value. Prepare* will return the number of entries actually found and there you'll know that they exist. Quote from AlexMax Nevertheless, a mod should be able to ask for the top-n entries. We explicitly want to allow mods to do stats tracking and this kind of request is totally natural in this context. We cannot force mods to do the sorting on their own in ACS, it's simply infeasible. |
(0009026) Watermelon (developer) 2014-06-10 22:23 |
Quote I can't think of any right now. I just like knowing its a possibility in case someone did come up with an idea. Quote For this one, is there an updated ACS instruction set I'm missing that returns how many entries are returned? I was going off of this: 'https://bitbucket.org/Torr_Samaho/zandronum/commits/f2f5a3c65fe4247ec136be2eb7df7cc116b808d6 [^]' Though as a note, upon thinking it over more, there's no reason to worry about a key that has an entry of zero versus the key not existing anyways since I assume SQLite if attempting to increment a non existent key will add it and then increment? So regardless, you'd always get 1 regardless of whether the key was zero, or never existed. All the other responses solve my questions. Thanks! |
(0009027) Hypnotoad (reporter) 2014-06-10 23:07 |
Quote Sounds good to me. |
(0009029) AlexMax (developer) 2014-06-10 23:37 edited on: 2014-06-11 00:39 |
> Nevertheless, a mod should be able to ask for the top-n entries. We explicitly want to allow mods to do stats tracking and this kind of request is totally natural in this context. We cannot force mods to do the sorting on their own in ACS, it's simply infeasible. After much thought, I think that I have managed to pinpoint what my issue is. Ever since the renaming of 'tables' to 'namespaces', I kind of got it in my head that each modification would have its own namespace to itself - i.e. zanrun would always use the namespace of 'zanrun' and never change it. And since each namespace was a key/value store, if a modification needed multiple types of bookkeeping, it would simply prefix the key with something appropriate. However, since you guys need actual sorting and filtering power, suddenly namespaces need to behave like actual tables again, because keys and values aren't enough anymore. I still think that my fear is justified, though. What I'm worried about is namespaces colliding because two mods that name their namespaces poorly (perhaps a map lump name) that share the same database. Here's a concrete example. Let's say that our server loads a stat-tracking mod AND a spree mod that keeps track of the player's highest personal spree on a map. What if both mods have a namespace called HighScoreMAP01? Or just "MAP01"? Of course, if mod makers wanted their mods to play well with others, they would always prefix a constant name to their namespace. But I think it would be a good idea for the API would "enforce" this at some level, through another parameter and a another column in the database - maybe called "section" or something. All filtering and sorting API's would be against sections/whatever, and would not work on namespaces (or whatever we end up calling the 'higher level') at all. Thoughts? |
(0009030) Hypnotoad (reporter) 2014-06-10 23:44 edited on: 2014-06-10 23:44 |
When I open up the database in a browser, the table is given a name called "Zandronum". What if acs libraries can decide the name of the entire table they will use themselves with a declaration at the top, something like #table "Zanrun", and perhaps prevent these database functions from being used if a table name isn't defined, or use the library name by default as the table name. |
(0009031) AlexMax (developer) 2014-06-11 00:04 edited on: 2014-06-11 00:27 |
Something else that occurred to me. Torr, how possible would it be for the ACS interface to incorporate "handles"? Like, instead of returning a single row or keeping global state, what if the sorting function returned a handle to the result set that was just an integer to ACS but pointed to the result set in memory? You could maybe use a GetRow(h) type function to iterate over all the rows for a handle, which would give you a single row handle instead, and then use the row handle to Get<type>Key(rh) and Get<type>Value(rh). The only troublesome thing about this is that you would have to free your handles after using them in the script itself. But being able to do one atomic query and grab everything at once seems like it'd be a smart thing to do. |
(0009032) AlexMax (developer) 2014-06-11 00:11 edited on: 2014-06-11 00:38 |
> When I open up the database in a browser, the table is given a name called "Zandronum". What if acs libraries can decide the name of the entire table they will use themselves with a declaration at the top, something like #table "Zanrun", and perhaps prevent these database functions from being used if a table name isn't defined, or use the library name by default as the table name. We have to use one gigantic table, we can't bind parameters to table names. Which is kind of why the 'namespace' is called that, it's not a real table. See'http://zandronum.com/tracker/view.php?id=1176#c8645 [^]' |
(0009033) Hypnotoad (reporter) 2014-06-11 00:46 |
Alright, well instead of creating a new table, the declaration could define a keyname in the new column you suggested that all the data is stored under. |
(0009097) Torr Samaho (administrator) 2014-06-11 20:08 |
Quote from Watermelon We don't have any function yet that could return multiple entries. I was referring to the non-existent PrepareTopNEntries or similar functions, see 0001176:0008982. Quote from Watermelon SQLite is not doing the increment itself directly, but my implementation in Zandronum is taking care of this, i.e. incrementing a non-existing entry will result in the same as if the entry had the value 0. Quote from AlexMax Ah, I see. I didn't assume this. For the stats tracking I always had the stat you are tracking as namespace, the player account name as key and the value of the stat as value in mind. Quote from AlexMax In that case, the mods will overwrite each others data. You are right, that could be a problem. Quote from AlexMax 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. Quote from AlexMax 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. BTW: Can somebody open a new ticket for the database discussion? This one is getting way too big. |
(0009138) AlexMax (developer) 2014-06-12 01:24 |
I was taking another look over the authentication protocol when I realized that we could probably save some bandwidth and headaches. The "A length", "B length", "M length" and "HAMK length" numbers were all Int32 so they would line up with CSRP. However, UDP has a maximum packet size of 64k and a max practical packet size much smaller than that. Plus, if Zandronum naively reads a size of INT_MAX off the wire, I don't think we want to allocate 2 gigabytes of memory for the buffer. ;) To that end, I have propose we make a small adjustment to the protocol so the "A length", "B length", "M length" and "HAMK length" fields are UInt16's. This would be a trivial change to make on my end. What say you? |
(0009139) Torr Samaho (administrator) 2014-06-12 06:05 |
Changing these lengths to int16 is fine with me. |
(0009546) StrikerMan780 (reporter) 2014-06-17 16:49 edited on: 2014-06-17 16:53 |
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. (A pooled virtual credits system that crosses over into another game. Ie. Getting cash drops from enemies in one game can be used as a means to get cosmetic items in another, to encourage the community I run to give other games a try, without it being mandatory.) |
(0009555) Torr Samaho (administrator) 2014-06-17 18:45 |
All database related issues will be discussed in the new ticket 0001831. See my answer over there. |
(0009775) AlexMax (developer) 2014-06-28 20:11 edited on: 2014-06-28 20:23 |
Right now, I believe that the only thing that the account system works with is ACS. Although this is useful, I think it would also be handy for the account system to designate who is allowed to connect to the server. I can think of a couple different use cases. 1. Servers should be able to only allow authenticated players to connect to the server or join the game. This would be for server owners who want to validate who is playing on their servers. This can also be handy if you are making a mod where you don't want to have to deal with 'anonymous' players. 2. Servers should be able to only allow specific authenticated players to connect to the server or join the game. This would be great for controlling who can play private CTF or who can play in a scrim server while allowing anybody to spectate, as well as being able to know who is who when picking for priv. 3. Servers should be able to hand out RCON to specific authenticated players. Instead of having a single RCON password that everybody shares, what if everybody used their normal accounts and they automatically got RCON access based on their account? Although some of these can be done with simple settings, I think that designating specific people in cases 2 and 3 is an open question. How can you designate who is allowed to connect? It would also be nice if this could be done to a number of servers at the same time without having to restart them. Maybe we could use database support for this...a separate table that contains accounts and their permissions? What about account support in banlist.txt/whitelist.txt/adminlist.txt? |
(0009776) Dusk (developer) 2014-06-28 23:28 |
Regarding banlist.txt and friends, maybe do it inspircd-style and add "R:<account>" to designate authenticated users? |
(0009778) Torr Samaho (administrator) 2014-06-29 10:33 edited on: 2014-06-29 10:35 |
Besides the ACS functions, we have sv_forcelogintojoin right now, but it would surely be good to extend the control the server has based on the accounts. Extending banlist.txt/whitelist.txt/adminlist.txt with account entries sounds like a good start, but to allow / disallow a player to connect to a server based on the player account requires the Zandronum client <-> server protocol to be adjusted. It also makes it necessary that a player can enter the login credentials before connecting to the server. So before we can start with this, we have to agree on how the player should supply the credentials. |
(0009788) AlexMax (developer) 2014-06-29 16:44 edited on: 2014-06-29 16:45 |
I think that it should be possible for Doomseeker to allow logging in, but not required. I think Blzut3 mentioned something about passing a file to execute with the credentials inside. However, do you think it would be better to have a 'auth_username' and 'auth_password' cvar that is set so it doesn't save to the config file? Or do you think perhaps a command would be better, like `login "username" "password"` that sets global variables in C++ that can then be retrieved when necessary, but does not use the cvar system at all? Either way, I always pictured that there would be a menu that comes up when the server demands the client log in before proceeding: --- Log In --- login server [server's login server setting, not editable] login [editbox for user's login] password [editbox for user's password, input shows up as * if possible] [Reconnect] If the client tries to connect to a server that requires logging in, the server can send back a special "Authentication Required" fail netmessage, which contains the server's login server. This forces the client to display this menu. If the user hits "Reconnect", the client tries to reconnect this time indicating that they're supplying credentials, otherwise if the user hits esc the connection attempt fails. I think that something similar should happen for servers that force the player to authenticate before joining. Servers that do not force players to authenticate before joining yet support authentication in the first place should have the "Join Game" menu have an "Authenticate" menu option. |
(0009790) AlexMax (developer) 2014-06-29 19:16 edited on: 2014-06-29 19:16 |
Okay, now that communication between the server and auth server is working again, there are a few more issues that have come to my mind. 1. Username differences. At least on paper, my authentication server tries to store all usernames in a case-insensitive manner, and automatically converts names that it obtains to lowercase. However, when the client uses mixed capitalization when trying to login, the auth server passes back the lowercased username and the authentication process stops there. I imagine that even if the username was "correctly" passed back to the server (i.e. correct case and all), the auth process would fail when the client sends its proof (because the identity is different). You could 'fix' this problem by force-lowercasing the username before you send it, but what if we...say...want to allow player to log in via email address, like they can on the webapp? 2. Login denial. What happens if two players try to log in using the same username at the same time? My server will see two authentication attempts for the same user and create two sessions for the same user. But how does Zandronum know which session to pass to which player? Could you potentially prevent another player from logging in by repeatedly sending bogus login attempts? |
(0009834) Hypnotoad (reporter) 2014-07-04 12:14 edited on: 2014-07-04 15:14 |
The difficulty of setting up charonauth on a local machine makes it hard for me to test the acs interface for the accounts system. I was wondering if there was or could be a way to 'simulate' being logged in to make it easier for modders to test how their code handles accounts. edit: fortunately Positron has set charonauth on his server so I can test it, however this might be useful for other people still. |
(0010184) Tomslucky7 (reporter) 2014-08-12 03:10 |
I would like to see how this is implemented into Zandronum. I am currently playing the Doom 2 Tower Defense mod and want to see how this would work in a multiplayer setting. Would most likely make saving a heck of a lot easier than it is currently with this mod (which is not even possible atm in multiplayer). |
Only registered users can voice their support. Click here to register, or here to log in. | |
Supporters: | Watermelon one_Two Toxicity bluewizard ZzZombo katZune Medicris S.S.M. Vavency Dusk Hypnotoad Esum Karakurt |
Opponents: | Tux roman6a |
Issue History | |||
Date Modified | Username | Field | Change |
2012-11-07 21:26 | Watermelon | New Issue | |
2012-11-07 21:34 | Dusk | Note Added: 0005330 | |
2012-11-07 21:55 | Dusk | Note Edited: 0005330 | View Revisions |
2012-11-07 22:03 | Dusk | Note Edited: 0005330 | View Revisions |
2012-11-07 23:09 | Qent | Note Added: 0005331 | |
2012-11-08 06:22 | Torr Samaho | Note Added: 0005332 | |
2012-11-08 13:49 | Watermelon | Note Added: 0005334 | |
2012-11-08 14:30 | Watermelon | Note Edited: 0005334 | View Revisions |
2012-11-08 20:31 | Torr Samaho | Note Added: 0005340 | |
2012-11-18 10:33 | Torr Samaho | Note Added: 0005412 | |
2012-11-18 10:33 | Torr Samaho | Status | new => feedback |
2012-11-18 18:39 | Dusk | Note Added: 0005417 | |
2012-11-18 19:42 | Torr Samaho | Note Added: 0005418 | |
2012-11-19 02:16 | ZzZombo | Note Added: 0005420 | |
2012-11-21 18:11 | Blzut3 | Note Added: 0005423 | |
2012-11-21 20:17 | Torr Samaho | Note Added: 0005424 | |
2012-11-21 20:18 | Torr Samaho | Note Edited: 0005424 | |
2012-11-21 20:19 | Torr Samaho | Note Edited: 0005424 | View Revisions |
2012-11-21 20:19 | Torr Samaho | Note Revision Dropped: 5424: 0002979 | |
2012-11-21 20:19 | Torr Samaho | Note Revision Dropped: 5424: 0002980 | |
2012-11-21 20:39 | Blzut3 | Note Added: 0005425 | |
2012-11-24 12:49 | Torr Samaho | Note Added: 0005431 | |
2012-12-05 20:40 | Watermelon | Note Added: 0005476 | |
2012-12-05 20:40 | Watermelon | Status | feedback => new |
2012-12-09 09:52 | Torr Samaho | Note Added: 0005491 | |
2012-12-09 09:53 | Torr Samaho | Assigned To | => Torr Samaho |
2012-12-09 09:53 | Torr Samaho | Status | new => feedback |
2012-12-09 09:53 | Torr Samaho | Assigned To | Torr Samaho => |
2012-12-22 05:20 | Watermelon | Note Added: 0005542 | |
2012-12-22 05:20 | Watermelon | Status | feedback => new |
2012-12-26 17:47 | Torr Samaho | Note Added: 0005570 | |
2013-01-30 20:30 | Torr Samaho | Note Added: 0005875 | |
2013-01-30 21:11 | Watermelon | Note Added: 0005877 | |
2013-01-30 22:22 | Torr Samaho | Note Added: 0005879 | |
2013-01-30 22:46 | Watermelon | Note Added: 0005880 | |
2013-01-31 06:29 | Torr Samaho | Note Added: 0005888 | |
2013-02-06 23:35 | Watermelon | Note Added: 0005951 | |
2013-02-06 23:35 | Watermelon | Note Edited: 0005951 | View Revisions |
2013-02-06 23:36 | Watermelon | Note Edited: 0005951 | View Revisions |
2013-02-07 03:34 | AlexMax | Note Added: 0005958 | |
2013-02-07 04:56 | ZzZombo | Note Added: 0005961 | |
2013-02-08 04:00 | AlexMax | Note Added: 0005966 | |
2013-02-08 05:51 | Dusk | Note Added: 0005967 | |
2013-02-10 10:57 | ZzZombo | Note Added: 0005978 | |
2013-03-07 02:29 | AlexMax | Note Added: 0006097 | |
2013-03-28 17:13 | Watermelon | Note Added: 0006179 | |
2013-03-28 17:14 | Watermelon | Note Edited: 0006179 | View Revisions |
2013-03-28 17:15 | Watermelon | Note Edited: 0006179 | View Revisions |
2013-03-29 10:58 | ZzZombo | Note Added: 0006185 | |
2013-03-29 16:07 | Watermelon | Note Added: 0006187 | |
2013-03-30 02:43 | ZzZombo | Note Added: 0006195 | |
2014-01-16 23:41 | AlexMax | Note Added: 0008033 | |
2014-01-17 07:15 | Torr Samaho | Note Added: 0008036 | |
2014-01-17 17:56 | AlexMax | Note Added: 0008043 | |
2014-01-17 21:56 | Blzut3 | Note Added: 0008048 | |
2014-01-18 12:38 | Torr Samaho | Note Added: 0008055 | |
2014-01-20 03:35 | AlexMax | Note Added: 0008118 | |
2014-01-20 20:05 | Torr Samaho | Note Added: 0008124 | |
2014-01-21 23:20 | AlexMax | Note Added: 0008125 | |
2014-01-23 20:36 | Torr Samaho | Note Added: 0008129 | |
2014-01-25 18:43 | Torr Samaho | Note Added: 0008132 | |
2014-01-26 02:12 | AlexMax | Note Added: 0008133 | |
2014-01-26 02:36 | AlexMax | Note Edited: 0008133 | View Revisions |
2014-01-26 02:37 | AlexMax | Note Edited: 0008133 | View Revisions |
2014-01-26 20:26 | Torr Samaho | Note Added: 0008135 | |
2014-01-29 00:03 | AlexMax | Note Added: 0008145 | |
2014-01-29 20:53 | Torr Samaho | Note Added: 0008147 | |
2014-01-30 23:54 | AlexMax | Note Added: 0008149 | |
2014-01-31 05:10 | AlexMax | Note Edited: 0008149 | View Revisions |
2014-02-02 13:30 | Torr Samaho | Note Added: 0008154 | |
2014-02-02 17:16 | Torr Samaho | Note Added: 0008155 | |
2014-02-02 18:45 | Torr Samaho | Note Added: 0008156 | |
2014-02-02 20:56 | AlexMax | Note Added: 0008161 | |
2014-02-02 21:30 | AlexMax | Note Added: 0008162 | |
2014-04-07 16:52 | AlienOverlord | Note Added: 0008511 | |
2014-04-16 02:26 | AlexMax | Note Added: 0008584 | |
2014-04-16 02:27 | AlexMax | Note Edited: 0008584 | View Revisions |
2014-04-16 18:57 | Torr Samaho | Note Added: 0008585 | |
2014-04-17 01:37 | AlexMax | Note Added: 0008593 | |
2014-04-17 01:52 | Blzut3 | Note Added: 0008594 | |
2014-04-17 17:38 | Torr Samaho | Note Added: 0008598 | |
2014-04-17 19:11 | Blzut3 | Note Added: 0008603 | |
2014-04-17 19:17 | Blzut3 | Note Edited: 0008603 | View Revisions |
2014-04-17 20:22 | Torr Samaho | Note Added: 0008605 | |
2014-04-17 20:24 | Blzut3 | Note Added: 0008606 | |
2014-04-18 00:45 | AlexMax | Note Added: 0008607 | |
2014-04-19 07:57 | Torr Samaho | Note Added: 0008608 | |
2014-04-19 08:07 | Torr Samaho | Note Edited: 0008608 | View Revisions |
2014-04-20 00:17 | AlexMax | Note Added: 0008609 | |
2014-04-20 00:19 | AlexMax | Note Edited: 0008609 | View Revisions |
2014-04-20 02:29 | jam | Note Added: 0008611 | |
2014-04-20 13:02 | Torr Samaho | Note Added: 0008616 | |
2014-04-20 17:18 | Torr Samaho | Note Added: 0008620 | |
2014-04-20 21:33 | AlexMax | Note Added: 0008622 | |
2014-04-20 21:40 | AlexMax | Note Edited: 0008622 | View Revisions |
2014-04-20 22:22 | AlexMax | Note Edited: 0008622 | View Revisions |
2014-04-21 10:31 | Torr Samaho | Note Added: 0008623 | |
2014-04-23 00:32 | AlexMax | Note Added: 0008627 | |
2014-04-25 12:07 | AlexMax | Note Added: 0008643 | |
2014-04-26 16:05 | Torr Samaho | Note Added: 0008644 | |
2014-04-26 16:05 | Torr Samaho | Note Edited: 0008644 | |
2014-04-26 16:06 | Torr Samaho | Note Edited: 0008644 | View Revisions |
2014-04-26 16:06 | Torr Samaho | Note Revision Dropped: 8644: 0004669 | |
2014-04-26 16:07 | Torr Samaho | Note Revision Dropped: 8644: 0004670 | |
2014-04-26 20:41 | AlexMax | Note Added: 0008645 | |
2014-04-26 20:47 | AlexMax | Note Edited: 0008645 | View Revisions |
2014-04-26 20:53 | AlexMax | Note Edited: 0008645 | View Revisions |
2014-04-27 15:29 | Torr Samaho | Note Added: 0008647 | |
2014-04-27 16:26 | Torr Samaho | Note Edited: 0008647 | View Revisions |
2014-04-27 20:39 | Torr Samaho | Note Added: 0008648 | |
2014-05-01 10:54 | jam | Note Added: 0008658 | |
2014-05-01 11:08 | jam | Note Edited: 0008658 | View Revisions |
2014-05-01 11:23 | Torr Samaho | Note Added: 0008659 | |
2014-05-01 16:07 | Torr Samaho | Note Added: 0008661 | |
2014-05-01 16:08 | Torr Samaho | Note Edited: 0008661 | View Revisions |
2014-05-01 22:30 | AlexMax | Note Added: 0008665 | |
2014-05-01 22:40 | Catastrophe | Note Added: 0008666 | |
2014-05-01 22:57 | Catastrophe | Note Edited: 0008666 | View Revisions |
2014-05-02 01:54 | AlexMax | Note Added: 0008667 | |
2014-05-02 02:48 | Blzut3 | Note Added: 0008668 | |
2014-05-02 19:59 | jam | Note Added: 0008671 | |
2014-05-03 08:46 | Torr Samaho | Note Added: 0008676 | |
2014-05-04 18:55 | AlexMax | Note Added: 0008694 | |
2014-05-04 20:04 | Dusk | Relationship added | related to 0001794 |
2014-05-05 17:28 | Watermelon | Note Added: 0008701 | |
2014-05-06 06:12 | Torr Samaho | Note Added: 0008718 | |
2014-06-07 20:52 | Watermelon | Relationship added | related to 0000204 |
2014-06-07 20:53 | Watermelon | Note Added: 0008879 | |
2014-06-07 20:53 | Watermelon | Status | new => needs testing |
2014-06-07 20:54 | Watermelon | Status | needs testing => new |
2014-06-07 20:54 | Watermelon | Note Edited: 0008879 | View Revisions |
2014-06-09 08:53 | Hypnotoad | Note Added: 0008951 | |
2014-06-09 09:01 | Hypnotoad | Note Edited: 0008951 | View Revisions |
2014-06-09 09:11 | Torr Samaho | Note Added: 0008952 | |
2014-06-09 11:19 | Hypnotoad | Note Added: 0008955 | |
2014-06-09 11:20 | Hypnotoad | Note Edited: 0008955 | View Revisions |
2014-06-09 11:52 | Torr Samaho | Note Added: 0008956 | |
2014-06-09 11:52 | Hypnotoad | Note Edited: 0008955 | View Revisions |
2014-06-09 11:53 | Hypnotoad | Note Added: 0008957 | |
2014-06-09 11:56 | Hypnotoad | Note Edited: 0008955 | View Revisions |
2014-06-09 14:48 | Torr Samaho | Note Added: 0008960 | |
2014-06-09 15:15 | Torr Samaho | Note Edited: 0008960 | View Revisions |
2014-06-09 15:16 | Torr Samaho | Note Edited: 0008960 | View Revisions |
2014-06-09 17:24 | Torr Samaho | Note Added: 0008969 | |
2014-06-09 17:31 | Watermelon | Note Added: 0008970 | |
2014-06-09 17:38 | Torr Samaho | Note Added: 0008971 | |
2014-06-09 17:39 | Torr Samaho | Note Edited: 0008971 | |
2014-06-09 17:39 | Torr Samaho | Note Revision Dropped: 8971: 0004871 | |
2014-06-09 17:40 | Torr Samaho | Note Edited: 0008971 | View Revisions |
2014-06-09 17:41 | Torr Samaho | Note Revision Dropped: 8971: 0004872 | |
2014-06-09 21:09 | Hypnotoad | Note Added: 0008977 | |
2014-06-09 22:25 | AlexMax | Note Added: 0008978 | |
2014-06-10 06:12 | Torr Samaho | Note Added: 0008982 | |
2014-06-10 06:51 | Torr Samaho | Note Edited: 0008982 | View Revisions |
2014-06-10 06:52 | Torr Samaho | Note Edited: 0008982 | View Revisions |
2014-06-10 14:54 | Watermelon | Note Added: 0008983 | |
2014-06-10 14:55 | Watermelon | Note Edited: 0008983 | View Revisions |
2014-06-10 16:42 | AlexMax | Note Added: 0009009 | |
2014-06-10 17:21 | Torr Samaho | Note Added: 0009010 | |
2014-06-10 17:22 | Torr Samaho | Note Edited: 0009010 | |
2014-06-10 17:23 | Torr Samaho | Note Edited: 0009010 | View Revisions |
2014-06-10 17:24 | Torr Samaho | Note Revision Dropped: 9010: 0004891 | |
2014-06-10 17:25 | Torr Samaho | Note Revision Dropped: 9010: 0004892 | |
2014-06-10 22:23 | Watermelon | Note Added: 0009026 | |
2014-06-10 23:07 | Hypnotoad | Note Added: 0009027 | |
2014-06-10 23:37 | AlexMax | Note Added: 0009029 | |
2014-06-10 23:41 | AlexMax | Note Edited: 0009029 | View Revisions |
2014-06-10 23:44 | Hypnotoad | Note Added: 0009030 | |
2014-06-10 23:44 | Hypnotoad | Note Edited: 0009030 | View Revisions |
2014-06-11 00:04 | AlexMax | Note Added: 0009031 | |
2014-06-11 00:11 | AlexMax | Note Added: 0009032 | |
2014-06-11 00:27 | AlexMax | Note Edited: 0009031 | View Revisions |
2014-06-11 00:38 | AlexMax | Note Edited: 0009032 | View Revisions |
2014-06-11 00:39 | AlexMax | Note Edited: 0009029 | View Revisions |
2014-06-11 00:46 | Hypnotoad | Note Added: 0009033 | |
2014-06-11 20:08 | Torr Samaho | Note Added: 0009097 | |
2014-06-11 21:54 | Watermelon | Relationship added | related to 0001831 |
2014-06-12 01:24 | AlexMax | Note Added: 0009138 | |
2014-06-12 06:05 | Torr Samaho | Note Added: 0009139 | |
2014-06-14 03:24 | Watermelon | Relationship added | related to 0000295 |
2014-06-17 16:49 | StrikerMan780 | Note Added: 0009546 | |
2014-06-17 16:50 | StrikerMan780 | Note Edited: 0009546 | View Revisions |
2014-06-17 16:53 | StrikerMan780 | Note Edited: 0009546 | View Revisions |
2014-06-17 18:45 | Torr Samaho | Note Added: 0009555 | |
2014-06-28 20:11 | AlexMax | Note Added: 0009775 | |
2014-06-28 20:23 | AlexMax | Note Edited: 0009775 | View Revisions |
2014-06-28 23:28 | Dusk | Note Added: 0009776 | |
2014-06-29 10:33 | Torr Samaho | Note Added: 0009778 | |
2014-06-29 10:35 | Torr Samaho | Note Edited: 0009778 | View Revisions |
2014-06-29 10:35 | Torr Samaho | Note Revision Dropped: 9778: 0005231 | |
2014-06-29 16:44 | AlexMax | Note Added: 0009788 | |
2014-06-29 16:45 | AlexMax | Note Edited: 0009788 | View Revisions |
2014-06-29 19:16 | AlexMax | Note Added: 0009790 | |
2014-06-29 19:16 | AlexMax | Note Edited: 0009790 | View Revisions |
2014-07-04 12:14 | Hypnotoad | Note Added: 0009834 | |
2014-07-04 15:14 | Hypnotoad | Note Edited: 0009834 | View Revisions |
2014-08-12 03:10 | Tomslucky7 | Note Added: 0010184 | |
2024-12-03 22:07 | DrinkyBird | Relationship added | related to 0004452 |
Copyright © 2000 - 2025 MantisBT Team |