Skip to content

Commit 3e4c5e4

Browse files
authored
Ez editor chain switching (#806)
* Ez editor chain switching * Published Solution Dependencies to Package Libraries as DLLs * Fixes * Published Solution Dependencies to Package Libraries as DLLs * Update StringListSearchProvider.cs * Published Solution Dependencies to Package Libraries as DLLs * Update ServerSettings.cs * Published Solution Dependencies to Package Libraries as DLLs * Update ServerSettings.cs * Published Solution Dependencies to Package Libraries as DLLs * Update ServerSettings.cs * Published Solution Dependencies to Package Libraries as DLLs * revert revert * Published Solution Dependencies to Package Libraries as DLLs --------- Co-authored-by: sneakzttv <[email protected]>
1 parent 41d3baa commit 3e4c5e4

19 files changed

+333
-39
lines changed

Packages/io.chainsafe.web3-unity/Editor/ServerSettings.cs

Lines changed: 154 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,120 @@
11
using UnityEditor;
22
using UnityEngine;
33
using System;
4+
using System.Collections.Generic;
45
using System.Threading.Tasks;
56
using UnityEngine.Networking;
67
using Newtonsoft.Json;
7-
using ChainSafe.GamingSdk.Editor;
88
using System.IO;
9+
using System.Linq;
910
using System.Text;
1011
using ChainSafe.Gaming.UnityPackage;
12+
using Microsoft.IdentityModel.Tokens;
13+
using UnityEditor.Experimental.GraphView;
14+
using UnityEngine.Events;
15+
using ChainInfo = ChainSafe.Gaming.UnityPackage.Model;
1116

1217
/// <summary>
1318
/// Allows the developer to alter chain configuration via GUI
1419
/// </summary>
1520
public class ChainSafeServerSettings : EditorWindow
1621
{
22+
#region Fields
23+
24+
// Default values
1725
private const string ProjectIdPrompt = "Please enter your project ID";
18-
private const string ChainIdPrompt = "Please enter your chain ID";
19-
private const string ChainPrompt = "Please enter your chain i.e Ethereum, Binance, Cronos";
20-
private const string NetworkPrompt = "Please enter your network i.e Mainnet, Testnet";
21-
private const string SymbolPrompt = "Please enter your chain's native symbol i.e Eth, Cro";
22-
private const string RpcPrompt = "Please enter your RPC endpoint";
23-
26+
private const string ChainIdDefault = "11155111";
27+
private const string ChainDefault = "Ethereum";
28+
private const string NetworkDefault = "Sepolia";
29+
private const string SymbolDefault = "Seth";
30+
private const string RpcDefault = "https://rpc.sepolia.org";
31+
32+
// Chain values
2433
private string projectID;
2534
private string chainID;
2635
private string chain;
2736
private string network;
2837
private string symbol;
2938
private string rpc;
39+
private string newRpc;
3040

3141
Texture2D logo = null;
42+
43+
// Search window
44+
private StringListSearchProvider searchProvider;
45+
private ISearchWindowProvider _searchWindowProviderImplementation;
46+
public UnityEvent onDropDownChange;
47+
private int previousNetworkDropdownIndex;
48+
private List<ChainInfo.Root> chainList;
49+
private int selectedChainIndex;
50+
private int selectedRpcIndex;
51+
52+
#endregion
3253

33-
// Checks if data is already entered
54+
#region Methods
55+
56+
/// <summary>
57+
/// Checks if data is already entered, sets default values if not
58+
/// </summary>
3459
void Awake()
3560
{
61+
// Get saved settings or revert to default
3662
var projectConfig = ProjectConfigUtilities.Load();
37-
3863
projectID = string.IsNullOrEmpty(projectConfig?.ProjectId) ? ProjectIdPrompt : projectConfig.ProjectId;
39-
chainID = string.IsNullOrEmpty(projectConfig?.ChainId) ? ChainIdPrompt : projectConfig.ChainId;
40-
chain = string.IsNullOrEmpty(projectConfig?.Chain) ? ChainPrompt : projectConfig.Chain;
41-
network = string.IsNullOrEmpty(projectConfig?.Network) ? NetworkPrompt : projectConfig.Network;
42-
symbol = string.IsNullOrEmpty(projectConfig?.Symbol) ? SymbolPrompt : projectConfig.Symbol;
43-
rpc = string.IsNullOrEmpty(projectConfig?.Rpc) ? RpcPrompt : projectConfig.Rpc;
64+
chainID = string.IsNullOrEmpty(projectConfig?.ChainId) ? ChainIdDefault : projectConfig.ChainId;
65+
chain = string.IsNullOrEmpty(projectConfig?.Chain) ? ChainDefault : projectConfig.Chain;
66+
network = string.IsNullOrEmpty(projectConfig?.Network) ? NetworkDefault : projectConfig.Network;
67+
symbol = string.IsNullOrEmpty(projectConfig?.Symbol) ? SymbolDefault : projectConfig.Symbol;
68+
rpc = string.IsNullOrEmpty(projectConfig?.Rpc) ? RpcDefault : projectConfig.Rpc;
69+
// Search menu
70+
onDropDownChange = new UnityEvent();
71+
onDropDownChange.AddListener(UpdateServerMenuInfo);
72+
// Fetch supported chains
73+
FetchSupportedChains();
74+
}
75+
76+
/// <summary>
77+
/// Updates the values in the server settings area when an item is selected
78+
/// </summary>
79+
public void UpdateServerMenuInfo()
80+
{
81+
// Get the selected chain index
82+
selectedChainIndex = Array.FindIndex(chainList.ToArray(), x => x.name == chain);
83+
84+
// Check if the selectedChainIndex is valid
85+
if (selectedChainIndex >= 0 && selectedChainIndex < chainList.Count)
86+
{
87+
// Set chain values
88+
network = chainList[selectedChainIndex].chain;
89+
chainID = chainList[selectedChainIndex].chainId.ToString();
90+
symbol = chainList[selectedChainIndex].nativeCurrency.symbol;
91+
// Ensure that the selectedRpcIndex is within bounds
92+
selectedRpcIndex = Mathf.Clamp(selectedRpcIndex, 0, chainList[selectedChainIndex].rpc.Count - 1);
93+
// Set the rpc
94+
rpc = chainList[selectedChainIndex].rpc[selectedRpcIndex];
95+
}
96+
else
97+
{
98+
// Handle the case where the selected chain is not found
99+
Debug.LogError("Selected chain not found in the chainList.");
100+
}
101+
}
102+
103+
/// <summary>
104+
/// Fetches the supported EVM chains list from Chainlist's github json
105+
/// </summary>
106+
private async void FetchSupportedChains()
107+
{
108+
using UnityWebRequest webRequest = UnityWebRequest.Get("https://chainid.network/chains.json");
109+
await EditorUtilities.SendAndWait(webRequest);
110+
if (webRequest.result != UnityWebRequest.Result.Success)
111+
{
112+
Debug.LogError("Error Getting Supported Chains: " + webRequest.error);
113+
return;
114+
}
115+
var json = webRequest.downloadHandler.text;
116+
chainList = JsonConvert.DeserializeObject<List<ChainInfo.Root>>(json);
117+
chainList = chainList.OrderBy(x => x.name).ToList();
44118
}
45119

46120
// Initializes window
@@ -51,17 +125,21 @@ public static void ShowWindow()
51125
GetWindow(typeof(ChainSafeServerSettings));
52126
}
53127

54-
// Called when menu is opened, loads Chainsafe Logo
55-
void OnEnable()
128+
/// <summary>
129+
/// Called when menu is opened, loads Chainsafe Logo
130+
/// </summary>
131+
private void OnEnable()
56132
{
57133
if (!logo)
58134
{
59135
logo = AssetDatabase.LoadAssetAtPath<Texture2D>("Packages/io.chainsafe.web3-unity/Editor/Textures/ChainSafeLogo.png");
60136
}
61137
}
62138

63-
// Displayed content
64-
void OnGUI()
139+
/// <summary>
140+
/// Displayed content
141+
/// </summary>
142+
private void OnGUI()
65143
{
66144
// Image
67145
EditorGUILayout.BeginVertical("box");
@@ -72,14 +150,48 @@ void OnGUI()
72150
GUILayout.Label("Here you can enter all the information needed to get your game started on the blockchain!", EditorStyles.label);
73151
// Inputs
74152
projectID = EditorGUILayout.TextField("Project ID", projectID);
75-
chainID = EditorGUILayout.TextField("Chain ID", chainID);
76-
chain = EditorGUILayout.TextField("Chain", chain);
77-
network = EditorGUILayout.TextField("Network", network);
78-
symbol = EditorGUILayout.TextField("Symbol", symbol);
79-
rpc = EditorGUILayout.TextField("RPC", rpc);
80-
153+
// Search menu
154+
// Null check to stop the recursive loop before the web request has completed
155+
if (chainList == null) return;
156+
// Set string array from chainList to pass into the menu
157+
string[] chainOptions = chainList.Select(x => x.name).ToArray();
158+
// Display the dynamically updating Popup
159+
EditorGUILayout.BeginHorizontal();
160+
EditorGUILayout.PrefixLabel("Select Chain");
161+
// Show the network drop down menu
162+
if (GUILayout.Button(chain, EditorStyles.popup))
163+
{
164+
searchProvider = CreateInstance<StringListSearchProvider>();
165+
searchProvider.Initialize(chainOptions, x => { chain = x;});
166+
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), searchProvider);
167+
}
168+
EditorGUILayout.EndHorizontal();
169+
network = EditorGUILayout.TextField("Network: ",network);
170+
chainID = EditorGUILayout.TextField("Chain ID: ",chainID);
171+
symbol = EditorGUILayout.TextField("Symbol: ",symbol);
172+
EditorGUILayout.BeginHorizontal();
173+
EditorGUILayout.PrefixLabel("Select RPC");
174+
// Remove "https://" so the user doesn't have to click through 2 levels for the rpc options
175+
string[] rpcOptions = chainList[selectedChainIndex].rpc.Where(rpc => rpc.StartsWith("https")).Select(rpc => rpc.Substring(8)).ToArray();
176+
string selectedRpc = rpcOptions[selectedRpcIndex];
177+
// Show the rpc drop down menu
178+
if (GUILayout.Button(selectedRpc, EditorStyles.popup))
179+
{
180+
searchProvider = CreateInstance<StringListSearchProvider>();
181+
searchProvider.Initialize(rpcOptions, x =>
182+
{
183+
selectedRpcIndex = Array.IndexOf(rpcOptions, x);
184+
// Add "https://" back
185+
rpc = "https://" + x;
186+
});
187+
SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), searchProvider);
188+
}
189+
EditorGUILayout.EndHorizontal();
190+
// Allows for a custom rpc
191+
rpc = EditorGUILayout.TextField("RPC: ", rpc);
192+
GUILayout.Label("If you're using a custom RPC it will override the selection above", EditorStyles.boldLabel);
193+
81194
// Buttons
82-
83195
// Register
84196
if (GUILayout.Button("Need To Register?"))
85197
{
@@ -106,7 +218,11 @@ void OnGUI()
106218
}
107219
GUILayout.Label("Reminder: Your ProjectID Must Be Valid To Save & Build With Our SDK. You Can Register For One On Our Website At Dashboard.Gaming.Chainsafe.io", EditorStyles.label);
108220
}
109-
221+
222+
/// <summary>
223+
/// Validates the project ID via ChainSafe's backend & writes values to the network js file, static so it can be called externally
224+
/// </summary>
225+
/// <param name="projectID"></param>
110226
static async void ValidateProjectID(string projectID)
111227
{
112228
try
@@ -124,8 +240,11 @@ static async void ValidateProjectID(string projectID)
124240
Debug.LogException(e);
125241
}
126242
}
127-
128-
internal static async Task<bool> ValidateProjectIDAsync(string projectID)
243+
244+
/// <summary>
245+
/// Validates the project ID via ChainSafe's backend
246+
/// </summary>
247+
private static async Task<bool> ValidateProjectIDAsync(string projectID)
129248
{
130249
var form = new WWWForm();
131250
form.AddField("projectId", projectID);
@@ -153,7 +272,10 @@ internal static async Task<bool> ValidateProjectIDAsync(string projectID)
153272
Debug.LogError(dbgProjectIDMessage);
154273
return false;
155274
}
156-
275+
276+
/// <summary>
277+
/// Writes values to the network js file
278+
/// </summary>
157279
public static void WriteNetworkFile()
158280
{
159281
Debug.Log("Updating network.js...");
@@ -207,4 +329,6 @@ private class ValidateProjectIDResponse
207329
[JsonProperty("response")]
208330
public bool Response { get; set; }
209331
}
210-
}
332+
333+
#endregion
334+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using UnityEditor;
5+
using UnityEditor.Experimental.GraphView;
6+
using UnityEngine;
7+
8+
/// <summary>
9+
/// Search menu functionality
10+
/// </summary>
11+
public class StringListSearchProvider : ScriptableObject, ISearchWindowProvider
12+
{
13+
#region Fields
14+
15+
private string[] listItems;
16+
private Action<string> onSetIndexCallback;
17+
18+
#endregion
19+
20+
#region Methods
21+
22+
/// <summary>
23+
/// Initializes list items and sets the callback for when items are selected
24+
/// </summary>
25+
/// <param name="list"></param>
26+
/// <param name="callback"></param>
27+
public void Initialize(string[] list, Action<string> callback)
28+
{
29+
listItems = list;
30+
onSetIndexCallback = callback;
31+
}
32+
33+
/// <summary>
34+
/// Creates, formats and sorts the search list tree
35+
/// </summary>
36+
/// <param name="context"></param>
37+
/// <returns></returns>
38+
public List<SearchTreeEntry> CreateSearchTree(SearchWindowContext context)
39+
{
40+
List<SearchTreeEntry> searchList = new List<SearchTreeEntry>();
41+
searchList.Add(new SearchTreeGroupEntry(new GUIContent("Select Option"), 0));
42+
List<string> sortedSearchList = listItems.ToList();
43+
sortedSearchList.Sort((a, b) =>
44+
{
45+
string[] splits1 = a.Split('/');
46+
string[] splits2 = b.Split('/');
47+
for (int i = 0; i < splits2.Length; i++)
48+
{
49+
if (i >= splits2.Length)
50+
{
51+
return 1;
52+
}
53+
54+
int value = splits1[i].CompareTo(splits2[i]);
55+
if (value != 0)
56+
{
57+
if (splits1.Length != splits2.Length && (i == splits1.Length || i == splits2.Length - 1))
58+
return splits1.Length < splits2.Length ? 1 : -1;
59+
return value;
60+
}
61+
}
62+
63+
return 0;
64+
});
65+
List<string> groups = new List<string>();
66+
foreach (var item in sortedSearchList)
67+
{
68+
string[] entryTitle = item.Split('/');
69+
string groupName = "";
70+
for (int i = 0; i < entryTitle.Length - 1; i++)
71+
{
72+
groupName += entryTitle[i];
73+
if (!groups.Contains(groupName))
74+
{
75+
searchList.Add(new SearchTreeGroupEntry(new GUIContent(entryTitle[i]), i + 1));
76+
groups.Add(groupName);
77+
}
78+
groupName += "/";
79+
}
80+
SearchTreeEntry entry = new SearchTreeEntry(new GUIContent(entryTitle.Last()));
81+
entry.level = entryTitle.Length;
82+
entry.userData = entryTitle.Last();
83+
searchList.Add(entry);
84+
}
85+
return searchList;
86+
}
87+
88+
/// <summary>
89+
/// Fires when an item is selected, also invokes the onDropDownChange event from Chainsafe server settings to save data
90+
/// </summary>
91+
/// <param name="SearchTreeEntry"></param>
92+
/// <param name="context"></param>
93+
/// <returns></returns>
94+
public bool OnSelectEntry(SearchTreeEntry SearchTreeEntry, SearchWindowContext context)
95+
{
96+
onSetIndexCallback?.Invoke((string)SearchTreeEntry.userData);
97+
ChainSafeServerSettings instance = EditorWindow.GetWindow<ChainSafeServerSettings>();
98+
instance.UpdateServerMenuInfo();
99+
return true;
100+
}
101+
102+
#endregion
103+
}

Packages/io.chainsafe.web3-unity/Editor/StringListSearchProvider.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)