Amiga.org
The "Not Quite Amiga but still computer related category" => Alternative Operating Systems => Topic started by: motorollin on May 02, 2006, 07:44:07 AM
-
OT I know, but I don't really know where else to ask. I tried a PHP forum but the responses I got weren't very helpful.
I'm writing an online, multiplayer text-based game. My aim is for the game to be totally cross platform, including Amiga, PC, Mac, *nix boxes using Lynx, PDAs, etc etc. The game is therefore written in PHP with a MySQL back end, with all output being plain text and all input being hyperlinks which pass commands back into the PHP script in the URL. All very compatible and easy so far :-)
My problem is that, if I understand correctly, in order to make the game multi-user, I have to implement sessions on the site so that people can log in to it and remain logged in as they move around the site. I have read the PHP manual on sessions, but still don't understand! I was hoping someone who has actually implemented sessions in PHP could point me to a simple tutorial, or better yet explain exactly what I need to do.
Thanks in advance for any help!
--
moto
-
Hey Moto,
Ok, here's what you need to understand. When someone is playing your game, their web browser will not maintain a permanent connection to your server. Instead, every time they click on a link they will make a brief connection, send and receive some information, and then close the connection.
This creates a problem because to maintain a history for each player (ie, to know what they previously did in the game), you will need to be able to identify the user each and every time they connect.
To solve the problem, you do this:
* You ask the user to login, ie, they enter an email address and a password and then hit a submit button to send the info to your server.
* PHP then reads the email and password and validates it against some kind of database.
* If their login is correct, you create some kind of unique ID in PHP - this could be a random sequence of numbers or characters or both, and then you write this uniqueID to a cookie in their browser.
* From then on, every time the user clicks on a link you can read your cookie, which is sent automatically by the browser to the server with each request. Then you know who they are, so you can make your game react accordingly.
If you need an actual code sample of how to do this you can email me via nycran at yahoo dot com
Cheers.
-
Hi Nycran! Thanks for your reply - really helpful! Checking the username I can do, that's easy. ("users" table in database with username/password fields, simple MySQL query and PHP code to check they match.)
A couple of questions about the rest:
1. I can use PHP to create a random string to ID the user. Would I then need to write this to their record in the "users" table to link the ID with the user?
2. If the ID is stored as a cookie, what would in theory stop the user from modifying the contents of the cookie to another user's ID and assuming their identity? I guess this is made less likely by making the ID string extremely long, but it's still a possibility, right?
Thanks again!
--
moto
-
The big difference between sessions and cookies is that with a session, data is retained on the server, and the session will expire in a fairly short time, making it ideal for secure data transactions, or if someone will use a public computer to access their account. Only the session number is sent from a member's computer to the server (which, itself, is stored as a cookie). A cookie has a longer login record (depending on the lifespan you specify), and the data is re-transmitted to the server every time they send an HTTP request. It's customary to give a member a choice when they log in.
If the ID is stored as a cookie, what would in theory stop the user from modifying the contents of the cookie to another user's ID and assuming their identity? I guess this is made less likely by making the ID string extremely long, but it's still a possibility, right?
The likliness of guessing a random number is virtually nil, but if you use a hash to encode a user name or password (such as MD5, and especially Crypt), it's easy to guess how things work internally and reproduce the hash. How easy that is depends if your project is open source or not.
People can edit cookies easily. In fact, Firefox has a number of plugins to directly manipulate POST data on-the-fly.
-
Ok, I think I understand the process now.
1. PHP script checks for a session ID stored as a cookie on the user's machine. If this is found, then the session is re-established so the user is logged on without having to type logon credentials. This is done every time a page is loaded to keep the user logged on.
2. If no cookie is found, the user is prompted for logon credentials. These are validated by the PHP script. Assuming the logon credentials match, a session ID is generated and stored as a cookie. Question: presumably this session ID needs to be stored on the server for later checking. Where is it kept?
Cheers
--
moto
-
The session ID and data are managed directly by PHP, according to the settings in the php.ini file. PHP can store the data lots of different ways. All you have to do is start a session, and PHP will manage all the numbers internally, for every page view.
As if registered globals wasn't enough indication, PHP sessions are just a bit too automatic for their own good, so read the manual carefully before going down this bumpy road.
PHP Sessions (http://us3.php.net/manual/en/ref.session.php)
PHP Session functions (http://us3.php.net/manual/en/function.session-start.php)
There are a few notable problems:
Only one session may be open at a time per member, so if you use frames, you're asking for trouble. "session_write_close()" exists to ease the pain.
Only one database connection per session.
Redirects do not re-transmit session data. You have to include the session ID in the redirect URL using "session_id()", or simply the constant "SID", which is defined when a session has been opened.
Also note that you can't really tell if a session is valid or not unless you test a variable with "isset($_SESSION['blah'])". The function "session_start()" returns TRUE no matter what. PHP's support for sessions changes all the time, so, like JavaScript, it's a good idea to test that things are valid before you use them, even though almost everything is server-side.
-
Thanks for the info. I'm still confused by this, but feel a bit more ready to tackle the PHP Sessions documentation (again).
Is there a reason why this would not work:
1. User is asked to log in
2. Logon credentials are validated
3. Unique session ID generated and stored both as cookie and also in the user's record in the "users" table of the database
4. Every time a page is loaded it retrieves the session ID from the cookie, then searches the users table and finds which user is associated with that session ID
5. The site acts as though that user has logged on
.....
6. User returns to site for another visit
7. Cookie still exists - goto 4
.....
8. User returns to site for another visit
9. Cookie has expired - goto 1
--EDIT
I've done a diagram (http://www.mashley.net/session.jpg) of how I see this working.
Cheers
--
moto
-
motorollin wrote:
3. Unique session ID generated and stored both as cookie and also in the user's record in the "users" table of the database
4. Every time a page is loaded it retrieves the session ID from the cookie, then searches the users table and finds which user is associated with that session ID
moto
searching the db for session id for each page hit is not a good idea. I don't know how much server load you expect but this will probably choke the system. I suggest you keep them in a global array (which I believe PHP does internally). I tried to do something very similar a few years ago, but I guess I had some problem with the global session id array. (I can't remember at the moment)
ps : sorry for highjacking the thread
-
Don't apologise - I appreciate all the input :-) There is lots of information which is read from the database each time a page loads or is refreshed - i.e. the user's status in the game. Checking a session ID would be just one more of many transactions.
Currently, the page loads immediately. However, this is with just me using the database. I understand that the more people there are logged on the slower it will be. Maybe by the time there are enough people playing the game to make a difference to the speed of the database I will be able to afford to upgrade the server :-)
--
moto
-
It all depends on how much security you want for your session.
In the low security scenario you can hit the user table in the database only on login verification and then keep all the required user data in the session variables (not their password or other sensetive data) and simply assume nobody has hijacked the session (or will).
The only other time you would need to hit the user table in this scenario is when you need to save off some user data, like a change in preferences etc. You could also serialize the session into a string for database storage on logout too so that it can be reconstructed when they next log in.
In the higher security scenario, you keep more information in the database and trust the session less, using it only to identify the user (and perhaps validating both the session, IP address and user agent etc against those that were logged in with).
It's a trade off between performance and security. Of course the worst that can happen with a session hijack in this situation (assuming you have no special privileges for some users) is that someone manages to cheat on your game by pretending to be someone else ;-)
-
Hi Karlos
The game is very database intensive and will require access to the database to do anything. There are not really any user variables, only the ones used to log in. So changing the way users log in won't really affect the efficiency of the code.
You are correct, that assuming somebody else's identity in the game will only allow them to cheat. That's not ideal, but I think I can minimise the risk.
Thanks everyone for your help!
--
moto
-
Thanks again everyone. I got it working using the diagram I drew (setting sessionID cookie and storing in user table, then comparing them each page refresh to keep the user logged on). As I said the game is database hungry anyway so I don't think this one extra step will make much of a difference.
--
moto
-
Hey Moto, glad that you've got it going and glad I could help.
In my experience validating the user with each page request is not a significant overhead at all. Any database worth a grain of salt (SQL Server, Oracle, MySQL, Postgres) with an indexed users table will perform this query in microseconds, even if there's hundreds of thousands of records. I manage databases at work with over 40 million records and we have no trouble at all. Queries that contain 20 correlated subqueries... now THEY create overhead! :-D
Cheers,
Andy.
-
Thanks Nycran :-)
What's a correlated subquery? My guess would be where you run a query to find a value from the database (e.g. query on session ID to find the user's name) then feed that value back in to another query (e.g. query on username to get the user's status in the game).
I'm probably totally wrong though :-)
--
moto
-
if you're worried about performance on the webserver, just set up a dedicated database server
-
What's a correlated subquery? My guess would be where you run a query to find a value from the database (e.g. query on session ID to find the user's name) then feed that value back in to another query (e.g. query on username to get the user's status in the game)
Spot on. Consider the following:
SELECT username, password, (SELECT MAX(id) FROM userlog WHERE username = users.username)
FROM users
The second select is correlated with the first via the username = users.username join. You can go nuts with these kinds of queries, correlating all over the show, but it does come with a performance cost.
-
I didn't know you could nest queries like that. I would do this:
SELECT username FROM users WHERE sessionID = '$_COOKIE['sessionid'];
then store the username in a variable in PHP, then do
SELECT * FROM status WHERE username = '$username';
I guess your way is more efficient (in speed and code).
--
moto