Close phase 3 package bridge and clarify wiring posture

This commit is contained in:
axiomlogicnexus 2026-06-11 18:34:19 +00:00
parent f529e8c9e9
commit 4b1f22c13e
10 changed files with 222 additions and 4 deletions

View file

@ -56,6 +56,7 @@ AHyperTwistClassicCubeGameMode::AHyperTwistClassicCubeGameMode()
PrimaryActorTick.bCanEverTick = true;
PlayerControllerClass = AHyperTwistClassicCubePlayerController::StaticClass();
DefaultPawnClass = AHyperTwistClassicCubeOrbitPawn::StaticClass();
CubeSpawnLocation = FVector(0.0f, 0.0f, 120.0f);
}
void AHyperTwistClassicCubeGameMode::BeginPlay()

View file

@ -1,8 +1,11 @@
#include "HyperTwistSimulation/HyperTwistClassicCubeOrbitPawn.h"
#include "Camera/CameraComponent.h"
#include "EngineUtils.h"
#include "GameFramework/PlayerController.h"
#include "GameFramework/SpringArmComponent.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeActor.h"
#include "HyperTwistSimulation/HyperTwistClassicCubeGameMode.h"
#include "InputCoreTypes.h"
#include "Components/SceneComponent.h"
@ -31,6 +34,7 @@ void AHyperTwistClassicCubeOrbitPawn::BeginPlay()
CurrentYawDegrees = InitialYawDegrees;
CurrentPitchDegrees = InitialPitchDegrees;
RefreshOrbitFocusPointFromClassicCube();
if (SpringArm != nullptr)
{
SpringArm->TargetArmLength = FMath::Clamp(InitialArmLength, MinimumArmLength, MaximumArmLength);
@ -43,6 +47,13 @@ void AHyperTwistClassicCubeOrbitPawn::Tick(const float DeltaSeconds)
Super::Tick(DeltaSeconds);
static_cast<void>(DeltaSeconds);
const FVector PreviousFocusPoint = OrbitFocusPoint;
RefreshOrbitFocusPointFromClassicCube();
if (!OrbitFocusPoint.Equals(PreviousFocusPoint))
{
ApplyOrbitTransform();
}
APlayerController* PlayerController = Cast<APlayerController>(GetController());
if (PlayerController == nullptr)
{
@ -89,6 +100,43 @@ void AHyperTwistClassicCubeOrbitPawn::ApplyOrbitTransform()
}
}
void AHyperTwistClassicCubeOrbitPawn::RefreshOrbitFocusPointFromClassicCube()
{
if (!bFollowActiveCubeActor)
{
return;
}
if (const AHyperTwistClassicCubeActor* CubeActor = ResolveFocusCubeActor())
{
OrbitFocusPoint = CubeActor->GetActorLocation();
}
}
AHyperTwistClassicCubeActor* AHyperTwistClassicCubeOrbitPawn::ResolveFocusCubeActor() const
{
if (GetWorld() == nullptr)
{
return nullptr;
}
if (const AHyperTwistClassicCubeGameMode* GameMode =
Cast<AHyperTwistClassicCubeGameMode>(GetWorld()->GetAuthGameMode()))
{
if (GameMode->ActiveCubeActor != nullptr)
{
return GameMode->ActiveCubeActor;
}
}
for (TActorIterator<AHyperTwistClassicCubeActor> ActorIt(GetWorld()); ActorIt; ++ActorIt)
{
return *ActorIt;
}
return nullptr;
}
bool AHyperTwistClassicCubeOrbitPawn::ShouldOrbitFromMouseInput() const
{
const APlayerController* PlayerController = Cast<APlayerController>(GetController());

View file

@ -4,6 +4,7 @@
#include "GameFramework/Pawn.h"
#include "HyperTwistClassicCubeOrbitPawn.generated.h"
class AHyperTwistClassicCubeActor;
class UCameraComponent;
class USceneComponent;
class USpringArmComponent;
@ -22,6 +23,9 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Camera")
FVector OrbitFocusPoint = FVector::ZeroVector;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Camera")
bool bFollowActiveCubeActor = true;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist|ClassicCube|Camera")
float InitialArmLength = 360.0f;
@ -69,6 +73,8 @@ public:
protected:
void ApplyOrbitTransform();
void RefreshOrbitFocusPointFromClassicCube();
AHyperTwistClassicCubeActor* ResolveFocusCubeActor() const;
bool ShouldOrbitFromMouseInput() const;
float CurrentYawDegrees = 0.0f;

View file

@ -35,6 +35,10 @@ bool FHyperTwistClassicCubeGameModeDefaultsTest::RunTest(const FString& Paramete
);
TestTrue(TEXT("The classic cube game mode must still auto-create its HUD by default."), GameMode->bAutoCreateHud);
TestTrue(TEXT("The classic cube game mode must still auto-spawn the cube actor by default."), GameMode->bAutoSpawnCubeActor);
TestTrue(
TEXT("The classic cube game mode must keep its default cube spawn above the generic validation-map floor."),
GameMode->CubeSpawnLocation.Z > 0.0f
);
return true;
}
@ -85,6 +89,10 @@ bool FHyperTwistClassicCubeOrbitPawnDefaultsTest::RunTest(const FString& Paramet
OrbitPawn->MinimumArmLength < OrbitPawn->InitialArmLength
&& OrbitPawn->InitialArmLength < OrbitPawn->MaximumArmLength
);
TestTrue(
TEXT("The orbit pawn must follow the active cube actor by default."),
OrbitPawn->bFollowActiveCubeActor
);
TestTrue(
TEXT("The orbit pawn must support at least one mouse-orbit gesture by default."),
OrbitPawn->bUseMiddleMouseOrbit || OrbitPawn->bUseRightMouseOrbitWithShift

View file

@ -149,6 +149,8 @@ for the full 29-repo queue and per-repo wiring posture.
- [x] Queue early Unreal command and shell-state pushes during bootstrap so the browser lane remains deterministic even if the JS runtime finishes loading slightly later than the CEF widget
- [x] Add shell-verification coverage proving the authoritative shell, bootstrap script, and fallback runtime exist and no longer point directly at a TypeScript source entrypoint
**Phase 1 answer after repair:** the intended wiring set is complete on the accepted posture. The runtime/direct/sidecar lanes for `1A` through `1J` are present in current code and runtime manifests; support-only rows such as `modelviewer.dev`, `glTF-Sample-Viewer`, `space-opera`, `render-fidelity-tools`, and `@react-spring/types` remain intentionally subordinate or non-default rather than evidence of a missing gameplay integration.
**Estimated actions:** 4060 per repo (UHT regen on first include)
**Estimated time:** 12 days per repo (build-debug cycles)
@ -216,12 +218,18 @@ for the full 29-repo queue and per-repo wiring posture.
**Goal:** A level you can launch, scramble, solve, and time.
**Phase 3 separation (current doctrine):**
- Code-owned runtime closure: first-party game mode, player controller, HUD, orbit camera, runtime cube spawn, and package-gate boot route.
- Packaged gate: real Windows package + launch proof.
- Editor-owned presentation closure: dedicated `.umap`, authored lights, and material polish.
### 3A — Level Creation
- [ ] Create `L_HyperTwist_ClassicTraining` (`.umap`)
- [ ] Place `AHyperTwistClassicCubeActor` at world origin
- [ ] Add directional light + ambient environment (basic, not immersive yet)
- [ ] Add `WBP_HyperTwistGameHUD` to viewport (optional polish replacement for the first-party runtime HUD that already auto-adds to viewport)
- [x] Add first-party orbit camera pawn with mouse-drag orbit and scroll-wheel zoom (`AHyperTwistClassicCubeOrbitPawn`) so camera interaction is owned in code before the `.umap` polish pass exists
- [x] Keep the source-owned runtime reachable on the generic validation map by spawning the cube above floor height and having the orbit pawn follow the active cube actor rather than assuming a hand-authored map origin
### 3B — Game Mode
- [x] Create first-party `AHyperTwistClassicCubeGameMode` : `AGameModeBase`
@ -230,6 +238,10 @@ for the full 29-repo queue and per-repo wiring posture.
- [x] Add an in-HUD button for "New Scramble" alongside the keyboard shortcut so restart does not depend on remembering `R`
### 3C — Build & Package Validation
- [x] Add source-controlled Windows helper scripts for package and smoke-launch validation:
- `scripts/Invoke-HyperTwistClassicCubePackage.ps1`
- `scripts/Launch-HyperTwistClassicCubePackage.ps1`
- [x] Define the temporary package-gate boot route explicitly as `/Engine/Maps/Templates/OpenWorld?game=/Script/UnrealHyperTwist.HyperTwistClassicCubeGameMode` until the dedicated `L_HyperTwist_ClassicTraining` map exists
- [ ] Package `UnrealHyperTwist` for Windows (not just Editor build)
- [ ] Launch packaged build on Windows host
- [ ] Verify: cube visible, clickable, scramble applies, timer works, solve detected
@ -241,7 +253,7 @@ for the full 29-repo queue and per-repo wiring posture.
## Phase 4: Wire Solver into Training Pipeline
**Prerequisite:** Phase 1A (rob-twophase) + Phase 2 (renderer)
**Prerequisite:** Phase 1A (rob-twophase) + Phase 2 (renderer) + Phase 3C packaged gate GREEN
### 4A — Solver HUD Integration
- [ ] Add "Hint" button to HUD
@ -266,7 +278,7 @@ for the full 29-repo queue and per-repo wiring posture.
## Phase 5: Speech Pipeline (STT + TTS)
**Prerequisite:** Phase 1B (freestyle) + Phase 1C (piper)
**Prerequisite:** Phase 1B (freestyle) + Phase 1C (piper) + Phase 3C packaged gate GREEN
### 5A — Voice Command Input
- [ ] Add "Voice" button to HUD (or use freestyle global hotkey)

View file

@ -164,8 +164,17 @@ Current repaired note:
- `1E`, `1G`, `1H`, `1I`, and `1J` now close through the authoritative `Content/Browser/index.html` shell, bundled-runtime upgrade path, committed clean-checkout fallback runtime, and embedded `UHyperTwistBrowserWidget` / bridge-object lane.
- The browser queue is no longer blocked on retaining `dist/index.html`; build artifacts can be cleaned after validation because the authoritative shell remains runnable without them.
### Phase 1 wiring answer — 2026-06-11
- Yes: the intended Phase 1 wiring set is complete on the accepted posture.
- `1A` through `1J` are live across direct-link, IPC, retained-sidecar, and authoritative-browser-shell routes.
- Do not miscount intentionally subordinate or non-default rows as missing runtime wiring:
- `google/model-viewer/packages/modelviewer.dev` remains docs-only.
- `@react-spring/types` remains dev-types only.
- `KhronosGroup/glTF-Sample-Viewer`, `google/model-viewer/packages/space-opera`, and `google/model-viewer/packages/render-fidelity-tools` are retained support or sidecar lanes beneath the landed browser-viewer owner rather than missing standalone gameplay lanes.
---
## Next Step
**Current next step after repair:** package-proof and presentation-proof work, not Phase 0. The repaired code lane already owns the authoritative browser shell, native-sidecar browser bridge, first-party classic-cube player controller, first-party orbit camera pawn, and first-party timer/HUD loop including GUI or keyboard scramble restart. The remaining best move is Windows packaged-build validation plus the level/material polish that still requires Unreal Editor content work.
**Current next step after repair:** package-proof and presentation-proof work, not Phase 0. The repaired code lane already owns the authoritative browser shell, native-sidecar browser bridge, first-party classic-cube player controller, first-party orbit camera pawn, active-cube camera follow, above-floor validation-map cube spawn, and first-party timer/HUD loop including GUI or keyboard scramble restart. The remaining best move is to run the real Windows packaged-build gate through `scripts/Invoke-HyperTwistClassicCubePackage.ps1`, then keep the dedicated `.umap` plus material/light polish as the separate Unreal Editor asset lane.

View file

@ -161,7 +161,7 @@ repo.
| Feature | Status | Primary authority | Notes |
|---|---|---|---|
| Native puzzle-state runtime | Implemented now | first-party runtime + landed donor packets | Core product identity. |
| Classic-cube playable runtime loop | Implemented now | first-party current code + landed timer/training substrate | First-party `AHyperTwistClassicCubeActor`, `AHyperTwistClassicCubeGameMode`, `AHyperTwistClassicCubePlayerController`, `AHyperTwistClassicCubeOrbitPawn`, and `UHyperTwistClassicCubeHUDWidget` now own the bounded classic-cube scramble/play/timer/solve loop with left-click, right-click, touch, middle-mouse drag orbit, scroll-wheel zoom, GUI or keyboard fresh-attempt restart, and solved-state submission in code. Packaged-build proof plus art/material/level polish remain separate next-step work. |
| Classic-cube playable runtime loop | Implemented now | first-party current code + landed timer/training substrate | First-party `AHyperTwistClassicCubeActor`, `AHyperTwistClassicCubeGameMode`, `AHyperTwistClassicCubePlayerController`, `AHyperTwistClassicCubeOrbitPawn`, and `UHyperTwistClassicCubeHUDWidget` now own the bounded classic-cube scramble/play/timer/solve loop with left-click, right-click, touch, middle-mouse drag orbit, scroll-wheel zoom, GUI or keyboard fresh-attempt restart, solved-state submission, active-cube orbit focus, and above-floor validation-map spawn in code. The current package-gate route is source-controlled through `scripts/Invoke-HyperTwistClassicCubePackage.ps1` and `scripts/Launch-HyperTwistClassicCubePackage.ps1` using the explicit classic-cube boot URL until the dedicated `.umap` lands. Packaged-build proof plus art/material/level polish remain separate next-step work. |
| Classic-cubing semantic/runtime adapter | Implemented now | landed `cubing/cubing.js` packet | Live adapter family. |
| Classic-cubing semantic, bridge, and `MPL`-boundary reference grounding | Implemented now | `cubing/cubing.js` retained boundary-sensitive lane + first-party current code | Current live `Classic Cubing Semantics and Runtime` reference side includes seven rewritten first-party contract/reference targets grounded in `cubing/cubing.js`: semantics, geometry, viewer adapter, device boundary, search contract, Melinda bridge, and explicit `MPL` compliance-boundary notes. This does not displace the landed `Phase 4R-A` first-party owner lane, the separate `cubing/twisty.js` replay shell lane, the separate `cubing/alg.js` parser/AST lane, or the explicit practical `MPL` path and notice-retention boundary. |
| Seeded competition scramble workflow and lightweight scramble-operator shell adjuncts | Deep-source grounded retained | `cubing/cubing.js` retained lane + `cubing/mark3` / `cubing/scramble.cubing.net` successor evaluation | Source-backed successor surfaces sharpen competition-spec workflow and operator-shell expectations above the retained scramble and visualization seams, but they do not displace `cubing/cubing.js` or `cubing/twisty.js`; `scramble-display` remains comparison-only. |

View file

@ -42,6 +42,15 @@ Current consolidated milestone snapshot:
not just a deferred idea; proof lives in the primary `22022` lane plus the
fallback `22023` recovery path documented in
`C:\HyperTwist\docs\ops\HYPERTWIST_REVERSE_SSH_WINDOWS_BUILD_LANE_VERIFICATION_2026-06-01.md`
- the classic-cube Phase 3 lane is now split explicitly into:
- code-owned runtime closure already present in first-party code
- a real packaged Windows gate that still must run green
- a separate editor-owned `.umap` / lights / materials polish lane
- the current packaged validation bridge for that lane is source-controlled via
`C:\HyperTwist\scripts\Invoke-HyperTwistClassicCubePackage.ps1` and
`C:\HyperTwist\scripts\Launch-HyperTwistClassicCubePackage.ps1`
- `Phase 4` and `Phase 5` widening should not proceed ahead of the classic-cube
packaged gate; they reopen only after that gate is green
- the canonical HyperTwist repo-row portfolio is now treated as `75` rows, not `71`
- currently implemented rows are now `35`, not `20`

View file

@ -0,0 +1,78 @@
param(
[string]$ProjectRoot = 'C:\HyperTwist',
[string]$ArchiveDirectory = 'C:\HyperTwist\packaged\classic-cube',
[ValidateSet('Development', 'Shipping')]
[string]$Configuration = 'Development',
[string]$CookMap = '/Engine/Maps/Templates/OpenWorld',
[switch]$CleanArchive,
[switch]$SkipBuild,
[switch]$SkipLaunch
)
$ErrorActionPreference = 'Stop'
$RunUatPath = 'C:\Program Files\Epic Games\UE_5.7\Engine\Build\BatchFiles\RunUAT.bat'
$UProjectPath = Join-Path $ProjectRoot 'UnrealHyperTwist\UnrealHyperTwist.uproject'
$LaunchScriptPath = Join-Path $ProjectRoot 'scripts\Launch-HyperTwistClassicCubePackage.ps1'
$ClassicCubeBootUrl = "$CookMap?game=/Script/UnrealHyperTwist.HyperTwistClassicCubeGameMode"
if (-not (Test-Path $RunUatPath))
{
throw "RunUAT was not found at '$RunUatPath'."
}
if (-not (Test-Path $UProjectPath))
{
throw "UnrealHyperTwist project file was not found at '$UProjectPath'."
}
if ($CleanArchive -and (Test-Path $ArchiveDirectory))
{
Remove-Item -LiteralPath $ArchiveDirectory -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $ArchiveDirectory | Out-Null
$RunUatArguments = @(
'BuildCookRun',
"-project=$UProjectPath",
'-noP4',
'-platform=Win64',
"-clientconfig=$Configuration",
'-cook',
'-stage',
'-package',
'-pak',
'-archive',
"-archivedirectory=$ArchiveDirectory",
"-map=$CookMap",
'-unattended',
'-utf8output'
)
if (-not $SkipBuild)
{
$RunUatArguments += '-build'
}
Write-Host "Packaging HyperTwist classic-cube validation lane to '$ArchiveDirectory'..."
& $RunUatPath @RunUatArguments
if ($LASTEXITCODE -ne 0)
{
throw "RunUAT packaging failed with exit code $LASTEXITCODE."
}
if (-not $SkipLaunch)
{
if (-not (Test-Path $LaunchScriptPath))
{
throw "Launch script was not found at '$LaunchScriptPath'."
}
& $LaunchScriptPath -PackageRoot $ArchiveDirectory -MapUrl $ClassicCubeBootUrl
if ($LASTEXITCODE -ne 0)
{
throw "Packaged classic-cube smoke launch failed with exit code $LASTEXITCODE."
}
}

View file

@ -0,0 +1,47 @@
param(
[string]$PackageRoot = 'C:\HyperTwist\packaged\classic-cube',
[string]$MapUrl = '/Engine/Maps/Templates/OpenWorld?game=/Script/UnrealHyperTwist.HyperTwistClassicCubeGameMode',
[int]$SmokeSeconds = 10,
[int]$ResX = 1600,
[int]$ResY = 900,
[switch]$KeepRunning
)
$ErrorActionPreference = 'Stop'
$CandidateExecutablePaths = @(
(Join-Path $PackageRoot 'Windows\UnrealHyperTwist.exe'),
(Join-Path $PackageRoot 'WindowsNoEditor\UnrealHyperTwist.exe'),
(Join-Path $PackageRoot 'UnrealHyperTwist.exe')
)
$ExecutablePath = $CandidateExecutablePaths | Where-Object { Test-Path $_ } | Select-Object -First 1
if ($null -eq $ExecutablePath)
{
throw "No packaged UnrealHyperTwist executable was found beneath '$PackageRoot'."
}
$ArgumentList = @(
$MapUrl,
"-ResX=$ResX",
"-ResY=$ResY",
'-windowed',
'-log'
)
Write-Host "Launching packaged classic-cube validation lane from '$ExecutablePath'..."
$Process = Start-Process -FilePath $ExecutablePath -ArgumentList $ArgumentList -PassThru
Start-Sleep -Seconds $SmokeSeconds
$Process.Refresh()
if ($Process.HasExited)
{
throw "Packaged classic-cube executable exited early with code $($Process.ExitCode)."
}
Write-Host "Packaged classic-cube smoke launch succeeded (PID $($Process.Id))."
if (-not $KeepRunning)
{
Stop-Process -Id $Process.Id -Force
Write-Host 'Stopped packaged classic-cube smoke process after successful launch validation.'
}