//
// Copyright (C) 2014 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
namespace GooglePlayGames.BasicApi.SavedGame
{
using System;
using System.Collections.Generic;
///
/// An enum for the different strategies that can be used to resolve saved game conflicts (i.e.
/// conflicts produced by two or more separate writes to the same saved game at once).
///
public enum ConflictResolutionStrategy
{
///
/// Choose which saved game should be used on the basis of which one has the longest recorded
/// play time. In other words, in the case of a conflicting write, the saved game with the
/// longest play time will be considered cannonical. If play time has not been provided by the
/// developer, or in the case of two saved games with equal play times,
/// will be used instead.
///
UseLongestPlaytime,
///
/// Choose the version of the saved game that existed before any conflicting write occurred.
/// Consider the following case:
/// - An initial version of a save game ("X") is written from a device ("Dev_A")
/// - The save game X is downloaded by another device ("Dev_B").
/// - Dev_A writes a new version of the save game to the cloud ("Y")
/// - Dev_B does not see the new save game Y, and attempts to write a new save game ("Z").
/// - Since Dev_B is performing a write using out of date information, a conflict is generated.
///
/// In this situation, we can resolve the conflict by declaring either keeping Y as the
/// canonical version of the saved game (i.e. choose "original" aka ),
/// or by overwriting it with conflicting value, Z (i.e. choose "unmerged" aka
/// ).
///
///
UseOriginal,
///
/// See the documentation for
///
UseUnmerged,
///
/// Manual resolution, no automatic resolution is attempted.
///
UseManual,
///
/// The use last known good snapshot to resolve conflicts automatically.
///
UseLastKnownGood,
///
/// The use most recently saved snapshot to resolve conflicts automatically.
///
UseMostRecentlySaved
}
public enum SavedGameRequestStatus
{
Success = 1,
///
/// The request failed due to a timeout.
///
///
TimeoutError = -1,
///
/// An unexpected internal error. Check the log for error messages.
///
///
InternalError = -2,
///
/// A error related to authentication. This is probably due to the user being signed out
/// before the request could be issued.
///
///
AuthenticationError = -3,
///
/// The request failed because it was given bad input (e.g. a filename with 200 characters).
///
///
BadInputError = -4
}
public enum SelectUIStatus
{
///
/// The user selected a saved game.
///
SavedGameSelected = 1,
///
/// The user closed the UI without selecting a saved game.
///
///
UserClosedUI = 2,
///
/// An unexpected internal error. Check the log for error messages.
///
///
InternalError = -1,
///
/// There was a timeout while displaying the UI.
///
///
TimeoutError = -2,
///
/// A error related to authentication. This is probably due to the user being signed out
/// before the request could be issued.
///
///
AuthenticationError = -3,
///
/// The request failed because it was given bad input (e.g. a filename with 200 characters).
///
///
BadInputError = -4,
UiBusy = -5
}
///
///
/// A delegate that is invoked when we encounter a conflict during execution of
/// . The caller must resolve the
/// conflict using the passed . All passed metadata is open.
/// If was invoked with
/// prefetchDataOnConflict set to true, the and
/// will be equal to the binary data of the "original" and
/// "unmerged" saved game respectively (and null otherwise). Since conflict files may be generated
/// by other clients, it is possible that neither of the passed saved games were originally written
/// by the current device. Consequently, any conflict resolution strategy should not rely on local
/// data that is not part of the binary data of the passed saved games - this data will not be
/// present if conflict resolution occurs on a different device. In addition, since a given saved
/// game may have multiple conflicts, this callback must be designed to handle multiple invocations.
///
public delegate void ConflictCallback(IConflictResolver resolver, ISavedGameMetadata original,
byte[] originalData, ISavedGameMetadata unmerged, byte[] unmergedData);
///
/// The main entry point for interacting with saved games. Saved games are persisted in the cloud
/// along with several game-specific properties ( for more
/// information). There are several core concepts involved with saved games:
///
/// Filenames - act as unique identifiers for saved games. Two devices
/// performing a read or write using the same filename will end up reading or modifying the same
/// file (i.e. filenames are not device specific).
///
///
/// Saved Game Metadata are represented by .
/// The instances allow access to metadata properties about the underlying saved game (e.g.
/// description). In addition, metadata functions as a handle that are required to read and
/// manipulate saved game contents. Lastly, metadata may be "Open". Open metadata instances are
/// required to manipulate the underlying binary data of the saved game. See method comments to
/// determine whether a specific method requires or returns an open saved game.
///
///
/// Conflicts occur when multiple devices attempt to write to the same file
/// at the same time. The saved game system guarantees that no conflicting writes will be lost or
/// silently overwritten. Instead, they must be handled the next time the file with a conflict is
/// Opened. Conflicts can be handled automatically (
/// ) or can be manuallyhandled by the developer
/// (). See the Open methods for more discussion.
///
///
/// Saved games will generally be used in the following workflow:
///
/// - Determine which saved game to use (either using a hardcoded filename or
/// ShowSelectSavedGameUI)
/// - Open the file using OpenWithManualConflictResolution or
/// OpenWithAutomaticConflictResolution
/// - Read the binary data of the saved game using ReadBinaryData handle it
/// as appropriate for your game.
/// - When you have updates, persist them in the cloud using CommitUpdate. Note
/// that writing to the cloud is relatively expensive, and shouldn't be done frequently.
///
///
///
/// See online
/// documentation for Saved Games for more information.
///
public interface ISavedGameClient
{
///
/// Opens the file with the indicated name and data source. If the file has an outstanding
/// conflict, it will be resolved using the specified conflict resolution strategy. The
/// metadata returned by this method will be "Open" - it can be used as a parameter for
/// and .
///
/// The name of the file to open. Filenames must consist of
/// only non-URL reserved characters (i.e. a-z, A-Z, 0-9, or the symbols "-", ".", "_", or "~")
/// be between 1 and 100 characters in length (inclusive).
/// The data source to use. for a description
/// of the available options here.
/// The conflict resolution that should be used if any
/// conflicts are encountered while opening the file.
/// for a description of these strategies.
/// The callback that is invoked when this operation finishes. The
/// returned metadata will only be non-null if the open succeeded. This callback will always
/// execute on the game thread and the returned metadata (if any) will be "Open".
void OpenWithAutomaticConflictResolution(string filename, DataSource source,
ConflictResolutionStrategy resolutionStrategy,
Action callback);
///
/// Opens the file with the indicated name and data source. If there is a conflict that
/// requires resolution, it will be resolved manually using the passed conflict callback. Once
/// all pending conflicts are resolved, the completed callback will be invoked with the
/// retrieved data. In the event of an error, the completed callback will be invoked with the
/// corresponding error status. All callbacks will be executed on the game thread.
///
/// The name of the file to open. Filenames must consist of
/// only non-URL reserved characters (i.e. a-z, A-Z, 0-9, or the symbols "-", ".", "_", or "~")
/// be between 1 and 100 characters in length (inclusive).
/// The data source to use. for a description
/// of the available options here.
/// If set to true, the data for the two
/// conflicting files will be automatically retrieved and passed as parameters in
/// . If set to false, null binary data
/// will be passed into and the caller will have to fetch
/// it themselves.
/// The callback that will be invoked if one or more conflict is
/// encountered while executing this method. Note that more than one conflict may be present
/// and that this callback might be executed more than once to resolve multiple conflicts.
/// This callback is always executed on the game thread.
/// The callback that is invoked when this operation finishes.
/// The returned metadata will only be non-null if the open succeeded. If an error is
/// encountered during conflict resolution, that error will be reflected here. This callback
/// will always execute on the game thread and the returned metadata (if any) will be "Open".
///
void OpenWithManualConflictResolution(string filename, DataSource source,
bool prefetchDataOnConflict, ConflictCallback conflictCallback,
Action completedCallback);
///
/// Reads the binary data of the passed saved game. The passed metadata must be opened (i.e.
/// returns true). The callback will always be executed
/// on the game thread.
///
/// The metadata for the saved game whose binary data we want to read.
/// This metadata must be open. If it is not open, the method will immediately fail with status
/// .
///
/// The callback that is invoked when the read finishes. If the
/// read completed without error, the passed status will be and the passed
/// bytes will correspond to the binary data for the file. In the case of
///
void ReadBinaryData(ISavedGameMetadata metadata,
Action completedCallback);
///
/// Shows the select saved game UI with the indicated configuration. If the user selects a
/// saved game in that UI, it will be returned in the passed callback. This metadata will be
/// unopened and must be passed to either or
/// in order to retrieve the binary data.
/// The callback will always be executed on the game thread.
///
/// The user-visible title of the displayed selection UI.
/// The maximum number of saved games the UI may display.
/// This value must be greater than 0.
/// If set to true, show UI that will allow the user to
/// create a new saved game.
/// If set to true show UI that will allow the user to
/// delete a saved game.
/// The callback that is invoked when an error occurs or if the user
/// finishes interacting with the UI. If the user selected a saved game, this will be passed
/// into the callback along with the status. This saved game
/// will not be Open, and must be opened before it can be written to or its binary data can be
/// read. If the user backs out of the UI without selecting a saved game, this callback will
/// receive and a null saved game. This callback will always execute
/// on the game thread.
void ShowSelectSavedGameUI(string uiTitle, uint maxDisplayedSavedGames, bool showCreateSaveUI,
bool showDeleteSaveUI, Action callback);
///
/// Durably commits an update to the passed saved game. When this method returns successfully,
/// the data is durably persisted to disk and will eventually be uploaded to the cloud (in
/// practice, this will happen very quickly unless the device does not have a network
/// connection). If an update to the saved game has occurred after the metadata was retrieved
/// from the cloud, this update will produce a conflict (this commonly occurs if two different
/// devices are writing to the cloud at the same time). All conflicts must be handled the next
/// time this saved game is opened. See and
/// for more information.
///
/// The metadata for the saved game to update. This metadata must be
/// Open (i.e. returns true)."/> If it is not open, the
/// method will immediately fail with status
/// All updates that should be applied to the saved game
/// metadata.
/// The new binary content of the saved game
/// The callback that is invoked when this operation finishes.
/// The returned metadata will only be non-null if the commit succeeded. If an error is
/// encountered during conflict resolution, that error will be reflected here. This callback
/// will always execute on the game thread and the returned metadata (if any) will NOT be
/// "Open" (i.e. commiting an update closes the metadata).
void CommitUpdate(ISavedGameMetadata metadata, SavedGameMetadataUpdate updateForMetadata,
byte[] updatedBinaryData, Action callback);
///
/// Returns the metadata for all known saved games for this game. All returned saved games are
/// not open, and must be opened before they can be used for writes or binary data reads. The
/// callback will always occur on the game thread.
///
/// The data source to use. for a description
/// of the available options here.
/// The callback that is invoked when this operation finishes.
/// The returned metadata will only be non-empty if the commit succeeded. If an error is
/// encountered during the fetch, that error will be reflected here. This callback
/// will always execute on the game thread and the returned metadata (if any) will NOT be
/// "Open".
void FetchAllSavedGames(DataSource source,
Action> callback);
///
/// Delete the specified snapshot.
/// This will delete the data of the snapshot locally and on the server.
///
/// the saved game metadata identifying the data to
/// delete.
void Delete(ISavedGameMetadata metadata);
}
///
/// An interface that allows developers to resolve metadata conflicts that may be encountered while
/// opening saved games.
///
public interface IConflictResolver
{
///
/// Resolves the conflict by choosing the passed metadata to be canonical. The passed metadata
/// must be one of the two instances passed as parameters into -
/// this instance will be kept as the cannonical value in the cloud.
///
/// The chosen metadata. This metadata must be open. If it is not
/// open, the invokation of that produced this
/// ConflictResolver will immediately fail with .
void ChooseMetadata(ISavedGameMetadata chosenMetadata);
///
/// Resolves the conflict and updates the data.
///
/// Metadata for the chosen version. This is either the
/// original or unmerged metadata provided when the callback is invoked.
/// Metadata update, same as when committing changes.
/// Updated data to use when resolving the conflict.
void ResolveConflict(ISavedGameMetadata chosenMetadata, SavedGameMetadataUpdate metadataUpdate,
byte[] updatedData);
}
}