Initial commit: AIGame Unity client with URP, HybridCLR, YooAsset and tooling.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
udx
2026-09-21 14:21:07 +08:00
co-authored by Cursor
commit f3f66ab063
931 changed files with 112366 additions and 0 deletions
@@ -0,0 +1,83 @@
using System;
using System.Collections.Generic;
using NUnit.Framework;
using Unity.Pipeline.Compilation;
namespace Unity.Pipeline.Tests.Runtime
{
/// <summary>
/// Compact smoke test targeting the interpreter's AOT-hazard surface. In the editor / a Mono
/// player it passes trivially; its <b>purpose is to fail loudly when run in an IL2CPP standalone
/// player</b> if an AOT regression (linker stripping, <c>MakeGenericMethod</c>, generic virtual
/// dispatch, or the <c>AllowTypeStruct&lt;T&gt;</c> boxed-struct fast path) creeps in.
///
/// The probe scripts are pre-compiled on desktop and embedded as IL blobs
/// (<see cref="IlInterpreterAotProbeBlobs"/>, generated by IlInterpreter/tests/IlInterpreter.ProbeGen) —
/// Roslyn cannot compile inside an IL2CPP player (proven by CI job 73639037), and the faithful
/// push_reload scenario ships compiled bytes anyway; the interpreter itself needs no Roslyn.
///
/// Categorised "Il2Cpp" so the CI player-build job can target it (see .yamato/il2cpp-playmode.yml).
/// </summary>
[Category("Il2Cpp")]
class IlInterpreterAotSmokeTests
{
static IlInterpreter.Interpreter.ScriptInterpreter Load(string base64)
{
// CreateStandard registers AllowTypeStruct<Vector3>() — the boxed-struct fast path that
// is the primary IL2CPP hazard this test guards. It's the same builder the eval and
// code-reload sinks run in the real player.
var binding = IlInterpreterHostBindings.CreateStandard();
var interp = new IlInterpreter.Interpreter.ScriptInterpreter(binding);
interp.Load(new RawScript(Convert.FromBase64String(base64)));
return interp;
}
[Test]
public void AllowTypeStruct_Vector3_FlatFrame_RoundTrips()
{
using var interp = Load(IlInterpreterAotProbeBlobs.HazardProbe);
Assert.AreEqual(6f, (float)interp.Invoke("VecSum"), 1e-4f);
}
[Test]
public void GenericTypedInvoke_TwoArgs()
{
using var interp = Load(IlInterpreterAotProbeBlobs.HazardProbe);
// Invoke<int,int> exercises the generic typed-arg path (MakeGenericMethod under IL2CPP).
Assert.AreEqual(42, interp.Invoke<int, int>("Add", 19, 23));
}
// A representative slice of the desktop differential-oracle corpus, replayed under IL2CPP.
// Sources and expected values live in the generator, which re-verifies both (real-C# oracle
// + interpreter) at generation time; this closes the "desktop interpreter == IL2CPP
// interpreter" link for more than the two hazard paths above.
static IEnumerable<TestCaseData> IntCorpusCases()
{
foreach (var c in IlInterpreterAotProbeBlobs.IntCorpus)
yield return new TestCaseData(c.Base64, c.Expected).SetName(
nameof(Corpus_Int_MatchesDesktop) + "_" + c.Name);
}
[TestCaseSource(nameof(IntCorpusCases))]
public void Corpus_Int_MatchesDesktop(string base64, int expected)
{
using var interp = Load(base64);
Assert.AreEqual(expected, (int)interp.Invoke("Run"));
}
[Test]
public void Corpus_Float_MatchesDesktop()
{
using var interp = Load(IlInterpreterAotProbeBlobs.FloatProbe);
Assert.AreEqual(3.5f, (float)interp.Invoke("Run"), 1e-4f);
}
sealed class RawScript : IlInterpreter.IScript
{
public RawScript(byte[] bytes) { Il = bytes; }
public string Name => "IlInterpreterAotProbe";
public ReadOnlyMemory<byte> Il { get; }
}
}
}