/// <summary> /// Used by above initialize methods to create any type of cloud preference. /// </summary> /// <param name="key">Must be a unique identifier for this specific value.</param> /// <param name="item">The <see cref="SyncableItem"/> you want to store in the local <see cref="GameData"/>.</param> private static void CreateItem(string key, SyncableItem item) { SyncableItem localItem; // If the item already exists, update it instead of creating a new item if (s_localGameData.SyncableItems.TryGetValue(key, out localItem)) { // Ignore if the new item doesn't have the same persistence type as the current item if (localItem.Metadata.PersistenceType == item.Metadata.PersistenceType) { // Only set item if the value is different from the current value if (!localItem.Equals(item)) { s_localGameData.SyncableItems[key] = ConflictResolver.ResolveConflict(localItem, item); IsLocalDataDirty = true; } } } else { // Create new item s_localGameData.SyncableItems.Add(key, item); IsLocalDataDirty = true; } }
/// <summary> /// Ensures that a specified <see cref="string"/> exists in the local <see cref="GameData"/>. /// </summary> /// <param name="key">Must be a unique identifier for this specific value.</param> /// <param name="persistenceType"> /// The method of conflict resolution to be used in case of a data conflict. Can happen if the data is altered by a different device. /// <see cref="PersistenceType.Latest"/> will prefer the latest (newest) <see cref="string"/>. /// <see cref="PersistenceType.Highest"/> will prefer the longest <see cref="string"/>. /// <see cref="PersistenceType.Lowest"/> will prefer the shortest <see cref="string"/>. /// </param> /// <param name="value">The initial value for this <see cref="string"/>.</param> public static void InitializeString(string key, PersistenceType persistenceType, string value) { if (!s_localGameData.SyncableItems.ContainsKey(key)) { var metaData = new SyncableItemMetaData(DataType.String, persistenceType); var syncableItem = new SyncableItem(value, metaData); CreateItem(key, syncableItem); } }
/// <summary> /// Ensures that a specified <see cref="decimal"/> exists in the local <see cref="GameData"/>. /// </summary> /// <param name="key">Must be a unique identifier for this specific value.</param> /// <param name="persistenceType"> /// The persistence type to use in case of a data conflict. /// <see cref="PersistenceType.Latest"/> will always prefer the newest data. /// <see cref="PersistenceType.Highest"/> will prefer the highest value. /// <see cref="PersistenceType.Lowest"/> will prefer the lowest value. /// </param> /// <param name="value">The initial value for this <see cref="decimal"/>.</param> public static void InitializeDecimal(string key, PersistenceType persistenceType, decimal value) { if (!s_localGameData.SyncableItems.ContainsKey(key)) { var metaData = new SyncableItemMetaData( DataType.Decimal, persistenceType); var syncableItem = new SyncableItem( value.ToString(CultureInfo.InvariantCulture), metaData); CreateItem(key, syncableItem); } }
/// <summary> /// Used to set a <see cref="decimal"/> that will be stored in the cloud. /// </summary> /// <param name="key">Must be a unique identifier for this specific value.</param> /// <param name="value">The value for this <see cref="decimal"/>.</param> /// <param name="persistenceType">The persistence type to use in case of a data conflict (ignored if value has been set before).</param> public static void SetDecimal(string key, decimal value, PersistenceType persistenceType) { if (!s_localGameData.SyncableItems.ContainsKey(key)) { var metaData = new SyncableItemMetaData(DataType.Decimal, persistenceType); var syncableItem = new SyncableItem(value.ToString(CultureInfo.InvariantCulture), metaData); s_localGameData.SyncableItems.Add(key, syncableItem); IsLocalDataDirty = true; } if (s_localGameData.SyncableItems[key].Metadata.DataType == DataType.Decimal) { s_localGameData.SyncableItems[key].ValueString = value.ToString(CultureInfo.InvariantCulture); IsLocalDataDirty = true; } else { throw new UnexpectedCollectionElementTypeException(key, typeof(decimal)); } }
/// <summary> /// Used to set a <see cref="string"/> that will be stored in the cloud. PersistenceType.Latest will be used in case of data conflict. /// </summary> /// <param name="key">Must be a unique identifier for this specific value.</param> /// <param name="value">The value for this <see cref="string"/>.</param> /// <param name="persistenceType">The persistence type to use in case of a data conflict (ignored if value has been set before).</param> public static void SetString(string key, string value, PersistenceType persistenceType) { if (!s_localGameData.SyncableItems.ContainsKey(key)) { var metaData = new SyncableItemMetaData(DataType.String, persistenceType); var syncableItem = new SyncableItem(value, metaData); s_localGameData.SyncableItems.Add(key, syncableItem); IsLocalDataDirty = true; } if (s_localGameData.SyncableItems[key].Metadata.DataType == DataType.String) { s_localGameData.SyncableItems[key].ValueString = value; IsLocalDataDirty = true; } else { throw new UnexpectedCollectionElementTypeException(key, typeof(string)); } }
/// <summary> /// Will return the highest of two <see cref="SyncableItem"/>s. /// </summary> /// <param name="localItem">The first of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <param name="otherItem">The second of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <returns>Returns the highest of the two <see cref="SyncableItem"/>s.</returns> private static SyncableItem MergeHighest(SyncableItem localItem, SyncableItem otherItem) { switch (localItem.Metadata.DataType) { case DataType.Bool: int result; if (int.TryParse(otherItem.ValueString, out result)) { return(result == 1 ? otherItem : localItem); } return(Convert.ToBoolean(otherItem.ValueString) ? otherItem : localItem); case DataType.Double: return(Convert.ToDouble(localItem.ValueString) > Convert.ToDouble(otherItem.ValueString) ? localItem : otherItem); case DataType.Float: return(Convert.ToSingle(localItem.ValueString) > Convert.ToSingle(otherItem.ValueString) ? localItem : otherItem); case DataType.Int: return(Convert.ToInt32(localItem.ValueString) > Convert.ToInt32(otherItem.ValueString) ? localItem : otherItem); case DataType.String: return(localItem.ValueString.Length > otherItem.ValueString.Length ? localItem : otherItem); case DataType.UInt: return(Convert.ToUInt32(localItem.ValueString) > Convert.ToUInt32(otherItem.ValueString) ? localItem : otherItem); case DataType.Long: return(Convert.ToInt64(localItem.ValueString) > Convert.ToInt64(otherItem.ValueString) ? localItem : otherItem); case DataType.Decimal: return(Convert.ToDecimal(localItem.ValueString) > Convert.ToDecimal(otherItem.ValueString) ? localItem : otherItem); default: throw new ArgumentOutOfRangeException(); } }
/// <summary> /// Takes two different <see cref="SyncableItem"/>s and returns one of them based on the persistence type. The two /// items must have the same data type and persistence type. /// </summary> /// <param name="localItem">The first of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <param name="otherItem">The second of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <returns> /// Returns one of the two <see cref="SyncableItem"/>s based on the persistence type. If the two <see cref="SyncableItem"/>s /// don't share the same data type or persistence type, <c>null</c> will be returned. /// </returns> public static SyncableItem ResolveConflict(SyncableItem localItem, SyncableItem otherItem) { if (localItem.Metadata.PersistenceType != otherItem.Metadata.PersistenceType) { Debug.LogWarning("Tried to resolve data conflict, but the two items did not have the same PersistenceType! Will use local data."); return(localItem); } if (localItem.Metadata.DataType != otherItem.Metadata.DataType) { Debug.LogWarning("Tried to resolve data conflict, but the two items did not have the same DataType! Will use local data."); return(localItem); } SyncableItem mergedItem; switch (localItem.Metadata.PersistenceType) { case PersistenceType.Latest: mergedItem = MergeLatest(localItem, otherItem); break; case PersistenceType.Highest: mergedItem = MergeHighest(localItem, otherItem); break; case PersistenceType.Lowest: mergedItem = MergeLowest(localItem, otherItem); break; default: throw new ArgumentOutOfRangeException(); } return(mergedItem); }
/// <summary> /// Will return the latest/newest of two <see cref="SyncableItem"/>s. /// </summary> /// <param name="localItem">The first of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <param name="otherItem">The second of two <see cref="SyncableItem"/>s that are in conflict.</param> /// <returns>Returns the latest/newest of the two <see cref="SyncableItem"/>s.</returns> private static SyncableItem MergeLatest(SyncableItem localItem, SyncableItem otherItem) { return(localItem.Metadata.Timestamp.CompareTo(otherItem.Metadata.Timestamp) > 0 ? localItem : otherItem); }