-
Notifications
You must be signed in to change notification settings - Fork 329
FIX: ISXB-1674 - Input actions asset not converted correctly when upgrading from 1.14.1 (Regression) #2244
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
FIX: ISXB-1674 - Input actions asset not converted correctly when upgrading from 1.14.1 (Regression) #2244
Changes from 2 commits
a260324
00b37a5
d637adf
4e76813
a8ba533
ff05b1d
829c4aa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -108,5 +108,31 @@ | |
|
||
yield return null; | ||
} | ||
|
||
[UnityTest] | ||
public IEnumerator Migration_FromLegacyJson_ShouldConvertOrdinal_KeepInvertVector2_AndSeparators() | ||
{ | ||
var legacyJson = m_Asset.ToJson().Replace("\"version\": 1", "\"version\": 0").Replace("Custom(SomeEnum=10)", "Custom(SomeEnum=1)"); | ||
|
||
// Add a trailing processor to verify the semicolon separator is preserved. | ||
if (!legacyJson.Contains(";InvertVector2(invertX=true)")) | ||
legacyJson = legacyJson.Replace("Custom(SomeEnum=1)\"", "Custom(SomeEnum=1);InvertVector2(invertX=true)\""); | ||
|
||
var migratedAsset = InputActionAsset.FromJson(legacyJson); | ||
Assume.That(migratedAsset, Is.Not.Null, "Failed to load legacy JSON into an InputActionAsset."); | ||
|
||
var migratedJson = migratedAsset.ToJson(); | ||
Assume.That(migratedJson, Is.Not.Null.And.Not.Empty, "Migrated JSON was empty."); | ||
|
||
Assert.Less(migratedJson.IndexOf("InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf(",Custom(SomeEnum=20)", StringComparison.Ordinal), | ||
"Expected a comma between the first and second processors, with InvertVector2 first." | ||
); | ||
|
||
Assert.Greater(migratedJson.IndexOf(";InvertVector2(invertX=true)", StringComparison.Ordinal), migratedJson.IndexOf("Custom(SomeEnum=20)", StringComparison.Ordinal), | ||
|
||
"Expected a semicolon between the second and third processors, with the trailing InvertVector2 last." | ||
); | ||
|
||
yield return null; | ||
} | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,7 @@ | |
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Reflection; | ||
using UnityEngine.InputSystem.Editor; | ||
using System.Globalization; | ||
using UnityEngine.InputSystem.Utilities; | ||
|
||
////TODO: make the FindAction logic available on any IEnumerable<InputAction> and IInputActionCollection via extension methods | ||
|
@@ -1014,60 +1014,125 @@ | |
{ | ||
if (parsedJson.version >= JsonVersion.Version1) | ||
return; | ||
if ((parsedJson.maps?.Length ?? 0) > 0 && (parsedJson.version) < JsonVersion.Version1) | ||
|
||
|
||
if (parsedJson.maps == null || parsedJson.maps.Length == 0) | ||
{ | ||
parsedJson.version = JsonVersion.Version1; | ||
return; | ||
} | ||
|
||
for (var mi = 0; mi < parsedJson.maps.Length; ++mi) | ||
{ | ||
for (var mi = 0; mi < parsedJson.maps.Length; ++mi) | ||
var mapJson = parsedJson.maps[mi]; | ||
if (mapJson.actions == null || mapJson.actions.Length == 0) | ||
continue; | ||
|
||
for (var ai = 0; ai < mapJson.actions.Length; ++ai) | ||
|
||
{ | ||
var mapJson = parsedJson.maps[mi]; | ||
for (var ai = 0; ai < mapJson.actions.Length; ++ai) | ||
var actionJson = mapJson.actions[ai]; | ||
var raw = actionJson.processors; | ||
if (string.IsNullOrEmpty(raw)) | ||
continue; | ||
|
||
var parts = System.Text.RegularExpressions.Regex.Split(raw, @"\s*([,;])\s*"); | ||
if (parts.Length == 0) | ||
continue; | ||
|
||
var tokens = new List<string>(); | ||
|
||
for (int i = 0; i < parts.Length; i += 2) | ||
if (!string.IsNullOrEmpty(parts[i])) | ||
tokens.Add(parts[i]); | ||
|
||
if (tokens.Count == 0) | ||
continue; | ||
|
||
var parsed = new List<NameAndParameters>(tokens.Count); | ||
|
||
foreach (var t in tokens) | ||
parsed.Add(NameAndParameters.Parse(t)); | ||
|
||
var rebuiltTokens = new List<string>(tokens.Count); | ||
|
||
var anyProcessorChanged = false; | ||
|
||
for (int pi = 0; pi < parsed.Count; pi++) | ||
{ | ||
var actionJson = mapJson.actions[ai]; | ||
var raw = actionJson.processors; | ||
if (string.IsNullOrEmpty(raw)) | ||
var nap = parsed[pi]; | ||
|
||
var procType = InputSystem.TryGetProcessor(nap.name); | ||
if (procType == null || nap.parameters.Count == 0) | ||
{ | ||
rebuiltTokens.Add(tokens[pi]); | ||
Check warning on line 1063 in Packages/com.unity.inputsystem/InputSystem/Actions/InputActionAsset.cs
|
||
continue; | ||
} | ||
|
||
var dict = new Dictionary<string, string>(nap.parameters.Count, System.StringComparer.OrdinalIgnoreCase); | ||
|
||
foreach (var p in nap.parameters) | ||
dict[p.name] = p.value.ToString(); | ||
|
||
var changedThisProcessor = false; | ||
|
||
var list = NameAndParameters.ParseMultiple(raw).ToList(); | ||
var rebuilt = new List<string>(list.Count); | ||
foreach (var nap in list) | ||
foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance)) | ||
{ | ||
var procType = InputSystem.TryGetProcessor(nap.name); | ||
if (nap.parameters.Count == 0 || procType == null) | ||
{ | ||
rebuilt.Add(nap.ToString()); | ||
if (!field.FieldType.IsEnum) | ||
continue; | ||
|
||
if (!dict.TryGetValue(field.Name, out var rawVal)) | ||
continue; | ||
} | ||
|
||
var dict = nap.parameters.ToDictionary(p => p.name, p => p.value.ToString()); | ||
var anyChanged = false; | ||
foreach (var field in procType.GetFields(BindingFlags.Public | BindingFlags.Instance).Where(f => f.FieldType.IsEnum)) | ||
if (int.TryParse(rawVal, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n)) | ||
|
||
{ | ||
if (dict.TryGetValue(field.Name, out var ordS) && int.TryParse(ordS, out var ord)) | ||
var values = System.Enum.GetValues(field.FieldType); | ||
var looksLikeOrdinal = n >= 0 && n < values.Length && !System.Enum.IsDefined(field.FieldType, n); | ||
if (looksLikeOrdinal) | ||
{ | ||
var values = Enum.GetValues(field.FieldType).Cast<object>().ToArray(); | ||
if (ord >= 0 && ord < values.Length) | ||
var underlying = Convert.ToInt32(values.GetValue(n)); | ||
if (underlying != n) | ||
{ | ||
dict[field.Name] = Convert.ToInt32(values[ord]).ToString(); | ||
anyChanged = true; | ||
dict[field.Name] = underlying.ToString(CultureInfo.InvariantCulture); | ||
changedThisProcessor = true; | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!changedThisProcessor) | ||
|
||
{ | ||
rebuiltTokens.Add(tokens[pi]); | ||
} | ||
else | ||
{ | ||
var ordered = nap.parameters.Select(p => | ||
{ | ||
var v = dict.TryGetValue(p.name, out var nv) ? nv : p.value.ToString(); | ||
return $"{p.name}={v}"; | ||
}); | ||
|
||
var migrated = $"{nap.name}({string.Join(",", ordered)})"; | ||
rebuiltTokens.Add(migrated); | ||
anyProcessorChanged = true; | ||
} | ||
} | ||
|
||
if (!anyChanged) | ||
if (anyProcessorChanged) | ||
{ | ||
var sb = new System.Text.StringBuilder(raw.Length + 16); | ||
int tokenIndex = 0; | ||
for (int partIndex = 0; partIndex < parts.Length; ++partIndex) | ||
{ | ||
if ((partIndex % 2) == 0) | ||
{ | ||
rebuilt.Add(nap.ToString()); | ||
if (tokenIndex < rebuiltTokens.Count) | ||
sb.Append(rebuiltTokens[tokenIndex++]); | ||
} | ||
else | ||
{ | ||
var paramText = string.Join(",", dict.Select(kv => $"{kv.Key}={kv.Value}")); | ||
rebuilt.Add($"{nap.name}({paramText})"); | ||
sb.Append(parts[partIndex]); | ||
} | ||
} | ||
|
||
actionJson.processors = string.Join(";", rebuilt); | ||
mapJson.actions[ai] = actionJson; | ||
actionJson.processors = sb.ToString(); | ||
} | ||
parsedJson.maps[mi] = mapJson; | ||
mapJson.actions[ai] = actionJson; | ||
} | ||
parsedJson.maps[mi] = mapJson; | ||
} | ||
// Bump the version so we never re-migrate | ||
parsedJson.version = JsonVersion.Version1; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fact we have an conditional treatment on our test data before we act on it is not a good sign, ideally the data we use for testing is known ahead of time and we can just apply the function under test on it.
Whenever we need to bump the current version to 2 the replace above will fail, which is not what the test is trying to test.
I am wondering if a better approach here would be to just have a static json string here instead.