[web] How do Browser games implement 'tickers'?

Started by
39 comments, last by louissan 13 years ago
Quote:Original post by sooner123
Quote:Original post by phresnel
Quote:Original post by sooner123
I am the server.

Huh?

Quote:I actually thought php scripts timed out after 30 or 60 seconds or something.

This is configurable. In theory, you can make that it never times out. In practice, it might depend on your hoster.


I am running the server.. on my own computer. I said that early on this thread but everyone seems to think I'm accessing a remote host.

If I disable timeouts.. it'll be disabled for anyone else accessing my server too?

I was hoping there was a command line argument to disable the timeout for that one call, but to leave the timeout active since it's an important feature.


you can pass a specific php.ini to the commandline, but...

Since what you really want to do is control the timeout on a per script basis the easiest solution is to call:
set_time_limit(0);
from the php script itself and then only run that php script from the cli.

As for running your own server, its something most of us do while we develop stuff (And i atleast assumed it was only for development purposes in your case aswell), most switch to external hosting services to get better uptime and reliability once the project is ready to be deployed (or as it grows in scale), alot of isps also won't allow you to run commercial services on their connections (Which pretty much removes your ability to generate revenue).
[size="1"]I don't suffer from insanity, I'm enjoying every minute of it.
The voices in my head may not be real, but they have some good ideas!
Advertisement
This isn't about an operation five times vs. a different operation that comes to the same answer.

But my point is why not? What can you possibly be doing that can't be rewritten as an algorithm to calculate X number of iterations?

This is about calling update.php once per minute for five minutes, vs. calling it 5 times in a row. I don't see a difference here.

There isn't one, per se, except for the server load. Calculating that calls for a lot more data on estimated stats and execution speeds than I feel like making up for an example.

Also I think you'd have to append 31 zeros for your example, which is really just showing that squaring numbers that are 1 followed by zeroes is easy in any base. It's not a shortcut.

Whoops -- yeah, did the wrong squares. The point is that it IS a shortcut: Shifting bits is a common optimization (Shifting left/right N places is the same as multiplying/dividing by 2^N) that most compilers (C/C++ at least) now do themselves because it's faster than actual multiplication or division. Different bases lend themselves to different mathematical tricks, but as long as computers run in binary those tricks are the fastest.

Switching to a different base doesn't help you at all since you still have to do the work of converting it back to the base you're interested in which is even more work than manually squaring 2, 5 times in decimal

Maybe that's easier for you, but the base conversion was to draw your attention back to the computer. It's running in binary, so what makes sense to you on paper isn't necessarily the same thing that makes sense in a computer. Maybe they don't teach the bit shifting thing anymore? I thought you might recognize the example.

But whatever, just go with what works. You can always optimize later! I'm glad you've gotten it working.
Quote:Original post by jolid
This isn't about an operation five times vs. a different operation that comes to the same answer.

But my point is why not? What can you possibly be doing that can't be rewritten as an algorithm to calculate X number of iterations?

This is about calling update.php once per minute for five minutes, vs. calling it 5 times in a row. I don't see a difference here.

There isn't one, per se, except for the server load. Calculating that calls for a lot more data on estimated stats and execution speeds than I feel like making up for an example.

Also I think you'd have to append 31 zeros for your example, which is really just showing that squaring numbers that are 1 followed by zeroes is easy in any base. It's not a shortcut.

Whoops -- yeah, did the wrong squares. The point is that it IS a shortcut: Shifting bits is a common optimization (Shifting left/right N places is the same as multiplying/dividing by 2^N) that most compilers (C/C++ at least) now do themselves because it's faster than actual multiplication or division. Different bases lend themselves to different mathematical tricks, but as long as computers run in binary those tricks are the fastest.

Switching to a different base doesn't help you at all since you still have to do the work of converting it back to the base you're interested in which is even more work than manually squaring 2, 5 times in decimal

Maybe that's easier for you, but the base conversion was to draw your attention back to the computer. It's running in binary, so what makes sense to you on paper isn't necessarily the same thing that makes sense in a computer. Maybe they don't teach the bit shifting thing anymore? I thought you might recognize the example.

But whatever, just go with what works. You can always optimize later! I'm glad you've gotten it working.


Yeah I was just nitpicking with specifics. I did get your point and agree entirely and appreciate the advice.
Quote:Original post by SimonForsman
set_time_limit(0);
from the php script itself and then only run that php script from the cli.


This does not work in safe mode, though. Also, are you sure you can override the php.ini's max-execution-time to a larger time, if set?
I just tested it.

set_time_limit(0); overrode php.ini's time limit.
Hey there. Stumbled by, and thought I would show this topic some love.

From a performance standpoint, in your typical PHP+MySQL web application, updating the game state on demand is an extremely bad idea, on par with holding a metal pole in a thunderstorm or bathing in shark-infested waters with a bathing suit made of fresh seal meat. You can try it, and you can make it work (if your server is hideously over-powered for the number of users you have), but it has severe scaling issues.

The first problem with on-demand updates has already been mentioned: it might take a while between sessions. The next user to come online has to suffer a huge performance hit to let the game server catch up with the current state. Not because the language is slow or because it's doing a+=1 five times instead of a+=5 once (all of this can be optimized, although not necessarily for free) but because a massively concurrent web application needs to save the data back to the database, and writing data to MySQL is insanely expensive. If your application has to perform even one indexed INSERT per interaction, then a single user catching up on all the interactions for an hour of idle time will have one hell of a wait time. And without one INSERT per interaction, good luck implementing the «here's what happened while you were away» feature on your game.

But that's not the worst issue (you can kind-of-solve-it by making sure someone requests an update every X seconds, just like the Original Poster is trying to solve it here).

The second problem with on-demand updates is stampeding (or serialization, depending on how you look at it). Stampeding basically happens like this:
  • User A connects to the server, notices that some updates should be done, and starts performing them transactionally.

  • User B connects to the server. Since User A is acting transactionally, User B sees the same needs-to-be-updated game state and starts updating it transactionally. This doubles the performance load of the update.

  • More users connect. Since the performance load increased, the updates take longer, so there's a higher probability of starting an upload that is already being processed. The server load increases until it basically gives up and takes its own life in frustration (yes, MySQL does that).


Depending on what your updates look like and how your transaction isolation level is configured, you might experience serialization instead: User A connects and does an update. User B connects and reads the "Todo" table(s) but is locked out because User A is working on it, so User B idles until User A is done. The lock on the "Todo" table(s) means your server can now only serve one user at a time. This is a major performance problem if you intend to serve more than a handful of simultaneous users, because locking problems can not be solved by adding more hardware.

The ideal architecture for a web game is to have a single update "thread" (might be a cron job or a script running without a time limit) that polls the database for things to be done and does them, and any number of threads to serve the HTTP requests from the players by reading data from the database and using lock-free, possibly delayed, cluster-indexed INSERTs to tell the update thread what to do. If you have the money, replicate the main database on another server and have all the players read from the slave to lighten the load and read locks on the master (if the server holding the update thread is powerful enough, you can scale your game to infinity). If you don't have that kind of money, and you see the update thread getting locked out too often, you might want to set lower a thread isolation level (READ COMMITTED) to reduce that (at the cost of sometimes displaying some inconsistent data on the user's screens).

In practice, it also helps if the game is designed so that most updates cost nothing. This can be done with two techniques:

  • Do not compute or store anything the game logic does not need. If you don't need it to run the game logic, then you can compute it on demand when (and if) the user asks to see it and discard it afterward. For instance, if your units gain ranks based on the number of enemies they kill, then you do need to compute and store the number of kills, but you don't have to find out the name of the current rank until the user needs to see it.

  • Use "do not open until" timestamps. Instead of the user inserting a "Build Space Station" order in an order table, they should instead insert a "Space Station" building in the building table with a timestamp of NOW() + buildtime. This means the act of successfully building the space station does not involve any computation (at the cost of an additional timestamp test when reading from the building table, but that can be easily indexed).


These will decrease the load on your server.

By the way, the default time limit for a PHP script running in CLI is infinite (and defaults to 30 seconds for CGI and Apache mod_php), so you don't have to configure anything.
Is there a way I could get my never-ending update.php to output some results to the page.. as it ticks away?

As far as I know, there is no html digest until the PHP script is finished.

Do I need to make an ajax page for myself to continually spit out status updates of the never-ending update.php?

As in: update.php loops forever, updating every X minutes or so.
Meanwhile, real_time_status.html uses ajax to query the server and see when the last successful update was, every second and paste it on the page &#106avascript style?
I found this thread to be pretty helpful as I come up with a way to implement "ticks" in my browser game.

However I am not clear on something.

Example: Players gain resources at a rate of x/hour and a player's global high score is calculated from total resources. If the player begins construction of a building that increases that rate by 1/hour, and is told that it will take 12 hours for the building to complete and start conferring the new production rate, what happens when that player goes on a business trip and doesn't come back for a couple of days (48 hours)? Other players will be able to look at the "top player scores", but if there is no update being run (since the player is away on business), there will be more than 24 hours where the player is ranked much lower because there was nobody to cause an update to his building and the last couple days' of resource production. How is this resolved in a "Just in time" updating system. It seems to me that a cron job or background updater running at intervals is required to keep everyone in sync and make sure that scores are correct.

Example: Players have "assistant" NPC characters that have a lifespan. If the NPC "dies" while the player is not logged in, how is that resolved in a "Just in time" update system? The same goes for births. If two "married" NPCs have offspring while player is away, something has to generate a new character during the player's absence. The only way i can imagine this working is with a cron job, otherwise these characters simply do not exist until the player logs in again. It could even be possible that a character will be born and die before the player logs in again, so the player would need to be notified when they log in again of both the person's birth and death. However, with the just in time method of updates, NPCs could only be "born" when a player runs and update, since logging in and hence running the update script causes a rash of births and deaths.

Example: A player wants an email notification when a particular resource threshold or event occurs while they are away. If updates to the players' game only happen when they are logged in and causing updates, they will never get an email, or they will simply get all the emails at once the next time they log in. This seems to require a cron job and I do not know how it is resolved with just in time updating.

Those are some examples that I could think of off the top of my head. I really am having a lot of trouble finding resources about how these many browser game companies are handling their backend infrastructure and updates. It seems to be either a closely guarded secret or something that nobody has been able to figure out how to do outside the confines of internal development studios.

If any of you know any resources I could look at to help me wrap my head around this problem of "persistent" browser games, ticks, updates, etc. I would be extremely grateful.
...Example: A player wants an email notification when a particular resource threshold or event occurs...This seems to require a cron job and I do not know how it is resolved with just in time updating....


You answered your own question.

The magic word here is: "GAME DESIGN".

Start designing your browser based game AROUND these problems! Do not want to cramp your ideas into a framework, which isn't supposed to support such requirements.

If you want player ranks updated in realtime, than you don't want a browser based game with ticks per hour.
If you want email notification on events, than you don't want a browser based game with updates per user request.
If you just need updates per user request, than you don't need cron jobs.
etc.......

The "big" companies don't have magic solutions to these kind of requirements, they just use the right tool for the right job. And they know, what they have at disposal, so they can design their games accordingly.
[Note: I'm not a PHP guy, so my response may not be applicable]
I think people trying to develop persistent games in ASP/PHP.ASP.net often miss the mark because they do not separate game logic from presentation logic. These 'languages' are scripting languages used to generate dynamic HTML. Sure you can use them to also do simple work, but in a moderately complex persistent game, you need to have a dedicated persistent engine running outside the confines of these languages.

Sure you can do simple persistent games (like those Facebook type games or Conquer-Club where events are very timed or user initiated), however in persistent games where there are self-sustaining/controlled NPCs and game triggered events that require no user interaction, ASP/PHP/ASP.net is a poor choice. I don't know about PHP, but ASP.net has support for multi-threading, but even that is a poor way of implementing a persistent engine.

You need to approach game development like you would approach any other application development. In proper application development, you should separate your business logic (or in this case, game logic) from the presentation. With that approach in mind, any ASP/PHP/ASP.NET developer should see PHP has the means of presenting the state of the game to the user, not the game itself. You should develop the game using a different technology that can run continuously in its own process space.

The trick comes in when you want your presentation layer to communicate with your logic layer. This can be done through a few different means:
- Game engine saves the game state to a persistent medium (i.e. a Database) for read-only retrieval from the presentation layer. Actions are sent to the engine through a channel such as a queue table that the game engine pings for actions to perform.
- Any presentation layer communicates with the game logic through the use of a custom protocol over a TCP socket. The HTTP request comes in, it opens a connection to the engine server, sends the action to the server, the server sends some sort of response, the presentation layer takes that response and presents it back to the user.

Both of these implementations, in my opinion, are better than trying to hack PHP/ASP/ASP.net into doing something that it was never designed to do. You'll drive yourself crazy trying to implement something as simple as an Update/Heartbeat event when it can be done easily and quickly using another approach.

Dino M. Gambone
Good judgment is gained through experience. Experience, however, is gained through bad judgment.

Currently working on Rise of Praxis MUD: http://www.riseofpraxis.net/

This topic is closed to new replies.

Advertisement