diff options
author | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
---|---|---|
committer | chai <chaifix@163.com> | 2019-08-14 22:50:43 +0800 |
commit | 15740faf9fe9fe4be08965098bbf2947e096aeeb (patch) | |
tree | a730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/Export/XboxServices.cs |
Diffstat (limited to 'Runtime/Export/XboxServices.cs')
-rw-r--r-- | Runtime/Export/XboxServices.cs | 794 |
1 files changed, 794 insertions, 0 deletions
diff --git a/Runtime/Export/XboxServices.cs b/Runtime/Export/XboxServices.cs new file mode 100644 index 0000000..23cddf6 --- /dev/null +++ b/Runtime/Export/XboxServices.cs @@ -0,0 +1,794 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Threading; +using UnityEngine; + +namespace UnityEngine +{ + #if UNITY_XENON_API && ENABLE_XENON_SOCIALAPI + // TODO: We have no concept of achievement types like xbox has, not exposed + // TODO: No concept of user privilege levels, not exposed + // NOTE: There doesn't seem to be any way to explicitly set your own state (online/offline/away/busy/playing) + public class XboxLive : ISocial + { + private static LocalUser s_LocalUser; + private Action<AchievementDescription[]> m_AchievementDescriptionCallback; + private Action<bool> m_AchievementReportingCallback; + + private static GameObject s_SessionObject; + private static GameObject s_LeaderboardRoutine; + + internal const uint kDefaultUserIndex = 0; + + public static bool onlineMode { get; set; } + + public XboxLive() + { + if (X360Core.IsUserSignedIn(0, true)) + { + Debug.Log("User already signed in, in online mode"); + onlineMode = true; + } + + // DEBUG: Log when these callbacks are triggered but have not been set elsewhere + X360Achievements.OnAchievementsEnumerated = () => { Debug.Log("OnAchievementsEnumerated called"); }; + X360Achievements.OnUserAchievementsUpdated = (id) => { Debug.Log("OnUserAchievementsUpdated called for user " + id); }; + X360Achievements.OnAward = + (uid, aid, status) => { Debug.Log("OnAward called for user=" + uid + " achievement=" + aid + " status=" + status); }; + } + + public void ShowAchievementsUI() + { + X360Achievements.ShowUI(kDefaultUserIndex); + } + + // TODO: These should include an action which is triggered (OnSystemUIVisibilityChange) when the UI state changes (UI is dismissed) + public void ShowAchievementsUI(uint userIndex) + { + X360Achievements.ShowUI(userIndex); + } + + public void ShowLeaderboardUI() + { + Debug.Log("Not implemented"); + } + + public void ShowFriendsUI(uint userIndex) + { + X360Friends.ShowFriendsUI(userIndex); + } + + // NOTE: localUser.underage is not implemented on Xbox Live + public LocalUser localUser + { + get + { + if (s_LocalUser == null) + s_LocalUser = new LocalUser(); + GetLocalUser(kDefaultUserIndex, ref s_LocalUser); + return s_LocalUser; + } + } + + // TODO: Cache user so he's not recreated every time + // TODO: Maybe set this up as a generic list, the list is populated as users are logged in and valid, no need to create on access, no need for seperate GetCount() type functions + public LocalUser this[uint index] + { + get + { + LocalUser user = new LocalUser(); + GetLocalUser(index, ref user); + return user; + } + } + + private void GetLocalUser(uint index, ref LocalUser user) + { + user.m_Authenticated = X360Core.IsUserSignedIn(index, onlineMode); + if (!user.m_Authenticated) + { + //Debug.Log("Must be logged in before getting local user details"); + return; + } + user.m_UserName = X360Core.GetUserName(index); + user.m_UserId = X360Core.GetUserOnlinePlayerId(index).Raw.ToString(); + user.m_Friends = GetFriendsList(index); + user.m_Image = X360Core.GetUserGamerPicture(index, true); + } + + public void Authenticate(Action<bool> callback) + { + // Request online login of exactly 1 user + X360Core.RequestSignIn(1, 1, onlineMode); + X360Core.OnUserStateChange = delegate() { callback(true); }; + } + + // Request sign-in for min users up to max user count + public void Authenticate(uint minUsers, uint maxUsers, Action<bool> callback) + { + X360Core.RequestSignIn(minUsers, maxUsers, onlineMode); + // Xbox core doesn't report success/failure of sign-in attempts + // so lets just wrap it in another delegate which always reports true + // I assume this means all users have signed in + X360Core.OnUserStateChange = delegate() { callback(true); }; + } + + // This is kind of pointless here as the friends list is not loaded seperately + public void LoadFriends(Action<bool> callback) + { + /*if (X360Friends.IsInitialized(0)) + { + Debug.Log("Not initialized yet... "); + X360Friends.OnFriendsUpdated = delegate(uint index) { callback(true); }; + return; + }*/ + callback(true); + Debug.Log("Friends list is always populated in the local user automatically"); + } + + private UserProfile[] GetFriendsList(uint index) + { + var friends = new List<UserProfile>(); + for (uint i = 0; i < X360Friends.GetFriendCount(index); i++) + { + UserProfile friend = new UserProfile(); + friend.m_UserName = X360Friends.GetFriendName(index, i); + friend.m_UserId = X360Friends.GetFriendPlayerId(index, i).Raw.ToString(); + friend.m_IsFriend = true; + X360FriendState state = X360Friends.GetFriendState(index, i); + friend.m_State = ConvertState(state); + friend.m_Image = X360Core.GetPlayerGamerPicture(index, X360Friends.GetFriendPlayerId(index, i), false); + friends.Add(friend); + } + return friends.ToArray(); + } + + private UserState ConvertState(X360FriendState state) + { + if (state.IsOnline) return UserState.Online; + if (state.IsOnlineAndAway) return UserState.OnlineAndAway; + if (state.IsOnlineAndBusy) return UserState.OnlineAndBusy; + if (state.IsPlaying) return UserState.Playing; + return UserState.Offline; + } + + // Achievements are set up with the Xbox 360 and LIVE Authoring Submission Tool (XLAST) + // The points (or gamescore) of each one depends on the title type (retail/arcade). + // They are actually loaded automatically at startup, so this doesn't actually load + // anything. + public void LoadAchievementDescriptions(Action<AchievementDescription[]> callback) + { + if (!X360Achievements.IsEnumerated()) + { + m_AchievementDescriptionCallback = callback; + X360Achievements.OnAchievementsEnumerated = CallbackAchivementDescriptionLoader; + } + else if (X360Achievements.GetCount() == 0) + { + Debug.Log("No achievement descriptions found"); + callback(new AchievementDescription[0]); + } + else + { + callback(PopulateAchievementDescriptions()); + } + } + + private void CallbackAchivementDescriptionLoader() + { + if (m_AchievementDescriptionCallback != null) + m_AchievementDescriptionCallback(PopulateAchievementDescriptions()); + } + + private AchievementDescription[] PopulateAchievementDescriptions() + { + var achievements = new List<AchievementDescription>(); + for (uint i = 0; i < X360Achievements.GetCount(); ++i) + { + // TODO: Should the points maybe just be an uint like xbox uses? Not like you get negative points ever. + X360Achievement xboxAchoo = new X360Achievement(i); + AchievementDescription achievement = new AchievementDescription( + xboxAchoo.Id.ToString(), + xboxAchoo.Label, + xboxAchoo.Picture, + xboxAchoo.Description, + xboxAchoo.Unachieved, + xboxAchoo.ShowUnachieved, + (int)xboxAchoo.Cred); + achievements.Add(achievement); + } + return achievements.ToArray(); + } + + // Apparently xbox has no concept of unhiding achievements by reporting 0 progress + // TODO: This is printed in the log: + // '[XUI] Warning: XuiControlPlayOptionalVisual: no fallback for control: PopupControl. Trying to play: "Normal"->"EndNormal"' + public void ReportProgress(string id, double progress, Action<bool> callback) + { + uint numericId; + if (!XboxLiveUtil.TryExtractId(id, out numericId)) return; + + ReportProgress(numericId, progress, callback); + } + + public void ReportProgress(uint id, double progress, Action<bool> callback) + { + m_AchievementReportingCallback = callback; + X360Achievements.OnAward = CallbackAchivementReported; + X360Achievements.AwardUser(kDefaultUserIndex, id); + } + + private void CallbackAchivementReported(uint userIndex, uint achievementId, X360AchievementStatus status) + { + // TODO: Check if the desired achievement ID actually got updated. + // TODO: We should have enums here instead of bools + if (m_AchievementReportingCallback != null) + { + if (status == X360AchievementStatus.AlreadyAwarded || status == X360AchievementStatus.Succeeded) + m_AchievementReportingCallback(true); + else + m_AchievementReportingCallback(false); + } + } + + public void LoadAchievements(Action<Achievement[]> callback) + { + // TODO: This should really be communicated back with an enum, but since users can re-enumerate + // it's kind of useless. We should allow that or automatically handle it. + if (!X360Achievements.IsEnumerated()) + { + Debug.Log("Achievements not yet enumerated"); + callback(new Achievement[0]); + } + if (X360Achievements.GetCount() == 0) + { + Debug.Log("No achievements found or achieved"); + callback(new Achievement[0]); + } + else + { + callback(PopulateAchievements(kDefaultUserIndex)); + } + } + + private Achievement[] PopulateAchievements(uint index) + { + var achievements = new List<Achievement>(); + for (uint i = 0; i < X360Achievements.GetCount(); ++i) + { + X360Achievement xboxAchoo = new X360Achievement(i); + if (X360Achievements.IsUnlocked(index, xboxAchoo.Id, true)) + { + Achievement achievement = new Achievement( + xboxAchoo.Id.ToString(), + 100.0, + true, + false, + X360Achievements.GetUnlockTime(index, xboxAchoo.Id)); + achievements.Add(achievement); + } + } + return achievements.ToArray(); + } + + // Got this: + // WRN[XGI]: Mismatched types for property 0x10000001. XUSER_PROPERTY.value.type = 2 but property type is 1. Skipping write. Pass the correct type or 0. + public void ReportScore(long score, string board, Action<bool> callback) + { + uint boardId; + if (!XboxLiveUtil.TryExtractId(board, out boardId)) return; + + // There must be a Score property assigned to this leaderboard and it must expect a 64 bit value + uint propertyId; + if (XboxLiveUtil.TryExtractId("PROPERTY_SCORE", out propertyId)) + { + Debug.Log("Found Score property: " + propertyId); + var properties = new X360UserProperty[1]; + properties[0].Id = propertyId; + properties[0].Value.Type = X360UserDataType.Int64; + properties[0].Value.ValueInt64 = score; + ReportScore(boardId, properties, callback); + } + else + Debug.LogError("Failed to report score to " + board); + } + + // TODO: The API is set up so you have one leaderboard object, and muliple ones if you need to report/read + // to/from multiple leaderboards. Xbox has the X360StatsViewProperties value which allows you to report + // scores etc to multiple leaderboard views at a time. Here we always have one such property. + public void ReportScore(uint boardId, X360UserProperty[] props, Action<bool> callback) + { + if (X360Core.GetTotalOnlineUsers() == 0) + { + Debug.Log("ERROR: Leaderboards can only be used when the user is logged in online (online=" + X360Core.GetTotalOnlineUsers() + " signed-in=" + X360Core.GetTotalSignedInUsers() + ")"); + callback(false); + return; + } + + LeaderboardRoutine routine = GetRoutine(); + XboxScore xboxScore = new XboxScore(); + xboxScore.boardId = boardId; + xboxScore.properties = props; + routine.ReportScore(xboxScore, callback); + } + + public void LoadScores(string category, Action<Score[]> callback) + { + uint categoryId; + if (!XboxLiveUtil.TryExtractId(category, out categoryId)) return; + + // This will find all columns assigned to this leaderboard, if specific columns are desired + // you need to use the xbox specific LoadScores call which has the columns parameter + ushort[] columnIds; + if (XboxLiveUtil.TryExtractId("STATS_COLUMN_" + category.ToUpper(), out columnIds)) + LoadScores(categoryId, columnIds, callback); + else + Debug.LogError("Failed to load scores from " + category); + } + + // We convert X360StatsRow + X360StatsColumn objects to Score objects. + // NOTE: Each row contains user info + all the columns for him. Here we will only support the user info + 1 single column (value/score/points) + // NOTE: We only support getting long (64bit) values, not floats etc. + // NOTE: The date field in the Score class is not populated, not supported here unless we support it as a custom column in the leaderboard config. + // TODO: Fix formattedValue field, it should be possible to populate based on the localized string accociated with a column + // TODO: Unused row fields: Rating + Gamertag + // NOTE: Arbitrated leaderboards + the TrueSkill system complicates matters here. An + // arbitrated session must be started to be able to report scores on an arbitrated leaderboard. + // When using TrueSkill, every player must report all scores (also for other players) and the server + // verifies they are correct. + public void LoadScores(uint boardId, ushort[] columnIds, Action<Score[]> callback) + { + LeaderboardRoutine routine = GetRoutine(); + routine.LoadScores(boardId, columnIds, callback); + } + + private LeaderboardRoutine GetRoutine() + { + LeaderboardRoutine routine; + if (s_LeaderboardRoutine == null) + { + s_LeaderboardRoutine = new GameObject(); + routine = s_LeaderboardRoutine.AddComponent<LeaderboardRoutine>(); + } + else + routine = s_LeaderboardRoutine.GetComponent<LeaderboardRoutine>(); + return routine; + } + + internal static XboxLiveSession GetSession() + { + XboxLiveSession session; + if (s_SessionObject == null) + { + Debug.Log("Creating new session object"); + s_SessionObject = new GameObject(); + session = s_SessionObject.AddComponent<XboxLiveSession>(); + } + else + { + Debug.Log("Reusing old session"); + session = s_SessionObject.GetComponent<XboxLiveSession>(); + } + return session; + } + + public void LoadScores(Leaderboard board, Action<bool> callback) + { + board.m_Loading = true; + var routine = GetRoutine(); + routine.LoadScores((XboxLeaderboard) board, callback); + } + + public bool GetLoading(Leaderboard board) + { + return board.m_Loading; + } + + public static void OpenSession(Action<bool> callback) + { + XboxLiveSession session = GetSession(); + + if (session.running) + { + callback(true); + return; + } + + session.SetupSession(callback); + } + + public static void CloseSession(Action<bool> callback) + { + if (s_SessionObject == null) + { + callback(true); + return; + } + + var routine = s_SessionObject.GetComponent<XboxLiveSession>(); + routine.Cleanup(callback); + } + } + + class LeaderboardRoutine : MonoBehaviour + { + private Action<Score[]> m_ScoresCallback; + private Action<bool> m_LeaderboardCallback; + // If it's possible with the Xbox SDK to do parallel leaderboard queries, then expand this into a boardID=>board hashtable + private XboxLeaderboard m_CurrentBoard; + + public void ReportScore(XboxScore score, Action<bool> callback) + { + StartCoroutine(DoReportScore(score, callback)); + } + + // TODO: Public/private slots needs to be exposed somehow, but this is only relevant for multiplayer games + // TODO: No callbacks are accociated with writes (as they happen later)? + // TODO: Deal with this error: WRN[XGI]: Invalid leaderboard id: 0x00000000 + public IEnumerator DoReportScore(XboxScore score, Action<bool> callback) + { + /*yield return StartCoroutine(SetupSession()); + Debug.Log("Session setup done"); + if (m_Session == null) + { + callback(false); + yield break; + }*/ + XboxLiveSession session = XboxLive.GetSession(); + if (!session.running) + { + Debug.Log("Must open a session first"); + callback(false); + yield break; + } + + var viewProp = new X360StatsViewProperties[1]; + viewProp[0] = new X360StatsViewProperties { ViewId = score.boardId, Properties = score.properties }; + X360PlayerId onlineId = X360Core.GetUserOnlinePlayerId(XboxLive.kDefaultUserIndex); + if (!session.activeSession.WriteStats(onlineId, viewProp)) + { + Debug.LogError("Failed to send leaderboard data to server"); + //yield return StartCoroutine(TearDownSession()); + callback(false); + } + + // DEBUG: Maybe also skip this yield + yield return new WaitForSeconds(0.1F); + Debug.Log(DateTime.Now + ": Waiting for session to become available."); + yield return !session.activeSession.IsIdle(); + + //Debug.Log("Flush stats"); + //m_Session.FlushStats(); + //yield return new WaitForSeconds(0.1F); + //Debug.Log(DateTime.Now + ": Waiting for session to become available."); + //yield return !m_Session.IsIdle(); + + // TODO: Was it actually a success? + callback(true); + } + + public void LoadScores(uint categoryId, ushort[] columnIds, Action<Score[]> callback) + { + m_ScoresCallback = callback; + XboxLeaderboard board = new XboxLeaderboard(); + board.boardId = categoryId; + board.columnIds = columnIds; + board.playerScope = PlayerScope.FriendsOnly; + StartCoroutine(DoLoadScores(board)); + } + + public void LoadScores(XboxLeaderboard leaderboard, Action<bool> callback) + { + m_LeaderboardCallback = callback; + m_CurrentBoard = leaderboard; + + string categoryString = ""; + if (!XboxLiveUtil.TryExtractString(leaderboard.boardId, "STATS_VIEW_", out categoryString)) + Debug.Log("Failed to set leaderboard category name"); + else + m_CurrentBoard.category = categoryString; + StartCoroutine(DoLoadScores(leaderboard)); + } + + public IEnumerator DoLoadScores(XboxLeaderboard board) + { + XboxLiveSession session = XboxLive.GetSession(); + if (!session.running) + { + Debug.Log("Must open a session first"); + yield break; + } + + /*yield return StartCoroutine(SetupSession()); + if (m_Session == null) + { + Debug.Log("Session setup failed"); + FinishCallbacks(false, new Score[0]); + yield break; + }*/ + + if (board.timeScope != TimeScope.AllTime) + Debug.Log("Time scope filtering is not supported, scores from any time are always used."); + + // TODO: Figure out what should be supported, ReadPlayerStats only returns to score + // of the local player, this should go into Leaderboard.localPlayerScore + if (board.playerScope == PlayerScope.FriendsOnly) + Debug.Log("Friends only support not implemented yet, loading from all players"); + X360Stats.ReadLeaderboardByIndex( + board.boardId, + board.columnIds, + (uint) board.range.from, + (uint) board.range.count, + ProcessLeaderboardResult); + + // TODO: If the scores returned do not contain the local player score we need to fetch it + // seperately so the localPlayerScore property can be populated. + //X360Stats.ReadPlayerStats(board.boardId, board.columnIds, m_OnlineID, PopulateLocalPlayerScore); + + Debug.Log(DateTime.Now + ": Waiting for session to become available."); + yield return !session.activeSession.IsIdle(); + if (session.activeSession.LastFunctionFailed()) + { + Debug.Log("Failed to read leaderboard info"); + //m_Session = null; + yield break; + } + } + + private void FinishCallbacks(bool result, Score[] scores) + { + if (m_CurrentBoard != null) + { + m_CurrentBoard.m_Scores = scores; + m_CurrentBoard.m_Loading = false; + if (m_LeaderboardCallback != null) + { + m_LeaderboardCallback(result); + m_LeaderboardCallback = null; + } + m_CurrentBoard = null; + } + else if (m_ScoresCallback != null) + { + m_ScoresCallback(scores); + m_ScoresCallback = null; + } + } + + // TODO: This currently assumes one and only one value (column) per score element + private void ProcessLeaderboardResult(UInt32 viewId, UInt32 totalRows, X360StatsRow[] rows) + { + Debug.Log("Received " + rows.Length + " out of " + totalRows + " rows for id " + viewId); + m_CurrentBoard.m_MaxRange = totalRows; + var scores = new List<Score>(); + foreach (X360StatsRow row in rows) + { + string playerId = row.Xuid.ToString(); + long value = -1; + if (row.Columns.Length >= 1) + value = row.Columns[0].Value.ValueInt64; + DateTime date = DateTime.Now; + int rank = (int)row.Rank; + + Score score = new Score(viewId.ToString(), value, playerId, date, value.ToString(), rank); + scores.Add(score); + } + FinishCallbacks(true, scores.ToArray()); + } + } + + public class XboxScore : Score + { + public X360UserProperty[] properties { get; set; } + public uint boardId { get; set; } + } + + public class XboxLeaderboard : Leaderboard + { + public uint boardId { get; set; } + public ushort[] columnIds { get; set; } + + public XboxLeaderboard() + { + boardId = 0; + columnIds = new ushort[0]; + } + + public override string ToString() + { + return base.ToString() + " BoardID: '" + boardId + "' ColumnIds: '" + columnIds.Length + "'"; + } + } + + + internal class XboxLiveSession : MonoBehaviour + { + private X360Session m_Session; + private bool m_SessionRunning; + private X360PlayerId m_OnlineID; + private bool m_HazError; + + internal bool running { get { return m_SessionRunning; } } + internal X360Session activeSession { get { return m_Session; } } + + XboxLiveSession() + { + Debug.Log("XboxLiveSession GO created"); + // Get the online ID, sanity checks is performed earlier to ensure that the user is actually online + m_OnlineID = X360Core.GetUserOnlinePlayerId(XboxLive.kDefaultUserIndex); + } + + public void Cleanup(Action<bool> callback) + { + StartCoroutine(TearDownSession(callback)); + } + + internal void SetupSession(System.Action<bool> callback = null) + { + StartCoroutine(DoSetupSession(callback)); + } + + // TODO: These sessions calls should all be returning success boolean... + // TODO: Deal with this error: WRN[XGI]: User at index 0 is already a member of a presence session. + private IEnumerator DoSetupSession(System.Action<bool> callback) + { + if (m_SessionRunning) + { + Debug.Log("Skip session creation as we already have one running"); + yield break; + } + + m_HazError = false; + + if (m_Session == null || m_Session.IsDead()) + { + Debug.Log(DateTime.Now + ": Creating session."); + m_Session = X360Session.CreateSinglePlayerSessionWithStats(XboxLive.kDefaultUserIndex, 4, 0); + if (m_Session == null) yield break; + } + yield return StartCoroutine(ValidateOperation("Session creation failed", callback)); + if (m_HazError) yield break; + + Debug.Log(DateTime.Now + ": Session is ready. Joining."); + if (!m_Session.Join(m_OnlineID, false)) + { + Debug.LogError("Failed to join session"); + m_Session = null; + yield break; + } + yield return StartCoroutine(ValidateOperation("Session joining failed", callback)); + if (m_HazError) yield break; + + Debug.Log(DateTime.Now + ": Session is ready. Starting."); + m_Session.Start(); + yield return StartCoroutine(ValidateOperation("Session failed to start", callback)); + if (m_HazError) yield break; + + Debug.Log("Session now running"); + m_SessionRunning = true; + if (callback != null) callback(true); + } + + // We do not delete the running session completely but only end it's operation. It will + // be reused if OpenSession is called again. + private IEnumerator TearDownSession(Action<bool> callback = null) + { + if (m_Session == null || m_Session.IsDead()) + { + if (callback != null) callback(true); + yield break; + } + m_HazError = false; + + Debug.Log("Leaving running session."); + m_Session.Leave(m_OnlineID, false); + yield return StartCoroutine(ValidateOperation("Failed to leave session", callback)); + if (m_HazError) yield break; + + Debug.Log("End running session"); + m_Session.End(); + yield return StartCoroutine(ValidateOperation("Failed to end session", callback)); + if (m_HazError) yield break; + + //Debug.Log("Deleting running session."); + //m_Session.Delete(); + //yield return StartCoroutine(ValidateOperation("Failed to delete session", callback)); + //if (m_HazError) yield break; + + m_SessionRunning = false; + Debug.Log("Successfully tore down session."); + if (callback != null) callback(true); + } + + private IEnumerator ValidateOperation(string error, Action<bool> callback) + { + //Debug.Log(DateTime.Now + ": Waiting for session to become available."); + yield return !m_Session.IsIdle(); + if (m_Session.LastFunctionFailed()) + { + Debug.Log(error); + m_Session = null; + if (callback != null) callback(false); + m_HazError = true; + yield break; + } + } + } + + + static internal class XboxLiveUtil + { + // TODO: Also look out for javascript/boo assemblies + internal static Assembly s_UserScriptingAssembly = Assembly.Load(new AssemblyName("Assembly-CSharp")); + + static internal bool TryExtractId(string id, out ushort[] shorts) + { + List<ushort> foundShorts = new List<ushort>(); + bool success = false; + foreach (FieldInfo info in GetSpaConfigFields()) + { + //Debug.Log("Haz " + info); + if (info.Name.Contains(id.ToUpper())) + { + // The reflected enum contains uint values, so it must first be cast to that type + ushort value = (ushort)(uint)info.GetValue(info.GetType()); + foundShorts.Add(value); + success = true; + //Debug.Log("Found desired ID: " + value); + } + } + shorts = foundShorts.ToArray(); + if (!success) Debug.Log("Failed to find " + id + " in SPAConfig enum"); + return success; + } + + static internal bool TryExtractId(string id, out uint numericId) + { + if (uint.TryParse(id, out numericId)) return true; + + foreach (FieldInfo info in GetSpaConfigFields()) + { + //Debug.Log("Haz " + info); + if (info.Name.Contains(id.ToUpper())) + { + numericId = (uint)info.GetValue(info.GetType()); + //Debug.Log("Found desired ID: " + numericId); + return true; + } + } + + Debug.Log("Failed to find " + id + " in SPAConfig enum"); + return false; + } + + static internal bool TryExtractString(uint numericId, string pattern, out string outputString) + { + outputString = ""; + foreach (FieldInfo info in GetSpaConfigFields()) + { + if (info.Name.Contains(pattern) && (uint)info.GetValue(info.GetType()) == numericId) + { + outputString = info.Name; + return true; + } + } + + Debug.Log("Failed to find " + pattern + " field in SPAConfig enum which matched " + numericId); + return false; + } + + static internal FieldInfo[] GetSpaConfigFields() + { + if (s_UserScriptingAssembly == null) return new FieldInfo[0]; + object spaObject = s_UserScriptingAssembly.CreateInstance("spaconfig", true); + if (spaObject == null) return new FieldInfo[0]; + Type spaType = spaObject.GetType(); + FieldInfo[] spaFields = spaType.GetFields(); + return spaFields; + } + } + +#endif +} |