This question already has an answer here:
Randomly pairing users in Firebase [closed]
(1 answer)
Closed 2 years ago.
I'm trying to create a game where users are matched with each other randomly using Firebase.
My idea to match them is like this:
User A comes, reads list of games. If there aren't any games waiting start game. Sets himself as initiator, opponent as null.
User B comes, reads list of games. Finds User A's game (opponent is null) and joins it (he becomes the opponent)
What happens if User C comes between the moment where User B read the games list and the moment User B wrote himself as the opponent (for him opponent would still be null). Basically the concurrency problem.
I've read about transactions but I'm not 100% sure they help in this case (Read games with no Opponent, Write opponent to one of the games) because I've seen them used a lot in increasing values rather than reading/writing data.
Transactions blocks are used for concurrency. What they do is prevent issues when two users attempt to write to the same area at the same time. Like you said this is commonly seen with likes. So if the game currently only has 1 player and you save a value of 0 so other players know the game is "open". When a player attempts to connect to that game they write to that area and change the 0 to a 1. If another player attempted to connect at the same time their transaction block will now fire. When they get the data they will see a 1 though not a zero. At this point you back out and look for a new game. Not sure if this is the best method, but it should work.
Related
I am making a leaderboard by using firebase realtime database, unity and facebook ( for profile pic and name )
But I am struggling, I am not sure about the way of doing that. I want to display multiple leaderboards -> All time WORLD / REGION / FRIENDS score, monthly WORLD / REGION / FRIENDS and weekly one.
I know I can use cloud functions to reset weekly and monthly leaderboards.
But how can I store world and region scores ?
Now I just have this ->
https://cdn.discordapp.com/attachments/440873502535450627/821361474353889320/ld.JPG
Thanks to that, I can easily get region leaderboards getref.getchild users . getchild france and then orderbyvalue . limittolast ! this is good !
The problem is how could I get world leaderboard ? ( I have other countries ) I am so lost...Do I need to make another structure for my leaderboard ?
There are a number of considerations that you might put into this including pricing, how much you trust the client, &c. Generally, with a NoSQL database like Realtime Database, you might have a lot of redundant data to make up for the fact that your ability to query is limited. It's also really easy to pull in a lot of data by mistake, so you'll see a number of best practices around keeping your database shallow.
With that said, I think I might recommend reorganizing your data a little bit. For example:
Have one node named "users" with all of your users in there. Each "user" in users should be placed in a node that's simply the userid (which makes security rules easier to write), and here you can place the all time score, monthly score, and weekly score. I'd also recommend storing the time you got that score (using ServerValue.Timestamp) so you don't have to worry about going through your users and deleting all the old scores. If your weekly timestamp isn't this week, you know to ignore/overwrite it (obviously, time is hard, so you'll have to work out what a "week" means to players of your game wrt time zones &c).
I'd also put an array of all a user's "friends" under this user node by their user id. That way, when you go to look up friends, you just just ask for "users/" explicitly there.
Now for regional and world leaderboards on monthly and weekly cadences, I'd just copy everything you want to display into that leaderboard node (say username and score) and add the uid if you need to attribute it back to a user (say if you want to click on that score and see their all time record). So if a user lives in France and they get a weekly high score for them, I'd first write that user's "users/" node with the weekly score and timestamp, then I'd go out to the weekly leaderboard for France and add the new score if it qualifies (ie: don't add it if you're only tracking the top 10), then go out to the world leaderboard and add it there.
How you do this copying is up to you. You could make it the client's responsibility with security rules just making sure they're well behaved - which would probably be fast and cheap but might get weird if they go offline partway through updating. You could use a Firebase trigger that would listen for a user updating their node and copy the data out to the respective leaderboards (this would be more expensive since you're paying for Cloud Functions time and a bit slower but will always work once the user node is updated).
A final note is that for, say, weekly leaderboards. I'd have a node that says "this is France's weekly leaderboard right now" and have all clients read that first before figuring out where to write. That way, at a time you decide for when the week turns over, you can change that node and just have people start writing somewhere else. You can then keep the old leaderboard up for some time (ex: maybe you want to see who changed between this week and last), and you can delete it at your leisure after you're sure that all players have their score in (ex: over slow internets, disconnects, &c).
I'm working on a game that requires player matchmaking i.e. putting 2 players into 1 battle room.
While there are few players online in the game this is not an issue, however if we assume scenario of 1,000,000 players online at a time, who are all trying to matchmake, then picture becomes more complex.
Assuming following flow
My first instinct was to
When player clicks "play" call cloud function that checks if "open" battle room exists.
If there is one open, place player in it and start the battle or set battle to be active.
In case there wasn't an open room, we create new one and wait for another player to join.
However I see a lot of issues here given that we have 1,000,000 players who would be performing this action. For example, while performing step 2 and setting room to be "active" we might be pushing more players into it during the time those requests resolve?
If you want to deal with concurrency, I think you should use transactions to perform this action (adding a player to a room). That way, you ensure to check if the room is still open before adding a player in it. If 2 players start a matchmaking at the same time and find the same room, then only one single player will be added to that room
https://firebase.google.com/docs/firestore/manage-data/transactions
As far as I understand I can suggest One way to make it easier is have state of a user itself instead of calling cloud function to create room
Make user state like active, online, offline. When a user press play set its state active.
Call a query to fetch user where state == active , limit to 5(example).
start a transaction to match it with first user if succeed change status to playing for both else if failed ( you got matched to someone else or player you were trying with got matched to someone) check next player.
If during this you should be always observing your own state if it changes to playing because someone else matched to you then straight head to game.
Lets say I wish to track
User action - game he played - which area he stays - his house number.
If I were to track these event actions in Tabular format, it would look like:
UserId|Game|Area|House|Timestamp so on.
Then I can always run SQL queries if I want to answer few business queries. Like
1. In a given day/week, who is the most active User
2. Which game is most-played?
3. Which area plays most events
4. Which user from which area are the most active
Whats the best way to capture this using Google analytics? Will custom dimensions be useful. Or GA is not suitable for this kind of insight?
Thanks.
First of all, the house number is too precise, it would be against GA's ToS.
In GA everything is captured in "hits", you can think of this as one "row" of data.
Let's look at what you wanted to find out:
Most Active User? - This depends on how you determine "Active". Is it the longest Session durations? Tried most games? Most logins? Most sessions? To track a user, you'd need a User ID tracked.
Which game is played the most? - Again, what is played the most? Longest time in game? Most "start" games? This would require you to know the Game that was played and when someone started playing
Which area is most active? -This would go back to the definition of active, the region information is needed along with the active definition
Which users are most active in an area? Same as above, the user would need to be identified and area
To determine which Custom Dimensions (CDs) you want, let's look at the example data points you want to track and try to determine the scope and if it already exists as a standard dimension:
User ID - this is obviously related to the user, makes sense to be user-scoped
Game - This is a tougher CD. I would think that in a single session, users can play multiple games, thus I'd think you'd want this to be hit-scoped.
Area - GA already provides this based on the ISP
Timestamp - GA already provides time dimensions
From above, we can determine that you need to create two CDs, one to track User ID, the other to track the Game.
You can also look into using the userid feature in GA for cross-device tracking.
I am making an app.. I mean a game type app. it's a quiz app where two people can
pair them up and can answer questions one by one and so on.
what I want is:
I want to make a virtual room,
when USER1 want to play he will click search competitor, a virtual room with random string key will be made with one player.
then when USER2 will click on find competitor button his room will also be created and he will also search all rooms created before him, if any room have less than 2 users then he will join that room and start playing.
I am new so I am not sure if that is good approach but that's all I have planned.
how to count childs of a node (node that we does not know whats its key..
)
see attached image
please find image here
Good start. It's always best to articulate precisely what you want before your start coding. It sounds like you have a pretty good idea of what you want.
I would recommend using Firestore and firebase.auth, even if only anonymous, so that each user can have 'state' and univeral id in their app. In other words when they close and re-open the app, the app keeps a universal id, uid, for them.
If I understand your requirements:
each user can have more than one room
each room has a max of 2 users
Scalability
As far as searching existing rooms, this will not scale without some categorization. What happens if you have 1,000 rooms? How will users search all that? So you would want to add subject matter, or tags, to each room. That way you can query where("tag")==chosenTag.
Basically each virtual room has fields identifying the 2 users (uid's), AND has subcollections - each collection has a document
uid has fields (accountCreateDate, favoriteColor, AND the uid of each room (as a key) they engage in where the value would be an integer where, for example (1== awaiting response, 2==closed, 3==whatever).
Firestore Structure
users(collection > userUid(document containing aboce said)
rooms(collection) roomA(Document w field identifying subject/name) AND sub-collection containing a doc for each pair of participants.
I'm coming from a SCORM end and trying to figure out two related issues with how to do update and find the most recent data (ie, looking for best practices).
In SCORM I'd have a set of activities that would all store their answers and scores (easily understandable from the docs etc). The "how" I'm after is specifically related to resuming the set of activities multiple times, and hitting "reset" and submitting a different answer to a single activity after a statement has been sent in.
From what I read with xAPI it states that statements are immutable - so how would I go about this.
My first thought was that I'd make the statement id generated from the activity id and void the old answer when it changes - but that sounds wrong (not least because it reads like you can't re-use the id even with voiding).
So it looks like the Statement id needs to be unique, which would mean that multiple identical Objects would be found - so would I have to look through every attempt and check for the latest one?
I'm currently looking at using xAPIWrapper in the middle.
Moving from SCORM to xAPI requires a change of mindset. SCORM deals with statuses which get updated; xAPI logs events like a journal.
You can think of it like Facebook. You post a photo of your new cat; a month later you post a photo of your cat 1 month older. There's no need to go back and delete the old post. If you want the latest photo of your cat you just go and get the most recent photo tagged "Ryochet's cat". You can also look at older photos to see how your cat developed. xAPI is like that activity stream on Facebook.
So, if somebody scores 10 points on their first attempt, then 20 points on their second attempt, you'd simply send a second set of statements about the 2nd attempt. There's no need to get rid of the statements about the old attempt, that happened and is useful data to see how the learner developed.