Share and Query Player-Generated Content

Datetime:2016-08-23 02:12:11         Topic: Lucene  Coder          Share        Original >>
Here to See The Original Article!!!

I am very excited to tell you about the latest addition to our cloud services for games. We call it Shared Storage.

Shared Storage is a specialised, scalable, cloud-based datastore to share and query content between players in game. It has a flexible query language inspired by Lucene so complex conditions like filters and rules can be expressed easily in queries.

The feature is designed to make it very easy to build all kinds of competitive games, share user-generated maps, create tournaments and guilds, play against ghosts (in racing games) and more.

For example let's imagine we're developing a competitive battle game where players can build bases with armies and compete against each other to win rewards, unlock achievements, and climb the leaderboards. We'll call this game Boom


Island. ;)

I'll use our Unity SDK in code examples to demonstrate how we can implement PvP battles in the game. To keep the example simple we'll use only 4 unit types: "farmer", "soldier", "archer", and "knight". Each player can spend points to build up their army from these units.

Whenever a player updates their army configuration we'll write it into Shared Storage:

IDictionary<string, object> army = new Dictionary<string, object>();
army.Add("name", "RiotLemon123");
army.Add("level", 10);
army.Add("units", new string[] {"farmer", "farmer", "archer"});
sessionClient.SharedStoragePut("Army", army, () => {
  // Success
}, (status, reason) => {
  // Handle request or network errors.

Let's assume we have 5 users playing the game which means we have 5 armies in Shared Storage. They all have slightly different army configuration but with the code above changed to build up the information from game state we might have:

{"name": "RiotLemon1234", "level": 6, "units": ["farmer", "farmer", "archer"]}
{"name": "MusicWeek6310", "level": 9, "units": ["soldier", "farmer", "archer"]}
{"name": "RightTheory0875", "level": 3, "units": ["farmer", "farmer"]}
{"name": "LuckyJazz9463", "level": 9, "units": ["soldier", "knight", "archer"]}
{"name": "AutomaticRobot7609", "level": 1, "units": ["farmer"]}

Now somewhere within the game's UI we have an option to find and challenge other players to battle. The simple way we could implement this is with a Shared Storage query:

string query = "*";
sessionClient.SharedStorageSearchGet(query, "Army", (SharedStorageSearchResults results) => {
  foreach (SharedStorageObject result in results) {
    // We'll just print out the JSON.
    Debug.Log("Shared Storage Object: " + result.ConvertPublic());
}, (status, reason) => {
  // Handle request or network errors.

"*" (asterisk) is the most basic query we could perform and simply asks for any objects in the datastore. It's handy to begin with but let's play a little with the query.

string query = "value.level > 5";
// Results would be RiotLemon1234, MusicalWeekend6310, and LuckyJazz9463.

Here we decide we only want armies which are level 5 or greater to be available to battle. This is more interesting but we really don't want our own army to appear in the battle results so let's filter it out:

string query = "value.level > 5 AND NOT `RiotLemon1234`";
// Results would be MusicalWeekend6310 and LuckyJazz9463.

What if we decide players must be within a specific range of levels relative to our current player for them to appear in battle results:

int currentLevel = 6;
int maxLevel = currentLevel * 1.5; // = 9
int minLevel  = Math.Floor(currentLevel * 0.75); // = 4
string query = "value.level: [" + minLevel + " TO "  + maxLevel + "]"
    + " AND NOT `RiotLemon1234`";
// i.e. "value.level: [4 TO 9] AND NOT `RiotLemon1234`";
// Results would be MusicalWeekend6310 and LuckyJazz9463.

Next we decide we want to offer an option where players can battle other players who have at least got a "farmer" in their army configuration:

// First part of the query would be built up as above.
string query = "value.level: [4 TO 9] AND value.units: (`farmer`) AND NOT `RiotLemon1234`"
// Results would be MusicalWeekend6310.

Finally we decide it's pretty important that players find other active players to battle before finding opponents who've not played in a while. We'll add a new "lastActive" field to the Dictionary we store when we update a player's army configuration:

// <snip> - See code at top of post.
DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long currentTimeMillis = ((DateTime.UtcNow - Jan1st1970).TotalMilliseconds);
army.Add("lastActive", currentTimeMillis);
// Etc.

A query to "prefer" players who've played within the last 30 days would then look like:

DateTime Jan1st1970 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long currentTimeMillis = ((DateTime.UtcNow - Jan1st1970).TotalMilliseconds);
long days30Ago = currentTimeMillis - 1000 * 60 * 60 * 24 * 30;
string query = " * OR (value.lastActive > "
    + Convert.ToString(days30Ago) ")^2";
// i.e. " * OR (value.lastActive > 1448106939903)^2"

The "^2" is known as "boosting" in Lucene terminology; it's a way for us to specify the "relevancy" of the results in the query. In this case we say that the players who have been active within the last 30 days are more relevant than those who have not and we'll get back their results before we get inactive players.

I've barely begun to scratch the surface of how many cool queries and gameplay mechanics you can build into your games with our Shared Storage.

In future blog posts I'll give more specific examples on how you can build Guilds, Tournaments, Ghosting, and other competitive PvP features into your games. In the meantime have a look at our concept documentation and theSDK guides.

Shared Storage brings unprecedented flexibility to game developers to build massive, competitive, gameplay experiences; with PvP, and multiplayer games. It's already in use within some games built by our developer community and I can't wait to see how it's used in many future game titles.


Put your ads here, just $200 per month.