Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ unity/**/*.pidb
unity/**/*.booproj
unity/**/*.svd
unity/**/TestResults.xml
unity/**/PlayModeTestResults.xml
unity/**/EditModeTestResults*.xml
unity/**/PlayModeTestResults*.xml
unity/**/*.log
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ unity/MagicExamHall

조작은 WASD/방향키 이동, 우클릭 hold/release 월드 드로잉입니다. 기본 플레이에서는 별도 입력 패널이나 `마법 시전` 버튼을 사용하지 않습니다.

제출 후보를 만들 때는 [Release Checklist](docs/RELEASE_CHECKLIST.md)를 따라 Web 검증, Unity 테스트, Windows 빌드, player smoke, 수동 5층 완주, 로그/개인정보 확인을 함께 점검합니다.

## 초기 검증 튜토리얼

처음 받았을 때는 아래 순서로 확인하면 됩니다.
Expand Down
78 changes: 78 additions & 0 deletions docs/RELEASE_CHECKLIST.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Release Checklist

Magic Recognizer V1.5 / Magic Exam Hall 제출 후보를 만들기 전에 이 문서를 위에서 아래로 실행한다.

관련 이슈: #13, #16, #18, #52

## 1. Web 검증

- [ ] `npm ci`
- [ ] `npm run validate:docs`
- [ ] `npm test`
- [ ] `npm run build`
- [ ] 브라우저에서 Vite dev server를 열고 base family, overlay, final seal compile 흐름을 짧게 확인한다.
- [ ] survey API나 export를 켠 경우, 테스트 데이터와 실제 제출용 데이터가 섞이지 않았는지 확인한다.

## 2. Unity 자동 검증

PowerShell에서 저장소 루트 기준으로 실행한다.

```powershell
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -quit -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -executeMethod MagicExamHall.Editor.MagicExamHallSceneBuilder.BuildAll
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -runTests -testPlatform editmode -testResults 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\EditModeTestResults.xml'
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -runTests -testPlatform playmode -testResults 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\PlayModeTestResults.xml'
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -quit -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -executeMethod MagicExamHall.Editor.MagicExamHallBuildPipeline.BuildWindowsPlayer -magicExamHallBuildPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\Builds\MagicExamHall.exe' -logFile 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\unity-build.log'
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-magic-exam-hall-player.ps1 -BuildPath 'unity/MagicExamHall/Builds/MagicExamHall.exe' -LogPath 'unity/MagicExamHall/player-smoke.log'
```

- [ ] EditMode 결과가 `Passed`, failed `0`이다.
- [ ] PlayMode 결과가 `Passed`, failed `0`이다.
- [ ] Windows player build log에 `Build Finished, Result: Success.`가 있다.
- [ ] Windows player build log에 `0 errors`가 있다.
- [ ] player smoke log에 `Initialize engine version`과 `UnloadTime`이 있다.
- [ ] player smoke script가 early exit나 fatal startup pattern 없이 통과한다.

## 3. Unity 수동 5층 완주

Unity Editor Play 또는 생성된 `unity/MagicExamHall/Builds/MagicExamHall.exe`로 확인한다.

- [ ] WASD 또는 방향키로 이동할 수 있다.
- [ ] 우클릭 hold로 바닥에 stroke가 보이고, release 후 주문이 판정된다.
- [ ] 1층에서 base family 실험과 목표 완료가 가능하다.
- [ ] 2층에서 overlay operator 실험과 목표 완료가 가능하다.
- [ ] 3층에서 bridge/flow 계열 반응이 읽힌다.
- [ ] 4층에서 hazard reset과 stabilizer 반응이 읽힌다.
- [ ] 5층에서 final seal 목표를 완료할 수 있다.
- [ ] 엔딩 리포트가 표시된다.
- [ ] 플레이 중 콘솔이나 player log에 fatal exception이 없다.

## 4. 로그와 개인정보

Unity 로그 경로:

```text
Application.persistentDataPath/MagicExamHallLogs/<sessionId>/
```

- [ ] `attempts.jsonl`과 `attempts.csv`가 생성된다.
- [ ] `survey.jsonl`과 `survey.csv`가 생성된다.
- [ ] 로그에 참가자 실명, 연락처, 학번 같은 직접 식별 정보가 들어가지 않는다.
- [ ] 연구용으로 공유할 로그는 session id를 익명화한다.
- [ ] 테스트 실행으로 생긴 임시 로그와 실제 플레이테스트 로그를 구분한다.
- [ ] 개인정보와 연구 데이터 처리 기준은 #16 범위 문서와 일치한다.

## 5. 제출 패키지

- [ ] 루트 `README.md`의 빠른 시작과 Unity 실행 안내가 최신이다.
- [ ] `unity/MagicExamHall/README.md`의 Unity 버전, 조작법, 검증 명령이 최신이다.
- [ ] `docs/PROJECT_ROADMAP.md`가 현재 구현 상태와 크게 어긋나지 않는다.
- [ ] Windows build 산출물을 새로 생성했다.
- [ ] 제출물에 포함할 build, README, 발표 자료, playtest notes의 버전을 기록했다.
- [ ] known issue가 있으면 PR, issue, 또는 발표 자료에 명시했다.

## Known Benign Messages

- Unity batchmode 종료 중 `abort_threads: Failed aborting id ... mono_thread_manage will ignore it`가 보일 수 있다.
- Unity licensing에서 access token 갱신 경고가 보일 수 있다.

이 메시지는 같은 로그에 compile error, failed test, build failure, fatal exception이 없을 때만 benign으로 취급한다.
65 changes: 65 additions & 0 deletions scripts/smoke-magic-exam-hall-player.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
param(
[string]$BuildPath = "unity/MagicExamHall/Builds/MagicExamHall.exe",
[string]$LogPath = "unity/MagicExamHall/player-smoke.log",
[int]$TimeoutSeconds = 8
)

$ErrorActionPreference = "Stop"

$resolvedBuildPath = Resolve-Path -LiteralPath $BuildPath
$absoluteLogPath = [System.IO.Path]::GetFullPath($LogPath)
$absoluteLogDirectory = Split-Path -Parent $absoluteLogPath

if ($TimeoutSeconds -lt 3) {
throw "TimeoutSeconds must be at least 3 so the player has time to load the scene."
}

if (-not (Test-Path -LiteralPath $absoluteLogDirectory)) {
New-Item -ItemType Directory -Path $absoluteLogDirectory | Out-Null
}

if (Test-Path -LiteralPath $absoluteLogPath) {
Remove-Item -LiteralPath $absoluteLogPath -Force
}

$arguments = @("-batchmode", "-nographics", "-logFile", $absoluteLogPath)
$process = Start-Process -FilePath $resolvedBuildPath.Path -ArgumentList $arguments -PassThru -WindowStyle Hidden

try {
$deadline = (Get-Date).AddSeconds($TimeoutSeconds)
while ((Get-Date) -lt $deadline) {
Start-Sleep -Milliseconds 250
if ($process.HasExited) {
throw "Magic Exam Hall player exited during smoke test with exit code $($process.ExitCode)."
}
}
}
finally {
if (-not $process.HasExited) {
Stop-Process -Id $process.Id -Force
$process.WaitForExit()
}
}

if (-not (Test-Path -LiteralPath $absoluteLogPath)) {
throw "Magic Exam Hall player did not create a smoke log at $absoluteLogPath."
}

$logText = Get-Content -LiteralPath $absoluteLogPath -Raw
$requiredPatterns = @(
"Initialize engine version",
"UnloadTime"
)

foreach ($pattern in $requiredPatterns) {
if ($logText -notmatch [regex]::Escape($pattern)) {
throw "Magic Exam Hall player smoke log is missing expected startup marker: $pattern"
}
}

$fatalPattern = "(?i)(NullReferenceException|MissingMethodException|DllNotFoundException|Fatal|Crash|Could not load scene|Failed to load)"
if ($logText -match $fatalPattern) {
throw "Magic Exam Hall player smoke log contains a fatal startup pattern: $($Matches[0])"
}

Write-Output "Magic Exam Hall player smoke passed: process stayed alive for $TimeoutSeconds seconds and startup log markers were present."
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.IO;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;

namespace MagicExamHall.Editor
{
public static class MagicExamHallBuildPipeline
{
private const string ScenePath = "Assets/Scenes/MagicExamHall.unity";
private const string DefaultBuildPath = "Builds/MagicExamHall.exe";
private const string BuildPathArgument = "-magicExamHallBuildPath";

[MenuItem("Magic Exam Hall/Build Windows Player")]
public static void BuildWindowsPlayer()
{
BuildWindowsPlayer(ResolveBuildPath(Environment.GetCommandLineArgs()));
}

public static BuildReport BuildWindowsPlayer(string outputPath)
{
if (string.IsNullOrWhiteSpace(outputPath))
{
throw new ArgumentException("A Windows player output path is required.", nameof(outputPath));
}

EnsureSceneRegistered();

var absoluteOutputPath = Path.GetFullPath(outputPath);
var outputDirectory = Path.GetDirectoryName(absoluteOutputPath);
if (!string.IsNullOrEmpty(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}

Debug.Log($"Building Magic Exam Hall Windows player at {absoluteOutputPath}");

var report = BuildPipeline.BuildPlayer(new BuildPlayerOptions
{
scenes = new[] { ScenePath },
locationPathName = absoluteOutputPath,
target = BuildTarget.StandaloneWindows64,
options = BuildOptions.None
});

var summary = report.summary;
Debug.Log(
$"Magic Exam Hall Windows player build finished with {summary.result}: {summary.totalSize} bytes, " +
$"{summary.totalErrors} errors, {summary.totalWarnings} warnings.");

if (summary.result != BuildResult.Succeeded)
{
throw new InvalidOperationException(
$"Magic Exam Hall Windows player build failed with {summary.result}: " +
$"{summary.totalErrors} errors, {summary.totalWarnings} warnings.");
}

return report;
}

internal static string ResolveBuildPath(string[] args)
{
if (args == null)
{
return DefaultBuildPath;
}

for (var i = 0; i < args.Length; i++)
{
var arg = args[i];
if (string.Equals(arg, BuildPathArgument, StringComparison.OrdinalIgnoreCase))
{
if (i + 1 >= args.Length || string.IsNullOrWhiteSpace(args[i + 1]))
{
throw new ArgumentException($"{BuildPathArgument} requires a non-empty output path.");
}

return args[i + 1];
}

var inlinePrefix = BuildPathArgument + "=";
if (arg.StartsWith(inlinePrefix, StringComparison.OrdinalIgnoreCase))
{
var inlinePath = arg.Substring(inlinePrefix.Length);
if (string.IsNullOrWhiteSpace(inlinePath))
{
throw new ArgumentException($"{BuildPathArgument} requires a non-empty output path.");
}

return inlinePath;
}
}

return DefaultBuildPath;
}

private static void EnsureSceneRegistered()
{
var scene = AssetDatabase.LoadAssetAtPath<SceneAsset>(ScenePath);
if (scene == null)
{
throw new FileNotFoundException($"Could not find Magic Exam Hall scene at {ScenePath}.", ScenePath);
}

EditorBuildSettings.scenes = new[] { new EditorBuildSettingsScene(ScenePath, true) };
AssetDatabase.SaveAssets();
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions unity/MagicExamHall/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ The scene contains a top-down exam tower room, player, world-space casting input
Magic Exam Hall/Rebuild Demo Scene
```

To create a Windows player from the editor, use:

```text
Magic Exam Hall/Build Windows Player
```

The default output is `unity/MagicExamHall/Builds/MagicExamHall.exe`.

## Controls

- Move: WASD or arrow keys
Expand Down Expand Up @@ -63,10 +71,14 @@ EditMode tests cover base recognition, overlay recognition, `martial_axis` depen

```powershell
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -quit -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -executeMethod MagicExamHall.Editor.MagicExamHallSceneBuilder.BuildAll
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -quit -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -executeMethod MagicExamHall.Editor.MagicExamHallBuildPipeline.BuildWindowsPlayer -magicExamHallBuildPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\Builds\MagicExamHall.exe' -logFile 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\unity-build.log'
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\smoke-magic-exam-hall-player.ps1 -BuildPath 'unity/MagicExamHall/Builds/MagicExamHall.exe' -LogPath 'unity/MagicExamHall/player-smoke.log'
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -runTests -testPlatform editmode -testResults 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\TestResults.xml'
& 'C:\Program Files\Unity\Hub\Editor\6000.3.14f1\Editor\Unity.com' -batchmode -projectPath 'C:\Users\silve\source\repos\magic\unity\MagicExamHall' -runTests -testPlatform playmode -testResults 'C:\Users\silve\source\repos\magic\unity\MagicExamHall\PlayModeTestResults.xml'
```

The player smoke script starts the generated Windows build headlessly, waits for the scene startup log markers, and fails if the player exits early or logs a fatal startup pattern. Manual release QA should still confirm WASD movement and right-mouse world drawing in the visible player.

## Troubleshooting

If batchmode exits before compile/test output and the log contains `No valid Unity Editor license found`, activate the Unity Editor license first and rerun the command.
Expand Down
Loading