存储重播数据的最佳方式

时间:2015-09-04 17:26:07

标签: azure database-design azure-cosmosdb

我目前有一个我正在分析的游戏重播文件,并且想知道存储数据的最佳方法,因此我可以快速创建复杂的查询。例如,分析仪每50毫秒返回一个数据结构,您可以在其中访问当前回合详细信息的快照和游戏中当前玩家的状态,例如他持有什么武器,他有多少健康,他目前正在拍摄哪些玩家我希望能够说:从重播文件的开始到10000毫秒,玩家的“Micheal”位置是什么。玩家“Kyle”在10000ms到20000ms之间对其他玩家造成了多大的伤害。我希望能够存储我正在分析的所有数据并使用API​​在前端重放它,以便您可以直观地重放它。

我可以将有关重放的元数据存储到数据库中,例如:(第1轮,开始时间:10000,结束时间:30000),(第2轮,开始时间:31000,结束时间:37000)。我还可以存储关于玩家被杀的时候的元数据(Kyle,DeathTime:31000,KilledBy:Micheal)或玩家受伤(Kyle,HurtBy:Micheal,伤害:10,武器:x)。

要完成我想要为不同情况创建复杂查询的目标,我是否需要两者的组合?例如将毫秒级毫秒数据存储在NoSql数据库/文档中,然后解析整个文件并将第二段中提到的元数据存储到另一个数据库中。仅存储毫秒级的毫秒数据是不可行的,然后能够创建快速查询来解析我想要的数据吗?

1 个答案:

答案 0 :(得分:0)

听起来像是一款很酷的游戏。 Microsoft Azure Table Storage(NoSQL数据库)速度非常快,非常适合您的游戏。您可以使用Slazure(我编码的Slazure BTW),它包含查询语言(自定义LINQ提供程序),并且还将数据存储在Azure表存储中。如果我在你的游戏中使用Slazure和Microsoft Azure Table Storage NoSQL数据库,我会怎么做。我建议您在 PlayersTable 表中存储一行,其中PartitionKey是玩家名称的每个事件,RowKey是游戏回合中的几毫秒 - 因为每个表都被编入索引 PartitionKey 列并按 RowKey 列排序所有使用这些列的查询都非常快。还有为所使用的武器创建的列,屏幕上的位置,健康状况,整数以及哪个玩家被谁杀死。您可以使用与下面相同的建议方法创建一个表来保存损坏数据:

using SysSurge.Slazure;
using SysSurge.Slazure.Linq;
using SysSurge.Slazure.Linq.QueryParser;

namespace TableOperations
{
    public class PlayerInfo
    {
        // List of weapons
        public enum WeaponList {
            Axe, Arrow, Knife, Sling
        };

        // Update a player with some new data
        public void UpdatePlayerData(dynamic playersTable, DateTime gameStartedTime, int round, string playerName, WeaponList weapon, int healthPoints, int xPos, int yPos)
        {
            // Create an entity in the Players table using the player name as the PartitionKey, the entity is created if it doesn't already exist
            var player = playersTable.Entity(playerName);

            // Store the time the event was recorded for later as milliseconds since the game started. 
            // This means there is one row for each stored player event
            player.Rowkey = ((DateTime.UtcNow.Ticks - gameStartedTime.Ticks)/10000).ToString("d19");

            player.Round = round; // Round number
            player.Weapon = (int)weapon; // Weapon carried by player. Example Axe

            // Store player X and Y position coordinates on the screen
            player.X = xPos;
            player.Y = yPos;

            // Number of health points, zero means player is dead
            player.HealthPoints = healthPoints; 

            // Save the entity to the Azure Table Service storage
            player.Save()
       }

        // Update a player with some new data
        public void PlayerKilled(dynamic playersTable, DateTime gameStartedTime, int round, string playerName, string killedByPlayerName)
        {
            // Create an entity in the Players table using the player name as the PartitionKey, the entity is created if it doesn't already exist
            var player = playersTable.Entity(playerName);

            // Store the time the event was recorded for later as milliseconds since the game started. 
            // This means there is one row for each stored player event
            player.Rowkey = ((DateTime.UtcNow.Ticks - gameStartedTime.Ticks)/10000).ToString("d19");

            player.Round = round; // Round number

            // Number of health points, zero means player is dead
            player.HealthPoints = 0;    

            player.KilledByPlayerName = killedByPlayerName; // Killed by this player, example "Kyle"

            // Save the entity to the Azure Table Service storage
            player.Save()
       }

        // Get all the player positions between two time intervals
        public System.Linq.IQueriable GetPlayerPositions(dynamic playersTable, string playerName, int fromMilliseconds, int toMilliseconds, int round)
        {
            return playersTable.Where("PrimaryKey == @0 && RowKey >= @1 && RowKey <= @2 && Round == @3", 
                playerName, fromMilliseconds.ToString("d19"), toMilliseconds.ToString("d19"), round).Select("new(X, Y)");
        }      

    }
}

首先,您需要记录游戏开始时的时间和圆数:

var gameStartedTime = DateTime.UtcNow;
var round = 1;  // Round #1

,并在NoQL数据库中创建一个表:

// Get a reference to the Table Service storage
dynamic storage = new DynStorage("UseDevelopmentStorage=true");

// Get reference to the Players table, it's created if it doesn't already exist
dynamic playersTable = storage.Players;

现在,在游戏中你可以不断更新玩家信息:

UpdatePlayerData(playersTable, gameStartedTime, round, "Micheal", WeaponList.Axe, 45, 12313, 2332);
UpdatePlayerData(playersTable, gameStartedTime, round, "Kyle", WeaponList.Knife, 100, 13343, 2323);

如果您需要在每个存储事件之间等待50毫秒,您可以执行以下操作:

System.Threading.Thread.Sleep(50);

,然后存储更多玩家事件数据:

UpdatePlayerData(playersTable, gameStartedTime, round, "Micheal", WeaponList.Axe, 12, 14555, 1990);
UpdatePlayerData(playersTable, gameStartedTime, round, "Kyle", WeaponList.Sling, 89, 13998, 2001);

当其中一名球员死亡时,您可以使用相同的方法调用零健康点和杀死他/她的玩家的名字:

PlayerKilled(playersTable, gameStartedTime, round, "Micheal", "Kyle");

现在,稍后在您的游戏分析器中,您可以查询从游戏开始(0毫秒)到游戏开始10,000毫秒的所有位置,如下所示:

// Get a reference to the table storage and the table
dynamic queryableStorage = new QueryableStorage<DynEntity>("UseDevelopmentStorage=true");
QueryableTable<DynEntity> queryablePlayersTable = queryableStorage.PlayersTable;

var playerPositionsQuery = GetPlayerPositions(queryablePlayersTable, "Micheal", 0, 10000, round);

// Cast the query result to a dynamic so that we can get access its dynamic properties
foreach (dynamic player in playerPositionsQuery)
{
    // Show player positions in the console
    Console.WriteLine("Player position: Name=" + player.PrimaryKey + ", Game time MS " + player.RowKey + ", X-position=" + player.X  + ", Y-position=" + player.Y;
}