PhasedEvolution

HTML strategy game. Logic

15 posts in this topic

Hello. I am playing a bit with mysql and js, html and I am making a small browser strategy multiplayer game like travian for example. I have some doubts though.

In this game players should be able to attack from X to X time. When they attack I change a state in the db for that player so it cannot attack until that state returns to the normal state. However that database update is not fast enough for if the player clicks too fast he attacks more than once before the query is over. How can I forbide players' actions then?

 

Share this post


Link to post
Share on other sites

I have no clue about Mysql and databases in general but the solution seems simple from a programmer's point of view - just keep a small local database (offline) and upload/download it from the server at convenient times. 

Share this post


Link to post
Share on other sites
26 minutes ago, scheffgames said:

I have no clue about Mysql and databases in general but the solution seems simple from a programmer's point of view - just keep a small local database (offline) and upload/download it from the server at convenient times. 

I am not sure if I understood. Are you saying to store the values and player actions in the server and then every 3 minutes for example upload everything to db at once?

Share this post


Link to post
Share on other sites
1 hour ago, PhasedEvolution said:

However that database update is not fast enough for if the player clicks too fast he attacks more than once before the query is over. How can I forbide players' actions then?

You need to COMMIT the update, before doing a SELECT.
But you could also just refuse the attack for some period after the last one. Maybe that would be enough for the database to 'commit' by itself, but you can never be sure...
 

Share this post


Link to post
Share on other sites

If I understand your problem right, it sounds like either your transactions are not atomic, or that you lack a conditional statement before doing the attack command. Before updating the field, check if the CanAttack (or whatever you have called it) property of that player's entity is true, if so, then accept the attack command and set CanAttack to false. When the cooldown/delay timer between attacks is over, set CanAttack back to true, ready for the next attack command.

I think the CASE statement is what you need.

Share this post


Link to post
Share on other sites

@Milton @Arcanorum Well, at the moment the logic I am using is like this:

1-Player action sent to server -> 2-server checks in db if player can act -> if player is able to act db is updated to forbide player from acting*** ( a timer also starts that will enable player acting again) ; if player is not able to act then nothing happens.

What happens, I am assuming is that in ***, if the player clicks multiple times the db update will not still be over and the conditional will still act as if player can vote. What I thought of was to have a player_queue array that would co-exist with the db check. But since checking for an element in array is much faster than a query it could work, no? Would be like an extra security check. 

Share this post


Link to post
Share on other sites
17 minutes ago, PhasedEvolution said:

'the db update will not still be over'

Make sure the conditional (I guess that's a SELECT) is only allowed to read the new state. So the DB update must be finished, before the SELECT returns. I haven't used MySQL for a while, but google something like 'prevent dirty read'. You need to set the ISOLATION level. So use TRANSACTIONS, and COMMIT.

Share this post


Link to post
Share on other sites

In addition to what Milton mentioned, you would benefit from adding the conditional statement to the database script itself, as that will make sure the new, correct value is evaluated, and not the old, 'dirty' one that the gameplay server is evaluating instead.

If you are doing the check on the gameplay server, then what it thinks is the value that is in the database is unreliable if you aren't relying on them being perfectly in synch.

Share this post


Link to post
Share on other sites
12 hours ago, PhasedEvolution said:

I am not sure if I understood. Are you saying to store the values and player actions in the server and then every 3 minutes for example upload everything to db at once?

I may not be very qualified to answer since I don't know Mysql but here's what I do if I had to communicate with an object to retrieve some values knowing that the response might not be instant.

I'd make small quick and fast cache data object - that lives in the code - containing some essential mirrored database entries and which I can access instantly. When the game loads I would update all these cache objects (since now it doesn't matter a couple seconds of delay) and while the game runs I would use them for querying instead of the database. Every now and then (when it's convenient) I would write the cache object entries to the  database.

The_dude8080 likes this

Share this post


Link to post
Share on other sites

Theoretically speaking

1. Don't let browser to register more than one click (like ecommerce industry does for payment button) This won't stop determined ones but at least you'll know their bad intention ( Gaming 101 - "If there's an exploit, it will be exploited" )

2. If possible consider using something like memcached/Redis to store a simple IsAttacked value and/or timestamp. Actually it would also be helpful for scheduling.

3. Not sure if mySQL supports such thing, if it does either lock that record or check before updating.

4. Implement a simple query to detect discrepenancies just in case. A player can get 100 XP a day but one of them got 200? Probably cheating somehow :)

Share this post


Link to post
Share on other sites
On 19/02/2017 at 5:28 PM, Milton said:

Make sure the conditional (I guess that's a SELECT) is only allowed to read the new state. So the DB update must be finished, before the SELECT returns. I haven't used MySQL for a while, but google something like 'prevent dirty read'. You need to set the ISOLATION level. So use TRANSACTIONS, and COMMIT.

Hey Milton. I tried to make it work but I am not getting the syntax right I am afraid. I made a question in SO but it didn't help me that much. I have read about isolation levels (READ COMMITED, READ UNCOMMITED...). I am using node.js and atm my query is similar to this:

// the query that updates the state
var query = "UPDATE users SET state = "+state+" WHERE id = "+user_id+" ";

// the query that checks for the state 
var query = "SELECT state FROM users WHERE id = "+user_id+" ";

I am not sure how to adapt my code to what I pretend. Should I do something like this in the SELECT query ?

// the SELECT query "transformed"
var query = "SET TRANSACTION ISOLATION LEVEL READ COMMITED SELECT state FROM users WHERE id = "+user_id+"";

 

Share this post


Link to post
Share on other sites

Why not just use the connection handler, like in this example?
If you're using InnoDB it seems autocommit is default (but maybe Node sets it to 0).

Setting the isolation level is a separate command, but I think the default InnoDB setting is ok, so you shouldn't need it...

SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
/* InnoDB */
START TRANSACTION;

INSERT INTO table_name (table_field) VALUES ('foo');
INSERT INTO table_name (table_field) VALUES ('bar');

COMMIT; /* SET AUTOCOMMIT = 1 might not set AUTOCOMMIT to its previous state */

/* MyISAM */
SET AUTOCOMMIT = 0;

INSERT INTO table_name (table_field) VALUES ('foo');
INSERT INTO table_name (table_field) VALUES ('bar');

SET AUTOCOMMIT = 1 /* COMMIT statement instead would not restore AUTOCOMMIT to 1 */;

 

Share this post


Link to post
Share on other sites

F*cking lol, just change the state first before you send attacks. On the bird's eyeview of three fast clicks, whichever of the 3 clicks arrives first will prevent the other 2 clicks from executing attacks.

Share this post


Link to post
Share on other sites
2 hours ago, ServerCharlie said:

F*cking lol, just change the state first before you send attacks. On the bird's eyeview of three fast clicks, whichever of the 3 clicks arrives first will prevent the other 2 clicks from executing attacks.

If state change is also a DB update, what's difference beside allowing n clicks instead of Xn? The problem is already about propagation time.

Assume a similar scenario, there is a marketplace people buying stuff. Three different players want to buy same item at the same time, what state there is to change even if you can?

Still best bet seems to be using an in-memory solution, maybe queueing all queries might help as well but there'd be performance hit.

 

 

ClusterAtlas likes this

Share this post


Link to post
Share on other sites
6 hours ago, ServerCharlie said:

F*cking lol, just change the state first before you send attacks. On the bird's eyeview of three fast clicks, whichever of the 3 clicks arrives first will prevent the other 2 clicks from executing attacks.

lol... I think mUnduli is right...

ClusterAtlas likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.