public static RethinkQueryStatus RethinkConnect(DatabaseCache cache, string connectionString, bool syncLocalChanges = true, bool filterGalaxyData = true) { // Add Unity.Mathematics serialization support to RethinkDB Driver //Converter.Serializer.Converters.Add(new MathJsonConverter()); JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = new List <JsonConverter> { new MathJsonConverter(), Converter.DateTimeConverter, Converter.BinaryConverter, Converter.GroupingConverter, Converter.PocoExprConverter } }; var connection = R.Connection().Hostname(connectionString).Port(RethinkDBConstants.DefaultPort).Timeout(60).Connect(); Debug.Log("Connected to RethinkDB"); if (syncLocalChanges) { // When entries are changed locally, push the changes to RethinkDB cache.OnDataUpdateLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db("Aetheria") .Table(table) .Get(entry.ID) .Replace(entry) .RunAsync(connection); Debug.Log($"Uploaded entry to RethinkDB: {entry.ID} result: {result}"); }; cache.OnDataInsertLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db("Aetheria") .Table(table) .Insert(entry) .RunAsync(connection); Debug.Log($"Inserted entry to RethinkDB: {entry.ID} result: {result}"); }; cache.OnDataDeleteLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db("Aetheria") .Table(table) .Get(entry.ID) .Delete() .RunAsync(connection); Debug.Log($"Deleted entry from RethinkDB: {entry.ID} result: {result}"); }; } var status = new RethinkQueryStatus(); // Get all item data from RethinkDB Task.Run(async() => { status.ItemsEntries = R .Db("Aetheria") .Table("Items") .Count().RunAtom <int>(connection); var result = await R .Db("Aetheria") .Table("Items") .RunCursorAsync <DatabaseEntry>(connection); while (await result.MoveNextAsync()) { var entry = result.Current; //Debug.Log($"Received Items entry from RethinkDB: {entry.GetType()} {(entry as INamedEntry)?.EntryName ?? ""}:{entry.ID}"); cache.Add(entry, true); status.RetrievedItems++; } }).WrapErrors(); // Get globaldata and all galaxy map layer data from RethinkDB Task.Run(async() => { ReqlAst operation = R .Db("Aetheria") .Table("Galaxy"); if (filterGalaxyData) { var filter = ((Table)operation).Filter(o => o["$type"] == typeof(GalaxyMapLayerData).Name || o["$type"] == typeof(GlobalData).Name || o["$type"] == typeof(MegaCorporation).Name); status.GalaxyEntries = filter.Count().RunAtom <int>(connection); operation = filter; } else { status.GalaxyEntries = ((Table)operation).Count().RunAtom <int>(connection); } var result = await operation .RunCursorAsync <DatabaseEntry>(connection); while (await result.MoveNextAsync()) { var entry = result.Current; //Debug.Log($"Received Galaxy entry from RethinkDB: {entry.GetType()} {(entry as INamedEntry)?.EntryName ?? ""}:{entry.ID}"); cache.Add(entry, true); status.RetrievedItems++; } }).WrapErrors(); // Subscribe to changes from RethinkDB Task.Run(async() => { var result = await R .Db("Aetheria") .Table("Items") .Changes() .RunChangesAsync <DatabaseEntry>(connection); while (await result.MoveNextAsync()) { var change = result.Current; if (change.OldValue == null) { Debug.Log($"Received change from RethinkDB (Entry Created): {change.NewValue.ID}"); } else if (change.NewValue == null) { Debug.Log($"Received change from RethinkDB (Entry Deleted): {change.OldValue.ID}"); } else { Debug.Log($"Received change from RethinkDB: {change.NewValue.ID}"); } cache.Add(change.NewValue, true); } }).WrapErrors(); Observable.Timer(DateTimeOffset.Now, TimeSpan.FromSeconds(60)).Subscribe(_ => { Debug.Log(R.Now().Run <DateTime>(connection).ToString() as string); }); return(status); }
public void OnGUI() { Event currentEvent = Event.current; EventType currentEventType = currentEvent.type; _view = BeginScrollView( _view, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, GUI.skin.scrollView, GUILayout.Width(EditorGUIUtility.currentViewWidth), GUILayout.ExpandHeight(true)); if (currentEventType == EventType.DragUpdated) { // Indicate that we don't accept drags ourselves DragAndDrop.visualMode = DragAndDropVisualMode.Rejected; } using (var h = new HorizontalScope()) { _connectionString = TextField(_connectionString); if (GUILayout.Button("Connect")) { EditorPrefs.SetString("RethinkDB.URL", _connectionString); _queryStatus = RethinkConnection.RethinkConnect(_databaseCache, _connectionString); } } using (var h = new HorizontalScope()) { if (GUILayout.Button("Connect All")) { EditorPrefs.SetString("RethinkDB.URL", _connectionString); _queryStatus = RethinkConnection.RethinkConnect(_databaseCache, _connectionString, true, false); } if (GUILayout.Button("Save")) { _databaseCache.Save(new DirectoryInfo(Application.dataPath).Parent.FullName); } } using (new HorizontalScope()) { if (GUILayout.Button("Delete Orphaned Blueprints")) { int count = 0; foreach (var blueprintData in _databaseCache.GetAll <BlueprintData>().ToArray()) { if (_databaseCache.Get(blueprintData.Item) == null) { _databaseCache.Delete(blueprintData); count++; } } Debug.Log($"Deleted {count} orphaned blueprints!"); } } if (_queryStatus != null && _queryStatus.RetrievedItems < _queryStatus.GalaxyEntries + _queryStatus.ItemsEntries) { var progressRect = GetControlRect(false, 20); EditorGUI.ProgressBar(progressRect, (float)_queryStatus.RetrievedItems / (_queryStatus.GalaxyEntries + _queryStatus.ItemsEntries), "Sync Progress"); } // else // { // if(_itemBlueprints == null) // _itemBlueprints = _databaseCache.GetAll<ItemData>() // .Select(itemData => new {itemData, blueprints = _databaseCache.GetAll<BlueprintData>() // .Where(blueprintData => blueprintData.Item == itemData.ID) // .Select(bp => bp.ID) // .ToList()}) // .ToDictionary(x => x.itemData.ID, x => x.blueprints); // } GUILayout.Space(5); // if(GUILayout.Button("Log Cache Count")) // Debug.Log($"Cache contains {_databaseCache.AllEntries.Count()} elements"); using (var h = new HorizontalScope(ListItemStyle)) { _itemsFoldout = Foldout(_itemsFoldout, "Items", true); } if (_itemsFoldout) { var items = _databaseCache.AllEntries.Where(item => item is ItemData).Cast <ItemData>().ToArray(); for (var i = 0; i < _itemTypes.Length; i++) { using (var h = new HorizontalScope(ListItemStyle)) { GUILayout.Space(10); _itemTypeFoldouts[i] = Foldout(_itemTypeFoldouts[i], ((NameAttribute)_itemTypes[i].GetCustomAttribute(typeof(NameAttribute)))?.Name ?? FormatTypeName(_itemTypes[i].Name), true); } if (_itemTypeFoldouts[i]) { var typedItems = items.Where(e => e.GetType() == _itemTypes[i]).OrderBy(item => item.Name); IEnumerable <IGrouping <string, ItemData> > itemGroups = new List <IGrouping <string, ItemData> >(); if (_itemTypes[i] == typeof(SimpleCommodityData)) { itemGroups = typedItems.GroupBy(item => Enum.GetName(typeof(SimpleCommodityCategory), (item as SimpleCommodityData).Category)); } else if (_itemTypes[i] == typeof(CompoundCommodityData)) { itemGroups = typedItems.GroupBy(item => Enum.GetName(typeof(CompoundCommodityCategory), (item as CompoundCommodityData).Category)); } else if (_itemTypes[i] == typeof(GearData)) { itemGroups = typedItems.GroupBy(item => Enum.GetName(typeof(HardpointType), (item as GearData).Hardpoint)); } else if (_itemTypes[i] == typeof(HullData)) { itemGroups = typedItems.GroupBy(item => Enum.GetName(typeof(HullType), (item as HullData).HullType)); } foreach (var itemGroup in itemGroups) { using (var h = new HorizontalScope(ListItemStyle)) { GUILayout.Space(20); if (Foldout(_itemGroupFoldouts.Contains(itemGroup.Key), FormatTypeName(itemGroup.Key), true)) { _itemGroupFoldouts.Add(itemGroup.Key); } else { _itemGroupFoldouts.Remove(itemGroup.Key); } } if (_itemGroupFoldouts.Contains(itemGroup.Key)) { foreach (var item in itemGroup) { var style = ListItemStyle; var selected = SelectedItem == item.ID; using (new HorizontalScope(selected?SelectedStyle:style)) { using (var h = new HorizontalScope()) { if (h.rect.Contains(currentEvent.mousePosition)) { if (currentEventType == EventType.MouseDrag) { DragAndDrop.PrepareStartDrag(); DragAndDrop.SetGenericData("Item", item.ID); DragAndDrop.StartDrag("Database Item"); } else if (currentEventType == EventType.MouseUp) { Select(item); } } GUILayout.Space(30); GUILayout.Label(item.Name, selected ? EditorStyles.whiteLabel : GUI.skin.label); } using (var h = new HorizontalScope(GUILayout.Width(15))) { if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none)) { if (_itemBlueprintFoldouts.Contains(item.ID)) { _itemBlueprintFoldouts.Remove(item.ID); } else { _itemBlueprintFoldouts.Add(item.ID); } } GUILayout.Label(_itemBlueprints[item.ID].Count.ToString()); var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight)); if (Event.current.type == EventType.Repaint) { var controlId = GUIUtility.GetControlID(1337, FocusType.Keyboard, position); EditorStyles.foldout.Draw(rect, GUIContent.none, controlId, _itemBlueprintFoldouts.Contains(item.ID)); } } } if (_itemBlueprintFoldouts.Contains(item.ID)) { foreach (var blueprintData in _itemBlueprints[item.ID] .Select(id => _databaseCache.Get <BlueprintData>(id)) .OrderBy(bp => bp.Quality)) { var blueprintStyle = ListItemStyle; var blueprintSelected = SelectedItem == blueprintData.ID; using (var h = new HorizontalScope(blueprintSelected ? SelectedStyle : blueprintStyle)) { if (h.rect.Contains(currentEvent.mousePosition)) { if (currentEventType == EventType.MouseDrag) { DragAndDrop.PrepareStartDrag(); DragAndDrop.SetGenericData("Item", blueprintData.ID); DragAndDrop.StartDrag("Database Item"); } else if (currentEventType == EventType.MouseUp) { Select(blueprintData); } } GUILayout.Space(40); GUILayout.Label(blueprintData.Name, blueprintSelected ? EditorStyles.whiteLabel : GUI.skin.label); } } using (var h = new HorizontalScope(ListItemStyle)) { if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none)) { var blueprint = new BlueprintData { Item = item.ID, Name = item.Name }; _databaseCache.Add(blueprint); Select(blueprint); } GUILayout.Space(40); GUILayout.Label("New Blueprint"); var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight)); GUI.DrawTexture(rect, Icons.Instance.plus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0); } } } using (var h = new HorizontalScope(ListItemStyle)) { if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none)) { CreateItem(_itemTypes[i], newItem => { if (newItem is SimpleCommodityData simpleCommodityData) { simpleCommodityData.Category = (SimpleCommodityCategory)Enum.Parse(typeof(SimpleCommodityCategory), itemGroup.Key); simpleCommodityData.Name = $"New {itemGroup.Key}"; } else if (newItem is CompoundCommodityData compoundCommodityData) { compoundCommodityData.Category = (CompoundCommodityCategory)Enum.Parse( typeof(CompoundCommodityCategory), itemGroup.Key); compoundCommodityData.Name = $"New {itemGroup.Key}"; } else if (newItem is GearData gearData) { gearData.Hardpoint = (HardpointType)Enum.Parse(typeof(HardpointType), itemGroup.Key); gearData.Name = $"New {itemGroup.Key}"; } else if (newItem is HullData hullData) { hullData.HullType = (HullType)Enum.Parse(typeof(HullType), itemGroup.Key); hullData.Name = $"New {itemGroup.Key}"; } }); } GUILayout.Space(30); GUILayout.Label("New " + itemGroup.Key); var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight)); GUI.DrawTexture(rect, Icons.Instance.plus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0); } } } if (!itemGroups.Any()) { using (var h = new HorizontalScope(ListItemStyle)) { if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none)) { CreateItem(_itemTypes[i]); } GUILayout.Space(20); GUILayout.Label("New " + _itemTypes[i].Name); var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight)); GUI.DrawTexture(rect, Icons.Instance.plus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0); } } } } } var entries = _databaseCache.AllEntries.Where(item => !(item is ItemData)).ToArray(); for (var i = 0; i < _entryTypes.Length; i++) { using (var h = new HorizontalScope(ListItemStyle)) { _entryFoldouts[i] = Foldout(_entryFoldouts[i], ((NameAttribute)_entryTypes[i].GetCustomAttribute(typeof(NameAttribute)))?.Name ?? FormatTypeName(_entryTypes[i].Name), true); } if (_entryFoldouts[i]) { int index = 0; foreach (var entry in entries.Where(e => e.GetType() == _entryTypes[i]).OrderBy(entry => entry is INamedEntry namedEntry ? namedEntry.EntryName : entry.ID.ToString())) { index++; var style = ListItemStyle; var selected = SelectedItem == entry.ID; using (var h = new HorizontalScope(selected?SelectedStyle:style)) { if (h.rect.Contains(currentEvent.mousePosition)) { if (currentEventType == EventType.MouseDrag) { DragAndDrop.PrepareStartDrag(); DragAndDrop.SetGenericData("Item", entry.ID); DragAndDrop.StartDrag("Database Item"); } else if (currentEventType == EventType.MouseUp) { Select(entry); } } GUILayout.Space(10); GUILayout.Label((entry as INamedEntry)?.EntryName ?? index.ToString(), selected ? EditorStyles.whiteLabel : GUI.skin.label); } } using (var h = new HorizontalScope(ListItemStyle)) { if (GUI.Button(h.rect, GUIContent.none, GUIStyle.none)) { CreateItem(_entryTypes[i]); } GUILayout.Space(10); GUILayout.Label("New " + _entryTypes[i].Name); var rect = GetControlRect(false, GUILayout.Width(EditorGUIUtility.singleLineHeight)); GUI.DrawTexture(rect, Icons.Instance.plus, ScaleMode.StretchToFill, true, 1, LabelColor, 0, 0); } } } EndScrollView(); }
public static RethinkQueryStatus RethinkConnect(CultCache cache, string connectionString, string dbName, bool syncLocalChanges = true) { // Add Unity.Mathematics serialization support to RethinkDB Driver JsonConvert.DefaultSettings = () => new JsonSerializerSettings { Converters = new List <JsonConverter> { new MathJsonConverter(), Converter.DateTimeConverter, Converter.BinaryConverter, Converter.GroupingConverter, Converter.PocoExprConverter } }; var syncTables = typeof(DatabaseEntry).GetAllChildClasses() .Select(t => t.GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Default").Distinct(); var connectionStringDomainLength = connectionString.IndexOf(':'); if (connectionStringDomainLength < 1) { throw new ArgumentException("Illegal Connection String: must include port!"); } var portString = connectionString.Substring(connectionStringDomainLength + 1); if (!int.TryParse(portString, out var port)) { throw new ArgumentException($"Illegal connection string! \"{portString}\" is not a valid port number!"); } var connection = R.Connection() .Hostname(connectionString.Substring(0, connectionStringDomainLength)) .Port(port).Timeout(60).Connect(); var tables = R.Db(dbName).TableList().RunAtom <string[]>(connection); foreach (var st in syncTables.Where(st => !tables.Contains(st))) { R.Db(dbName).TableCreate(st).RunNoReply(connection); } if (syncLocalChanges) { // When entries are changed locally, push the changes to RethinkDB cache.OnDataUpdateLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db(dbName) .Table(table) .Get(entry.ID) .Replace(entry) .RunAsync(connection); }; cache.OnDataInsertLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db(dbName) .Table(table) .Insert(entry) .RunAsync(connection); }; cache.OnDataRemoveLocal += async entry => { var table = entry.GetType().GetCustomAttribute <RethinkTableAttribute>()?.TableName ?? "Other"; var result = await R .Db(dbName) .Table(table) .Get(entry.ID) .Delete() .RunAsync(connection); }; } var status = new RethinkQueryStatus(); foreach (var table in syncTables) { // Get entries from RethinkDB Task.Run(async() => { status.TotalEntries += R .Db(dbName) .Table(table) .Count().RunAtom <int>(connection); var result = await R .Db(dbName) .Table(table) .RunCursorAsync <DatabaseEntry>(connection); while (await result.MoveNextAsync()) { var entry = result.Current; cache.Add(entry, true); status.RetrievedEntries++; } }).WrapAwait(); // Subscribe to changes from RethinkDB Task.Run(async() => { var result = await R .Db(dbName) .Table(table) .Changes() .RunChangesAsync <DatabaseEntry>(connection); while (await result.MoveNextAsync()) { var change = result.Current; cache.Add(change.NewValue, true); } }).WrapAwait(); } return(status); }