// // 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); } }