views:

704

answers:

5

I need to design and implement a timer-based game (Flash on client, PHP on server), like Cafe World, i.e. user clicks on a button, waits a few seconds, something happens, and then he can click again. It'll be a simulation of a food production line.

There will be N production line elements, each has a separate timer with different duration, and each can cue up to M operations (first starts immediately, next starts when the previous has finished and so on).

How do I implement the server side of that kind of feature with a database backend? Currently I'm thinking about a counter for the cue, "time left" indication for currently active operation and the time of last update. When an operation is requested, I'd update the "time left" and cue counter using the time elapsed from last update. Any thoughts, comments or better ideas?

Best answer gets the bounty.

A: 

Games like CafeWorld, FarmVille, YoVille and others use a 3-tier architecture. The flash application being a fat(ish!) client with some logic built into it, using data stored on the server, a middle layer which sources the data interface and handles validation rules (such as not being able to use two stoves at once) and a persistence layer which handles the storage of data.

A state is maintained by the client, but the important part is that the client can't write state to the server, only events on the client can trigger changes in the server (middle layer). In this way the middle layer and client both maintain separate timings which are synced at intervals. When these intervals do not line up you will notice in games like FarmVille that you get a message notifying you that the "Game is out of sync".

Tim
How do I handle the intervals? How is the state synchronized between the server and the client?
Pies
You would have to set the intervals. Usually the sync is via some sort of services layer which passes through rich objects which are updated. If you wanted an example in Silverlight look at Entity Framework and RIA Services.
Tim
I'm actually asking about the server side, I can you be more specific about "set the intervals", and what gets synced and how?
Pies
For example, you could set a sync interval on the client of 20s, at this mark you could then check the state of the server against the state of the client. You don't have to do a full state check, but you can get a checksum of both states to ensure they are staying in sync; this will keep users from "cheating" by changing the in-memory state at the client end.
Tim
You don't understand, the Flash app on the client side is only for show. The real game is on the server. I'm not asking about how to make it secure, I'm asking how to handle the timers used by the game. Read the question please.
Pies
+1  A: 

Using a "time-left" field is a bad idea, because that can be hacked on the client-side using a program such a Cheat Engine or the likes.

In your database, you record the time of the last event. Your middleware (see Tim's Answer) still performs the validation, but tells the client whether the requested action can be performed or not. All of your validation needs to be done server-side in the middleware. Upon a new request, the middleware can check a database timestamp, see if the required amount of time has passed, and either perform the request or return an error to the client. Doing the interval check at both the server side prevents the case where your client-side validation is compromised through in-memory patches. Never leave validation up to the client alone.

Kenaniah
The time-left field is on the server.
Pies
+5  A: 

After reading through your question, other answers, and the discussion, I think I finally have a grasp on what you are asking (hence a new answer).

To begin, you need to track an end-user's session. When the game begins, you will have to start a new PHP session and send some sort of session identifier to the flash game. Whenever the flash game pings the server, it will pass along the session identifier so PHP knows which instance of the game it is working with.

The PHP server itself will be a passive entity -- the flash client will be doing the pushing and pulling of data. Let's say a user queues events X, Y, and Z, and they each take 20 seconds... As soon as the user attempts to queue each event, the flash client notifies the server, and the server either records it in the queue, or returns an error due to validation (ie, too many events in queue).

In addition, the client will poll the server every few seconds to check on the status of events. Every single time the client registers a new event, or polls for status, the server is going to check the timestamps in the queue and mark events as finished/etc, and then send a response to the client. This is much cleaner and much more practical than trying to run a script in the background that attempts to update the database every second in real time. The only downside is that events for a specific session aren't processed until a client reconnects if there has been a disconnect for whatever reason. But regardless of the amount of time in between connections, the user will still perceive the server as updating in real time because data is always refreshed just before it is sent back to the client.

If you have any implementation-specific questions, please feel free to post your database schema and/or your server code.

Update: In response to Pie's comment, there are essentially three ways to mark events as finished... 1.) Mark finished events as finished in the database when the client connects to the server for a request, or 2.) automatically mark events as finished by using a background process, or 3.) a combination of both.

If you follow option one and check your event timestamps and mark finished events upon every client connection, your database will be fully up-to-date when serving the client's request. If you follow option two, a PHP script running minutely on cron can insure that the database is up-to-date even if the client disappears, ensuring that your data is usually less than a minute fresh -- regardless of whether the client is present or not.

Regardless of how you are going to be using the data (high scores table, etc), having a combination of both techniques is probably the best idea. Fresh data is always a plus, and periodic incremental refreshes from a cron script never hurt anyone.

As far as marking events as completed is concerned, you merely need to check the relative start time of the event + the duration of the event against the current time. In a queue, the start time for an event is defined as the duration and cool-off times of all prior uncompleted queued events added to the starting timestamp of the first item in the queue.

Kenaniah
Don't forget, you will have to secure the connection between server and client, or cheating will still be easy. Secure means not obfuscation but real encryption! RSA would be appropriate (public key for the client).
DaMacc
The session tracking in PHP is automatic, so no problem there. The server-side obviously will need to be passive because HTTP works that way. I would like to hear more about marking events as finished. The way I currently implemented it the session data indeed is updated on each client request, though there will be no polling every X seconds, I don't think there's any need for that, since the Flash client can easily track timers separately (only for the purposes of displaying it in client ofc) so I'm planning on doing updates only on user actions (like starting a timer.)
Pies
@DaMacc I'm not sure how encrypting the connection could prevent cheating, since the client could easily modify the data it sends before it's encrypted. The security in case of this game is achieved by doing everything on the server. The client serves as the user interface only, to display information, accepts input and visualize how the game works, i.e. the client tells the server when production starts, but the server tells the client when it ends.
Pies
Thank you for the update, that's roughly what I was thinking too. I'm saving the data with last_update timestamp and use the amount of time that passed since to update the timers and queue. Updating the queue was kinda difficult, I've settled on iterating second by second until either the time is up or the queue and timer are both empty. Any comments on that? I've tried to do it analytically with a few algorithms (like converting queue and timer into a single number of seconds), but that didn't work out so well.
Pies
Updating the queue client side or server side was difficult? If items in your queue are given an absolute starting timestamp, life becomes much easier, because you merely need to track duration at that point. Items are obviously completed when the duration start + duration are greater than the current timestamp. You really shouldn't have to iterate anything server side. If talking client side, you will need to iterate every second. I would suggest sending the local time to the server and allowing the server to respond with a properly offset evenst start time and duration.
Kenaniah
Oh, and please don't forget to accept an answer before the time expires :-)
Kenaniah
I'm just working on server-side, client side I'm just designing an API for. You mean I should keep a database table with every queue item in it? That seems kind of wasteful, it's number of users times number of items produced times number of stages of production. With 5k users, 900 items per user (30 per day times 30 days), 10 stages of production, that's a 45M records in a table. I don't think I should need a 45M records table to do this either. And don't worry about the bounty, looks like you're getting it.
Pies
Your database doesn't necessarily need to have every queue item in it (if the items are of a certain type, quantity columns usually suffice). Performance depends on how your queue is managed and what DB you choose to go with. I do recommend using a database one way or another for the simple argument of scalability -- file-based sessions do not load balance.
Kenaniah
A: 

If I have understood your question correctly -- the game rules are implemented by the server; the timers are started at the client; it is the "elapsed time to the user" that is important, not "elapsed time at the server" (ie the user musn't be punished for a laggy connection); but it mustn't let a fake client pretend there is high latency.

In which case, here's my very woolily worded advice:

At the server, only store information the client has sent you and information you have sent the client. (ie, I wouldn't plan on storing a server-calculated "time-left" because it is almost guaranteed to be wrong. In stead, just store "client start time" and "permitted duration" for that "start action" event.)

Look into a solution similar to timeseal http://www.freechess.nl/timeseal.htm to ensure cheating clients would be harder to write. For example, encrypt the timestamps (or the entire message) using a private key in the client and on the server check the received timestamp was encrypted by a valid key.

Depending on how much accidental lag is tolerable, you might want to send the client "potential triggers" rather than "immediate triggers". Ie, rather than sending the client "now customer B leaves without paying (because the player attended to customer A first)", send "if the player attends to customer A first, customer B leaves without paying".

Checking on the server that those rules have occurred correctly at the client is a simple matter of validating the order of events received against the options in the database. So, for the above example, if you see the event "attend to customer A", you expect the next event to be "customer B leaves without paying".

Your database table for the player's game ends up being a glorified event log -- except that it also stores "what can happen next". You take rules from the game rule engine and store them as "next available action state" events.

If you do take the option of sending the client "potential triggers", I'd advice encrypting them too. Otherwise someone will be tempted to write a sniffer so they can write a very simple bot to work out the best available option from the events on offer.

William Billingsley
I think this is very bad advice. If you run anything on the client, you have to consider it compromised. If you use a key to sign or encrypt something on the client, it's easy to disassemble the client, extract the key and use it to cheat. Besides, my game is not multi-player in the sense that there is no interaction between players, and player performance is not time-limited.
Pies
It's been successfully used by thousands of chess-players online for a decade or more. And that's a game where the players are likely to be technical enough to disassemble if they were going to. While it is possible to cheat, it puts it over the threshold of effort where people decide it just isn't worth it. (Especially given that for most games there are more effective ways of cheating than just faking the timer -- which would also become pretty obvious to the other players or to the server pretty quickly.)
William Billingsley
A: 

having read through your question and the given answers I suggest you rethink your application architecture. In my opinion you should implement the whole game logic in the client-side application (flash). Your server side application should only handle user logins and the management of the game states. E.g. a player logs in in your flash application -> check on server side if user exists, then return the actual gamestate for this user. Why this proposition: 1. let the users machine do the heavy work not the server machine 2. limit the traffic to and from the server

to handle the security issues I'd suggest you implement a simple "challenge-response authentication" in your data traffic challenge response on wiki

ruedi
If you implement ANYTHING on server side, you have to assume it's compromised. There is literally no reliable method to ensure the client was not modified or, for that matter, that it's actually your client software that the server is talking to. For example, imagine if your game is designed to give 2 points for 1 correct answer. That '2' must be kept somewhere in memory. It's easy to selectively modify the '2' in memory into '4'. How would you check for that, assuming that any check on client can be hacked around as well?
Pies
Sorry, it's "If you implement ANYTHING on client side, ..."
Pies
okay, I can see you have big issues with security let me tell you the follwing:a priori consider all the data in the client's flash application as secure.the problem you have with security is when it actually leaves the flash app and is sent over the net.I think you'll be using flash's xml functionality to exchange data.Let's say you send along an md5 hash of your xml datas string representation plus a secret word or something. When you receive the data make the same on the server side if the hashes match accept the request if they don't reject it.
ruedi
let me say something about being sure if it's your client application or someone elses: by using an iframe to embed your application on any site, which points to the swf file on your server, you can limit the access to your data in the cross-domain policy file to your domain only. this makes it already very complicated to mock up a request of your application.
ruedi
How do I verify that a request to my server is coming from a Flash application?
Pies
With the method above you should already be able to assure the request is coming from your server (use the curl library in php). with the method even further up you can assure it's coming from your application. there are plenty of examples out there in the web on how to implement these mechanisms.
ruedi
Even if you can 100% guarantee the data stream is secure and the client is unhackable, or if you put all your code on the server, you still can't be sure it's a human playing the game. In the UK I heard tale of a site that did mini-games for money. Regularly, the top two scorers were a couple of students who wrote AIs that took snapshots of the screen (outside the app), analysed the situation, and pushed the buttons -- much faster than a human could react. That's why I and others are advising "good enough" security -- it's almost impossible to make cheating completely impossible.
William Billingsley