// This method will automatically be called repeatedly until no conflict remains. private static void ConflictResolvingLoop(iOSGKSavedGame[] conflictingGames, bool prefetchData, iOSConflictCallback conflictCallback, Action <iOSGKSavedGame, string> completedCallback) { if (conflictingGames.Length == 1) { // No more conflict! var openedSavedGame = conflictingGames[0]; completedCallback(openedSavedGame, null); return; } // The 1st element in the array is always the base game. // Either it is the game with oldest timestamp to start with, // or the resolved game of the previous conflict resolution request. var baseGame = conflictingGames[0]; var remoteGame = conflictingGames[1]; var resolver = new NativeConflictResolver( baseGame, remoteGame, completedCallback, updatedConflictingGames => ConflictResolvingLoop(updatedConflictingGames, prefetchData, conflictCallback, completedCallback) ); // Invoke the conflict callback immediately if we don't need to prefetch data if (!prefetchData) { conflictCallback(resolver, baseGame, null, remoteGame, null); return; } // If we have to prefetch the data, we delegate invoking the conflict resolution // callback to the joiner instance (once both callbacks resolve, the joiner will // invoke the lambda that we declare here, using the fetched data). Prefetcher joiner = new Prefetcher((baseData, remoteData) => conflictCallback(resolver, baseGame, baseData, remoteGame, remoteData), completedCallback); // Kick off the read calls. InternalLoadSavedGameData(baseGame, joiner.OnBaseDataRead); InternalLoadSavedGameData(remoteGame, joiner.OnRemoteDataRead); }
private void InternalManualOpen(string filename, DataSource source, bool prefetchDataOnConflict, ConflictCallback conflictCallback, Action <SavedGameRequestStatus, ISavedGameMetadata> completedCallback) { mSnapshotManager.Open(filename, AsDataSource(source), Types.SnapshotConflictPolicy.MANUAL, delegate(GooglePlayGames.Native.PInvoke.SnapshotManager.OpenResponse response) { if (!response.RequestSucceeded()) { completedCallback(AsRequestStatus(response.ResponseStatus()), null); } else if (response.ResponseStatus() == CommonErrorStatus.SnapshotOpenStatus.VALID) { completedCallback(SavedGameRequestStatus.Success, response.Data()); } else if (response.ResponseStatus() == CommonErrorStatus.SnapshotOpenStatus.VALID_WITH_CONFLICT) { NativeSnapshotMetadata original = response.ConflictOriginal(); NativeSnapshotMetadata unmerged = response.ConflictUnmerged(); NativeConflictResolver resolver = new NativeConflictResolver(mSnapshotManager, response.ConflictId(), original, unmerged, completedCallback, delegate { InternalManualOpen(filename, source, prefetchDataOnConflict, conflictCallback, completedCallback); }); if (!prefetchDataOnConflict) { conflictCallback(resolver, original, null, unmerged, null); } else { Prefetcher @object = new Prefetcher(delegate(byte[] originalData, byte[] unmergedData) { conflictCallback(resolver, original, originalData, unmerged, unmergedData); }, completedCallback); mSnapshotManager.Read(original, @object.OnOriginalDataRead); mSnapshotManager.Read(unmerged, @object.OnUnmergedDataRead); } } else { Logger.e("Unhandled response status"); completedCallback(SavedGameRequestStatus.InternalError, null); } }); }
private void InternalOpen(string filename, DataSource source, ConflictResolutionStrategy resolutionStrategy, bool prefetchDataOnConflict, ConflictCallback conflictCallback, Action <SavedGameRequestStatus, ISavedGameMetadata> completedCallback) { Types.SnapshotConflictPolicy policy; switch (resolutionStrategy) { case ConflictResolutionStrategy.UseLastKnownGood: policy = Types.SnapshotConflictPolicy.LAST_KNOWN_GOOD; break; case ConflictResolutionStrategy.UseMostRecentlySaved: policy = Types.SnapshotConflictPolicy.MOST_RECENTLY_MODIFIED; break; case ConflictResolutionStrategy.UseLongestPlaytime: policy = Types.SnapshotConflictPolicy.LONGEST_PLAYTIME; break; case ConflictResolutionStrategy.UseManual: policy = Types.SnapshotConflictPolicy.MANUAL; break; default: policy = Types.SnapshotConflictPolicy.MOST_RECENTLY_MODIFIED; break; } mSnapshotManager.Open(filename, AsDataSource(source), policy, response => { if (!response.RequestSucceeded()) { completedCallback(AsRequestStatus(response.ResponseStatus()), null); } else if (response.ResponseStatus() == Status.SnapshotOpenStatus.VALID) { completedCallback(SavedGameRequestStatus.Success, response.Data()); } else if (response.ResponseStatus() == Status.SnapshotOpenStatus.VALID_WITH_CONFLICT) { // If we get here, manual conflict resolution is required. NativeSnapshotMetadata original = response.ConflictOriginal(); NativeSnapshotMetadata unmerged = response.ConflictUnmerged(); // Instantiate the conflict resolver. Note that the retry callback closes over // all the parameters we need to retry the open attempt. Once the conflict is // resolved by invoking the appropriate resolution method on // NativeConflictResolver, the resolver will invoke this callback, which will // result in this method being re-executed. This recursion will continue until // all conflicts are resolved or an error occurs. NativeConflictResolver resolver = new NativeConflictResolver( mSnapshotManager, response.ConflictId(), original, unmerged, completedCallback, () => InternalOpen(filename, source, resolutionStrategy, prefetchDataOnConflict, conflictCallback, completedCallback) ); // If we don't have to pre-fetch the saved games' binary data, we can // immediately invoke the conflict callback. Note that this callback is // constructed to execute on the game thread in // OpenWithManualConflictResolution. if (!prefetchDataOnConflict) { conflictCallback(resolver, original, null, unmerged, null); return; } // If we have to prefetch the data, we delegate invoking the conflict resolution // callback to the joiner instance (once both callbacks resolve, the joiner will // invoke the lambda that we declare here, using the fetched data). Prefetcher joiner = new Prefetcher((originalData, unmergedData) => conflictCallback(resolver, original, originalData, unmerged, unmergedData), completedCallback); // Kick off the read calls. mSnapshotManager.Read(original, joiner.OnOriginalDataRead); mSnapshotManager.Read(unmerged, joiner.OnUnmergedDataRead); } else { Logger.e("Unhandled response status"); completedCallback(SavedGameRequestStatus.InternalError, null); } }); }