Skip to content

Abstractions

The public contract. If a type here changes shape, somebody's course stops compiling — so this is the folder to be slowest in.

The interfaces

ILmsRuntime is what content holds. It exposes the data model (Cmi), the session lifecycle (CommitAsync, FinishAsync), and LastError.

IScormDataModel is the data model itself: status, score, lesson location, suspend data, session time, and the launch context the LMS supplied. The SCORM 1.2 and 2004 runtimes both implement it, and the per-standard differences are absorbed behind it rather than leaking to the caller.

IScormObjectives and IScormInteractions are separate because not every runtime supports them and not every course uses them. Reach them through the extension methods in LmsObjectivesExtensions and LmsInteractionExtensions, which handle the "this runtime does not do objectives" case for you.

IScormDataModel also carries GetElement / SetElement, a raw accessor for elements it does not wrap. It is an escape hatch, not the main road: the typed members spell the element correctly for the running standard, parse culture-invariantly, and keep the two 2004 status axes apart, and the raw pair does none of that. It exists so an element nobody anticipated — a 2004-only one like cmi.progress_measure, or cmi.comments_from_lms — does not leave a course stuck waiting for a package release.

It still goes through the session guard and reports a refusal with the decoded error, so it is an escape hatch rather than a hole.

ILmsBridge is the seam the WebGL bridge and the test fakes both sit behind. It is the narrowest interface here on purpose — six calls, Init, Commit, Finish, GetString, SetString, GetLastError — because strings in and strings out is all a browser bridge can actually carry. Everything typed happens above it.

The enums, and why their order matters

LmsTypes.cs holds LessonStatus, LessonMode, ExitMode, LmsVersion, LmsError, SuccessStatus, and CompletionStatus.

LessonStatus.Unknown and LessonMode.Normal are deliberately zero. A Unity-serialized field that was never assigned reads as zero, and so does a default-constructed struct — so whatever sits at zero is what an un-set field silently claims. Passed and Browse used to hold those slots, which meant an unconfigured field asserted a pass and a browse-mode session. The safest reading of "nobody said" is "nobody knows", so that is what zero means now.

PublicSurfaceTests pins both orderings, along with the list of public types, so reordering them fails a test rather than a customer's build.