Skip to content

Instantly share code, notes, and snippets.

@FuzzySlipper
Created September 18, 2018 20:23
Show Gist options
  • Save FuzzySlipper/20523cb10a29690346c38fafaf96d21d to your computer and use it in GitHub Desktop.
Save FuzzySlipper/20523cb10a29690346c38fafaf96d21d to your computer and use it in GitHub Desktop.
Runtime CastleDB file reader
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using SimpleJSON;
namespace PixelComrades {
public class JsonDB {
private const string Id = "ID";
private const string EnumProp = "hasIndex";
private JsonParser _parser;
private Dictionary<string, List<LoadedDataEntry>> _sheets = new Dictionary<string, List<LoadedDataEntry>>();
public Dictionary<string, List<LoadedDataEntry>> Sheets { get => _sheets; }
public JsonParser Parser { get => _parser; }
public JsonDB(string input) {
if (string.IsNullOrEmpty(input)) {
return;
}
_parser = new JsonParser(input);
SetupSheets();
}
public void SetupSheets() {
for (var s = 0; s < _parser.Sheets.Count; s++) {
var sheet = _parser.Sheets[s];
var list = new List<LoadedDataEntry>();
_sheets.Add(sheet.Name, list);
for (int r = 0; r < sheet.Rows.Count; r++) {
JSONNode line = sheet.Rows[r];
var id = line[0].Value;
var entry = new LoadedDataEntry(id, sheet, sheet.Columns.Count);
entry.Cells[0] = new LoadedDataCell<string>(Id, entry, id);
for (int c = 1; c < sheet.Columns.Count; c++) {
entry.Cells[c] = GetColumnData(entry, line[c], sheet.Columns[c]);
}
list.Add(entry);
}
if (sheet.HasProp(EnumProp)) {
GameData.Enums.Add(sheet.Name, new FakeEnum(sheet));
}
}
for (var s = 0; s < _parser.Nested.Count; s++) {
var nestedSheet = _parser.Nested[s];
var targetSheet = _parser.GetSheetWithName(nestedSheet.NestedParent);
var column = targetSheet.GetColumn(nestedSheet.NestedColumn);
var entryList = _sheets[targetSheet.Name];
for (int e = 0; e < entryList.Count; e++) {
var entry = entryList[e];
var list = entry.Get(column.Name) as LoadedDataList;
if (list == null || nestedSheet.Columns.Count == 0) {
Debug.Log(string.Format("No list for {0} at {1}", column.Name, entry.ID));
continue;
}
for (int c = 0; c < nestedSheet.Columns.Count; c++) {
var nestedColumn = nestedSheet.Columns[c];
for (int i = 0; i < list.TempData.Count; i++) {
var data = list.TempData[i][nestedColumn.Name];
if (data == null) {
continue;
}
list.Value.Add(GetColumnData(entry, data, nestedColumn));
}
}
list.TempData = null;
}
}
}
private LoadedDataCell GetColumnData(LoadedDataEntry owner, JSONNode data, ColumnNode column) {
LoadedDataCell cell = null;
var typeNum = GetTypeNumFromCastleDBTypeString(column.TypeStr);
switch (typeNum) {
case "0":
case "1":
//0 = UniqueIdentifier
//1 = Text
cell = new LoadedDataCell<string>(column.Name, owner, data.Value);
break;
case "2":
cell = new LoadedDataCell<bool>(column.Name, owner, data.AsBool);
break;
case "3":
cell = new LoadedDataCell<int>(column.Name, owner, data.AsInt);
break;
case "4":
cell = new LoadedDataCell<float>(column.Name, owner, data.AsFloat);
break;
case "6":
cell = new LoadedDataReference(column.Name, owner, GetTypeStrData(column.TypeStr), data.Value, this);
break;
case "7":
case "13":
case "14":
case "16":
//7 = Image
//13 = File
//14 = Tile
//16 = Dynamic
cell = new LoadedDataCell<string>(column.Name, owner, data.Value);
break;
case "9":
//Custom type
cell = new LoadedDataCell<string>(column.Name, owner, data.Value);
break;
case "11":
//Color
if (ColorUtility.TryParseHtmlString((data.AsInt).ToString("X"), out var color)) {
cell = new LoadedDataCell<Color>(column.Name, owner, color);
}
break;
case "12":
case "15":
//12 = Data Layer
//16 = Tile Layer
cell = new LoadedDataReference(column.Name, owner, GetTypeStrData(column.TypeStr), data.Value, this);
break;
case "5":
case "10":
//5 = Enum
//10 = Flags Enum
var enumMembers = GetEnumValuesFromTypeString(column.TypeStr);
cell = new LoadedDataCell<string>(column.Name, owner, enumMembers[data.AsInt]);
break;
case "8":
//List
var list = new LoadedDataList(column.Name, owner, new List<LoadedDataCell>());
cell = list;
foreach (var item in data) {
list.TempData.Add(item);
}
break;
}
return cell;
}
private string GetTypeStrData(string entry) {
var index = entry.LastIndexOf(':');
if (index < 0) {
for (int i = 0; i < entry.Length; i++) {
if (!System.Char.IsDigit(entry[i])) {
return entry.Substring(i);
}
}
}
return entry.Substring(index + 1);
}
public static string[] GetEnumValuesFromTypeString(string inputString) {
char delimiter1 = ':';
char delimiter2 = ',';
string[] init = inputString.Split(delimiter1);
string[] enumValues = init[1].Split(delimiter2);
return enumValues;
}
public static string GetTypeNumFromCastleDBTypeString(string inputString) {
char delimiter = ':';
string[] typeString = inputString.Split(delimiter);
return typeString[0];
}
}
public class JsonParser {
public const string JsonSheets = "sheets";
public const string JsonName = "name";
public const string JsonDisplay = "display";
public const string JsonType = "typeStr";
public const string JsonColumns = "columns";
public const string JsonLines = "lines";
public const string JsonProps = "props";
public List<SheetNode> Sheets { get; protected set; }
public List<SheetNode> Nested { get; protected set; }
public JsonParser(string text) {
var root = JSON.Parse(text);
Sheets = new List<SheetNode>();
Nested = new List<SheetNode>();
foreach (var item in root[JsonSheets]) {
var sheet = new SheetNode(item.Value);
if (sheet.NestedType) {
Nested.Add(sheet);
}
else {
Sheets.Add(sheet);
}
}
}
public SheetNode GetSheetWithName(string name) {
for (var i = 0; i < Sheets.Count; i++) {
var item = Sheets[i];
if (item.Name == name) {
return item;
}
}
return null;
}
}
public class ColumnNode {
public ColumnNode(JSONNode sheetValue) {
var value = sheetValue;
Name = value[JsonParser.JsonName];
Display = value[JsonParser.JsonDisplay];
TypeStr = value[JsonParser.JsonType];
}
public string TypeStr { get; protected set; }
public string Name { get; protected set; }
public string Display { get; protected set; }
}
public class SheetNode {
public SheetNode(JSONNode sheetValue) {
var value = sheetValue;
Name = value[JsonParser.JsonName];
char delimit = '@';
var splitString = Name.Split(delimit);
if (splitString.Length <= 1) {
NestedType = false;
}
else {
NestedParent = splitString[0];
NestedColumn = splitString[1];
NestedType = true;
}
Columns = new List<ColumnNode>();
Rows = new List<JSONNode>();
Props = value[JsonParser.JsonProps];
foreach (KeyValuePair<string, JSONNode> item in value[JsonParser.JsonColumns]) {
Columns.Add(new ColumnNode(item.Value));
}
foreach (KeyValuePair<string, JSONNode> item in value[JsonParser.JsonLines]) {
Rows.Add(item.Value);
}
}
public string NestedParent { get; }
public string NestedColumn { get; }
public bool NestedType { get; protected set; }
public string Name { get; protected set; }
public List<ColumnNode> Columns { get; protected set; }
public List<JSONNode> Rows { get; protected set; }
public JSONNode Props { get; protected set; }
public ColumnNode GetColumn(string name) {
for (var i = 0; i < Columns.Count; i++) {
var item = Columns[i];
if (item.Name == name) {
return item;
}
}
return null;
}
public bool HasProp(string prop) {
var targetProp = Props[prop];
return targetProp != null && targetProp.AsBool;
}
}
public abstract class LoadedDataCell {
public abstract System.Object Get { get; }
public string ID { get; }
public string FullID { get; }
protected LoadedDataCell(string id, LoadedDataEntry owner) {
ID = id;
FullID = string.Format("{0}.{1}", owner.ID, id);
}
}
public class LoadedDataCell<T> : LoadedDataCell {
public T Value { get;}
public System.Type Type { get; }
public override System.Object Get { get { return Value; } }
public LoadedDataCell(string id, LoadedDataEntry owner, T value) : base(id, owner) {
Value = value;
Type = typeof(T);
}
}
public class LoadedDataList : LoadedDataCell<List<LoadedDataCell>> {
public List<JSONNode> TempData = new List<JSONNode>();
public LoadedDataList(string id, LoadedDataEntry owner, List<LoadedDataCell> value) : base(id, owner, value) {}
}
public class LoadedDataReference : LoadedDataCell {
public string TargetSheet { get; }
public string TargetID { get; }
private JsonDB _db;
private LoadedDataEntry _entry;
public override System.Object Get { get { return Value != null ? _entry.FullID : string.Format("Missing {0} at {1}", TargetID, TargetSheet); } }
public LoadedDataEntry Value {
get {
if (_entry != null) {
return _entry;
}
var list = _db.Sheets[TargetSheet];
for (int i = 0; i < list.Count; i++) {
if (list[i].ID == TargetID) {
_entry = list[i];
break;
}
}
return _entry;
}
}
public LoadedDataReference(string id, LoadedDataEntry owner, string targetSheet, string targetId, JsonDB db) : base(id, owner) {
TargetSheet = targetSheet;
TargetID = targetId;
_db = db;
}
}
public class LoadedDataEntry {
public LoadedDataCell[] Cells;
public string ID { get; }
public string FullID { get; }
public LoadedDataEntry(string id, SheetNode owner, int cnt) {
Cells = new LoadedDataCell[cnt];
ID = id;
FullID = string.Format("{0}.{1}", owner.Name, id);
}
public LoadedDataCell Get(string label) {
for (int i = 0; i < Cells.Length; i++) {
if (Cells[i].ID == label) {
return Cells[i];
}
}
return null;
}
public T Get<T>(string field) {
var cell = Get(field);
if (cell == null) {
return default(T);
}
if (cell is LoadedDataCell<T> typeCell) {
return typeCell.Value;
}
return (T) cell.Get;
}
public T TryGet<T>(string field, T defaultValue) {
var cell = Get(field);
if (cell == null) {
return defaultValue;
}
if (cell is LoadedDataCell<T> typeCell) {
return typeCell.Value;
}
return (T) cell.Get;
}
}
public class FakeEnum {
private const string JsonName = "Name";
private const string JsonDescription = "Description";
private const string JsonValue = "Value";
private string[] _ids;
private string[] _fullIds;
private string[] _names;
private string[] _descriptions;
private int[] _associatedValues;
public string TypeName { get; }
public string[] Ids { get => _ids; }
public string[] FullIds { get => _fullIds; }
public string[] Names { get => _names; }
public string[] Descriptions { get => _descriptions; }
public int[] AssociatedValues { get => _associatedValues; }
public int Length { get { return _ids.Length; } }
public int Count { get { return _ids.Length; } }
public string this[int index] { get { return _ids[index]; } }
public string GetDescriptionAt(int index) {
return _descriptions[index];
}
public string GetID(int index) {
return _ids[index];
}
public string GetID(string key) {
if (TryParse(key, out var index)) {
return _ids[index];
}
return "";
}
public int GetAssociatedValue(int index) {
return _associatedValues[index];
}
public FakeEnum(SheetNode sheet) {
TypeName = sheet.Name;
_ids = new string[sheet.Rows.Count];
_fullIds = new string[sheet.Rows.Count];
_names = new string[sheet.Rows.Count];
_descriptions = new string[sheet.Rows.Count];
_associatedValues = new int[sheet.Rows.Count];
for (int r = 0; r < sheet.Rows.Count; r++) {
JSONNode line = sheet.Rows[r];
_ids[r] = line[0].Value;
_fullIds[r] = string.Format("{0}.{1}", TypeName, _ids[r]);
var name = line[JsonName];
_names[r] = name != null ? name.Value : _ids[r];
var description = line[JsonDescription];
_descriptions[r] = description != null ? description.Value : _ids[r];
var value = line[JsonValue];
_associatedValues[r] = value != null ? value.AsInt : r;
}
}
public bool TryParse(string text, out int index) {
index = -1;
for (int i = 0; i < _ids.Length; i++) {
if (text.CompareCaseInsensitive(_ids[i]) ||
text.CompareCaseInsensitive(_names[i]) ||
text.CompareCaseInsensitive(_fullIds[i])) {
index = i;
return true;
}
}
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment