Android Google Saved Games 意外冲突

Posted

技术标签:

【中文标题】Android Google Saved Games 意外冲突【英文标题】:Android Google Saved Games unexpected conflicts 【发布时间】:2016-11-16 14:47:09 【问题描述】:

在我的游戏中,我有游戏内货币,我想将其价值保存到云端。我决定使用 Google Saved Games API。一切都很好,但是当我将数据保存到快照中,然后在游戏再次启动时读取它时,即使我在同一台设备上,我也会遇到冲突。现在我在每次更改后保存货币的状态,所以当玩家花费或获得一些“硬币”时。我在想这可能经常发生并且服务无法处理它,因为当我离线时(没有连接到网络)一切都很好而且很快,但是当我在线时(连接到 Wi-fi)可以使用快照速度较慢,正如我所说,我与上次保存的数据和以前保存的数据发生冲突(我正在记录所有值......)。有时我什至会遇到 5 个冲突。我有 3 个函数可用于保存的游戏。一种用于读取数据,一种用于保存数据,一种用于检查冲突:

读取数据:

private void readSavedGame(final String snapshotName) 
    AsyncTask<Void, Void, Boolean> readingTask = new AsyncTask<Void, Void, Boolean>() 
        @Override
        protected Boolean doInBackground(Void... params) 
            Snapshots.OpenSnapshotResult result = Games.Snapshots.open(mGoogleApiClient, snapshotName, false).await();

            Snapshot snapshot = processSnapshotOpenResult(result, 0);

            if(snapshot != null) 
                try 
                    updateGameData(snapshot.getSnapshotContents().readFully());
                    Log.d(TAG, "Updating game: "+String.valueOf(coins)+"...");
                    return true;
                 catch (IOException e) 
                    Log.d(TAG, "Error: " + e.getMessage());
                
            

            return false;
        

        @Override
        protected void onPostExecute(Boolean result) 
            super.onPostExecute(result);

            if(result) Log.d(TAG, "Game state read successfully...");
            else Log.d(TAG, "Error while reading game state...");

            updateUi();
        
    ;

    readingTask.execute();

保存数据:

private void writeSavedGame(final String snapshotName, final byte[] data) 
    AsyncTask<Void, Void, Boolean> updateTask = new AsyncTask<Void, Void, Boolean>() 
        @Override
        protected Boolean doInBackground(Void... params) 
            Snapshots.OpenSnapshotResult result = Games.Snapshots.open(
                    mGoogleApiClient, snapshotName, false).await();

            Snapshot snapshot = processSnapshotOpenResult(result, 0);

            if(snapshot != null) 
                snapshot.getSnapshotContents().writeBytes(getGameData());
                Log.d(TAG, "Saving: "+String.valueOf(coins)+"...");

                Snapshots.CommitSnapshotResult commitSnapshotResult = Games.Snapshots.commitAndClose(mGoogleApiClient, snapshot, SnapshotMetadataChange.EMPTY_CHANGE).await();

                if(commitSnapshotResult.getStatus().isSuccess()) return true;
            

            return false;
        

        @Override
        protected void onPostExecute(Boolean result) 
            if (result) Log.d(TAG, "Game was saved successfully....");
            else Log.d(TAG, "Error while saving game state...");
        
    ;

    updateTask.execute();

检查冲突或处理 OpenSnapshotResult

Snapshot processSnapshotOpenResult(Snapshots.OpenSnapshotResult result, int retryCount) 
    Snapshot mResolvedSnapshot = null;
    retryCount++;

    int status = result.getStatus().getStatusCode();
    Log.i(TAG, "Save Result status: " + status);

    if (status == GamesStatusCodes.STATUS_OK) 
        Log.d(TAG, "No conflict, SNAPSHOT is OK");
        return result.getSnapshot();
     else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONTENTS_UNAVAILABLE) 
        return result.getSnapshot();
    
    else if (status == GamesStatusCodes.STATUS_SNAPSHOT_CONFLICT) 
        Log.d(TAG, "Conflict: "+String.valueOf(retryCount));

        Snapshot snapshot = result.getSnapshot();
        Snapshot conflictSnapshot = result.getConflictingSnapshot();

        // Resolve between conflicts by selecting the newest of the conflicting snapshots.
        mResolvedSnapshot = snapshot;

        if (snapshot.getMetadata().getLastModifiedTimestamp() <
                conflictSnapshot.getMetadata().getLastModifiedTimestamp()) 
            mResolvedSnapshot = conflictSnapshot;
        

        try 
            Log.d(TAG, "Snapshot data: "+new String(snapshot.getSnapshotContents().readFully()));
            Log.d(TAG, "Conflicting data: "+new String(conflictSnapshot.getSnapshotContents().readFully()));
         catch (IOException e) 
            Log.e(TAG, "ERROR WHILE READING SPAPSHOTS CONTENTS...");
        


        Snapshots.OpenSnapshotResult resolveResult = Games.Snapshots.resolveConflict(
                mGoogleApiClient, result.getConflictId(), mResolvedSnapshot).await();

        if (retryCount < MAX_SNAPSHOT_RESOLVE_RETRIES) 
            // Recursively attempt again
            return processSnapshotOpenResult(resolveResult, retryCount);
         else 
            // Failed, log error and show Toast to the user
            String message = "Could not resolve snapshot conflicts";
            Log.e(TAG, message);
            //Toast.makeText(getBaseContext(), message, Toast.LENGTH_LONG).show();
        

    

    // Fail, return null.
    return null;

很好地解释了冲突原则here。 我的代码基于official docs implementations 和samples。

所以离线时,一切正常,但连接时,我在同一台设备上遇到冲突……也许我经常更新我保存的游戏,而服务无法处理。有任何想法吗?谢谢。

【问题讨论】:

你解决了吗?我面临同样的问题。相同的代码在 iCloud 上运行良好,但在 Google 上会产生随机冲突。它与保存频率有关吗?文档的“合理频率”毫无意义。 我也遇到了同样的问题,你的最终解决方案是什么? 【参考方案1】:

如Saved Games - Conflict resolution中所述

通常,当您的应用程序实例在尝试加载或保存数据时无法访问 Saved Games 服务时,就会发生数据冲突。一般来说,避免数据冲突的最佳方法是在应用程序启动或恢复时始终从服务中加载最新数据,并以合理的频率将数据保存到服务中。

除此之外,还建议关注Best practices for implementing saved games,为您的玩家提供最好的产品。

另外,要了解如何为您的平台实施存档游戏,请参阅Client implementations 中提供的资源。

【讨论】:

以上是关于Android Google Saved Games 意外冲突的主要内容,如果未能解决你的问题,请参考以下文章

基于cocos2dx,在android的游戏中加入google play game排行榜。

Google Game Center 中的空指针异常

如何使用来自 Google AutoML 视觉分类的 TensorFlow Frozen GraphDef (single saved_model.pb) 进行推理和迁移学习

Unity Google Game Play Services,不向排行榜提交分数

Google Play Game Services 和 LibGDX,如何在不显示标题栏的情况下集成?

Android Google Play 排行榜