So I can confirm that this is on the Epic’s servers, I went into my local cache files to ensure a fresh cook, %APPDATA%..\Local\UnrealEditorFortnite and removed all folders containing the project name “RewinDead” including the ValkyrieUploadTemp\ and Saved\VerseProject and VerseSnapshots. This was to ensure a completely fresh copy locally that did not even contain the file “vhs_player_device.verse”.
The EPIC servers ARE caching this file somewhere! As I removed it from my workspace, and from the entire project and local cache and cook locations it would not have been pushed up, thus the build server should not have had it.
I DO NOT USE URC - We use Perforce so we can control our builds and enviroment to prevent issues like this. I had one of my team members perform the same tests and the results were the same showing that the epic cook still was finding this now removed “vhs_player_device.verse”. I would assume we wouldn’t be hitting the same pre-cooked containers.
This tells us the NOMAD pipeline is pre-caching and not flushing for new builds. I’ve worked in server architecture and security for 20 years and understand how the pipeline is setup thanks to a talk from HashiCorp (Cooking with Nomad: Powering the Fortnite creator ecosystem)
I’ve even restored the file from perforce to the EXACT file with no changes (exact MD5 checksum) of the version that worked - PRIVATE CODE: 9258-3078-4302 11/04/2025, 12:18 PM
As you can see there is no classes and the class referenced is also not a persistable class:
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Assets }
using { /UnrealEngine.com}
using { /Verse.org}
using { VhsSystem }
using { SearchSystem }
###############################################################################
### VHS Player Device (TV/Monitor in world)
### When player interacts, spawns collectible tape and turns off display
###############################################################################
# Material wrapper that holds an instantiated material for parameter access
<#
vhs_material_instance<public> := class:
# The instantiated material we can modify
Material<public> : Flipbooks.tape_0000.MI_VHS_ParentMaterial = Flipbooks.tape_0000.MI_VHS_ParentMaterial{}
# Always set the material on the prop first
SetMaterial(ScreenProp:creative_prop, ?Index:int = 0) : void =
ScreenProp.SetMaterial(Material, ?Index:=Index)
# Set preview parameters
SetPreviewParams<public>(?ScreenProp:?creative_prop = false) : void =
if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
set Material.NoiseIntensity = 0.3
set Material.ScreenOff = 0.0
set Material.FlipbookUVs = 0.0
set Material.FPS = 4.0
# Set playback parameters
SetPlaybackParams<public>(FPS : float, Duration : float, TotalFrames : float, ?ScreenProp:?creative_prop = false) : void =
if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
set Material.NoiseIntensity = 0.0
set Material.ScreenOff = 0.0
set Material.FlipbookUVs = 0.0
set Material.FPS = FPS
set Material.PlayRate = 1.0
set Material.Duration_Seconds = Duration
set Material.Total_Frames = TotalFrames
# Update flipbook frame
UpdateFrame<public>(FrameValue : float, ?ScreenProp:?creative_prop = false) : void =
if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
set Material.FlipbookUVs = FrameValue
# Turn screen off
SetScreenOff<public>(?ScreenProp:?creative_prop = false) : void =
if (ValidProp := ScreenProp?) then ValidProp.SetMaterial(Material)
set Material.ScreenOff = 1.0
set Material.NoiseIntensity = 0.0
# Get total frames
GetTotalFrames<public>() : float = Material.Total_Frames
#>
# Constructor to create material instance - directly instantiate from source material
<#
MakeVHSMaterial<constructor>(TapeMaterial : ?material)<transacts><decides>:= vhs_material_instance:
# Typecast Tapematerial to ensure it's a valid child material
# Function will fail if not
Material := Flipbooks.tape_0000.MI_VHS_ParentMaterial[TapeMaterial?]
MakeVHSMaterial<constructor>(Src : vhs_material_instance)<transacts>:= vhs_material_instance:
# Make a new class with static parameter
Material := Src.Material
#>
vhs_player_device<public> := class<unique>(creative_device):
Logger : log = log{Channel := log_vhs}
# TV/Monitor prop with VHS material
@editable
var ScreenProp<public> : creative_prop = creative_prop{}
@editable
var ScreenMaterial : Flipbooks.MM_VHS_PARENT_UI = Flipbooks.MM_VHS_PARENT_UI{}
# This tape's specific material instance for playback
@editable
var TapeMaterial<public> : Flipbooks.MM_VHS_PARENT_Prop = Flipbooks.MM_VHS_PARENT_Prop{}
@editable
var TapeTexture<public> : ?texture = false
# Instantiated material wrapper for parameter access
# var VHSMaterial<private> : vhs_material_instance = vhs_material_instance{}
#@editable
#VHSMat : vhs_material = vhs_material{}
# This tape's resource ID (e.g., 5101, 5204, 5306)
@editable
TapeResourceID<public> : int = 5101
# Playback properties - configure per device in UEFN
@editable
FlipbookFrames<public> : int = 80
@editable
PlaybackFPS<public> : float = 4.0
@editable
DurationSeconds<public> : float = 20.0
@editable
StoryID<public> : vhs_story_id = vhs_story_id.None
@editable
TapeNumber<public> : int = 0 # Position within story (1, 2, 3...)
# Audio device playing static/preview
@editable
AudioDevice<public> : ?audio_player_device = false
@editable
UIPreviewAudio: ?audio_player_device = false
@editable
StaticAudio: ?audio_player_device = false
# Collectible to spawn when player interacts
@editable
TapeCollectible<public> : collectible_object_device = collectible_object_device{}
# Interaction method
@editable
InteractionTrigger<public> : ?trigger_device = false
@editable
PlayTapeUI: conversation_device = conversation_device{}
# Use SearchSystem integration (tag with vhs_player_tag)
#@editable
#UseSearchSystem<public> : logic = true
var TapeData : vhs_tape_data = vhs_tape_data{}
# State tracking - lives on the device
var HasBeenCollected<private> : logic = false
var HasBeenWatched<private> : logic = false
var IsScreenOn<public> : logic = true
# Public state accessors
IsCollected<public>()<transacts> : logic = HasBeenCollected
IsWatched<public>()<transacts> : logic = HasBeenWatched
MarkAsCollected<public>()<transacts> : void = set HasBeenCollected = true
MarkAsWatched<public>()<transacts> : void = set HasBeenWatched = true
OnBegin<override>()<suspends> : void =
Logger.Print("VHS Player Device initializing (Tape ID: {TapeResourceID})...")
# Register this tape's data in the map - this preserves the material instance
# Use ScreenMaterial which is the typed material configured in UEFN
ResourceName := if (ResourceDef := ResourceSystem.GetResource[TapeResourceID]):
ResourceDef.Name
else:
"Unknown"
# Create source data object with the ScreenMaterial from UEFN
# This preserves the unique flipbook_texture baked into each material instance
SourceData := vhs_tape_data:
ResourceID := TapeResourceID
TapeName := ResourceName
StoryID := StoryID
TapeNumber := TapeNumber
FlipbookFrames := FlipbookFrames
PlaybackFPS := PlaybackFPS
DurationSeconds := DurationSeconds
ScreenProp := ScreenProp
TapeMaterial := option{ScreenMaterial} # Store the typed material instance
AudioDevice := AudioDevice
# Use constructor to create final tape data
set TapeData = MakeTapeData(SourceData)
RegisterTapeData(TapeData)
Logger.Print("VHS Player {TapeResourceID} registered tape data with material instance")
# Fall back to trigger/button
if (Trigger := InteractionTrigger?):
Logger.Print("VHS Player {TapeResourceID} configured with Trigger for interaction")
Trigger.TriggeredEvent.Subscribe(OnVHSTriggered)
# Subscribe to conversation events
PlayTapeUI.OnConversationEvent.Subscribe(HandleConversationEvent)
Logger.Print("VHS Player Device {TapeResourceID} ready")
SetPreview(Agent:agent):void =
Logger.Print("Setting VHS Player {TapeResourceID} to preview mode")
# Configure THIS tape's unique material for preview with noise
ScreenProp.SetMaterial(TapeMaterial, ?Index:=0 )
if (FlipbookTexture := TapeTexture?):
set TapeMaterial.FlipbookTexture = FlipbookTexture
#var PreviewMaterial : Flipbooks.MM_VHS_PARENT_UI = TapeMaterial
set TapeMaterial.NoiseIntensity = 0.3 # Light noise for preview
set TapeMaterial.ScreenOff = 0.0
set TapeMaterial.FlipbookUVs = 0.0
set TapeMaterial.FPS = 4.0
set TapeMaterial.PlayRate = 0.5 # Slow playback for preview
Logger.Print("Texture set for {TapeResourceID} Succesfully")
else:
ScreenProp.SetMaterial(ScreenMaterial) # REMOVE AFTER TESTING 11/1/25 21.15
Logger.Print("FAILED to set texture for Screenprop {TapeResourceID}", ?Level:=log_level.Error)
# Apply the configured material (keeps the unique flipbook texture)
#ScreenProp.SetMaterial(PreviewMaterial, ?Index:= 0)
# Retrieve material from tape data map - this preserves the actual instance type
#if (TapeData := GetTapeData[TapeResourceID]):
# if (Mat := TapeData.TapeMaterial?):
# Typecast to parent to access parameters
# if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
## set VHSMat.NoiseIntensity = 0.3
# set VHSMat.ScreenOff = 0.0
# set VHSMat.FlipbookUVs = 0.0
# set VHSMat.FPS = 4.0
# ScreenProp.SetMaterial(Mat) # Apply the original material, not the typecast
# Logger.Print("Applied preview material for tape {TapeResourceID}")
# Play static audio
if(AudioPlayer := StaticAudio?):
AudioPlayer.Play()
# Enable the preview audio
if (PreviewAudio := UIPreviewAudio?):
PreviewAudio.Enable()
PreviewAudio.Register(Agent)
# Enable main audio device
if (AudioPlayer := AudioDevice?):
AudioPlayer.Enable()
AudioPlayer.Register(Agent)
###########################################################################
# Public methods for broadcast playback
###########################################################################
# Play this tape on a target screen (or this device's screen if none provided)
# Returns when playback is complete
Play<public>(Player : agent, ?TargetScreen : ?creative_prop = false)<suspends> : void =
# Determine which screen to play on
PlaybackScreen := if (Target := TargetScreen?):
Target
else:
ScreenProp
# Retrieve material from tape data map - this preserves the actual instance type
if (TapeDataSession := GetTapeData[TapeResourceID]):
if (Mat := TapeDataSession.TapeMaterial?):
# Typecast to parent to access parameters
if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
# Set playback parameters
set VHSMat.NoiseIntensity = 0.0 # Clean playback
set VHSMat.ScreenOff = 0.0
set VHSMat.FlipbookUVs = 0.0
set VHSMat.FPS = PlaybackFPS
set VHSMat.PlayRate = 1.0
set VHSMat.Duration_Seconds = DurationSeconds
set VHSMat.Total_Frames = FlipbookFrames * 1.0
PlaybackScreen.SetMaterial(Mat) # Apply the original material, not the typecast
Logger.Print("Applied material for tape {TapeResourceID} to screen")
# Start audio playback
if (AudioDev := AudioDevice?):
AudioDev.Play(Player)
Logger.Print("Audio playback started for tape {TapeResourceID}")
else:
Logger.Print("Warning: No audio device for tape {TapeResourceID}", ?Level := log_level.Warning)
# Animate playback over duration by updating FlipbookUVs
UpdateInterval := 0.1 # Update every 100ms
var ElapsedTime : float = 0.0
loop:
if (ElapsedTime >= DurationSeconds):
break
# Update material with new frame
Progress := ElapsedTime / DurationSeconds
CurrentFrame := Progress * VHSMat.Total_Frames
set VHSMat.FlipbookUVs = CurrentFrame
PlaybackScreen.SetMaterial(Mat) # Apply the original material
Sleep(UpdateInterval)
set ElapsedTime += UpdateInterval
# Stop audio when complete
if (AudioDev := AudioDevice?):
AudioDev.Stop(Player)
Logger.Print("Audio playback stopped for tape {TapeResourceID}")
# Mark as watched
MarkAsWatched()
Logger.Print("âś… Tape {TapeResourceID} playback complete")
#else:
# Logger.Print("ERROR: Cannot play tape {TapeResourceID} - no material instance", ?Level:=log_level.Error) # Handle player interaction
OnVHSTriggered<public>(MaybeAgent : ?agent) : void =
if (Agent := MaybeAgent?):
Logger.Print("VHS Player {TapeResourceID} triggered by player")
spawn {OnPlayerInteract(Agent)}
else:
Logger.Print("VHS Player {TapeResourceID} triggered with no agent", ?Level := log_level.Warning)
OnPlayerInteract<private>(Agent : agent)<suspends> : void =
if (HasBeenCollected?):
Logger.Print("Tape {TapeResourceID} already collected from this player")
return
Logger.Print("Player interacting with VHS player - playing tape {TapeResourceID}")
# Start Preview mode
SetPreview(Agent)
# Show the conversation UI for playing the tape
PlayTapeUI.InitiateConversation(Agent)
HandleConversationEvent(EventData : tuple(agent, int)) : void =
Agent := EventData(0)
EventInt := EventData(1)
# EventInt 3 = Take Tape
case (EventInt):
1 => block: # STOP TAPE
Logger.Print("Player chose to Stop tape {TapeResourceID} again")
if(PreviewAudio := UIPreviewAudio?):
PreviewAudio.Stop(Agent)
Logger.Print("Stopping Preview Audio for UI Tape {TapeResourceID}")
PlayTapeUI.HideConversation(Agent)
2 => block: # PLAY TAPE
Logger.Print("Player chose to Start playing tape preview {TapeResourceID}")
if(PreviewAudio := UIPreviewAudio?):
PreviewAudio.Play(Agent)
Logger.Print("Playing Preview Audio for UI Tape {TapeResourceID}")
3 => block: # EJECT TAPE
Logger.Print("Player Ejecting Tape {TapeResourceID}")
if(PreviewAudio := UIPreviewAudio?):
PreviewAudio.Stop(Agent)
Logger.Print("Stopping Preview Audio for UI Tape {TapeResourceID}")
OnTakeTape(Agent)
PlayTapeUI.EndConversation(Agent)
_ => Logger.Print("Unknown conversation event {EventInt} for tape {TapeResourceID}", ?Level := log_level.Warning)
OnTakeTape(Agent:agent):void =
Logger.Print("Player chose to take the tape - {TapeResourceID}")
# Turn off screen (set to black)
TurnOffScreen()
# Stop audio
StopAudio()
#EjectTapeAudio.Play() - This should play inside the conversation_device UI widget
# Get the location of the screenProp to spawn the tape collectible
# ScreenLocation := Temporary.SpatialMath.FromTransform(ScreenProp.GetTransform())
OffsetScreenLocation := (SpatialMath.vector3{Left := 0.0, Up := 50.0, Forward := 200.0}) # Offset above the screen
XYZOffset := Temporary.SpatialMath.FromVector3(OffsetScreenLocation)
XYZPosition := Temporary.SpatialMath.FromTransform(ScreenProp.GetTransform())
# Ensure both are vector3, then add
OffsetTranslation := Temporary.SpatialMath.FromVector3(SpatialMath.vector3{
Left := XYZPosition.Translation.Left + OffsetScreenLocation.Left,
Up := XYZPosition.Translation.Up + OffsetScreenLocation.Up,
Forward := XYZPosition.Translation.Forward + OffsetScreenLocation.Forward
})
NewTransform := Temporary.SpatialMath.FromTransform(transform{
Translation := OffsetTranslation
#Rotation := Rotation{}#Temporary.SpatialMath.FromVector3(XYZPosition.rotation{}) # Keep the vfx starting rotation
})
# Spawn collectible
if(ResourceName := ResourceSystem.GetResource[TapeResourceID].Name):
Logger.Print("Spawning tape collectible for resource {ResourceName}")
ResourceSystem.GetItem(Agent, ResourceName, ?Location := option{NewTransform})
# Register this tape in the VHS session so it appears in the Mix Deck UI
if (Session := GetVHSSession[Agent]):
Session.AddCollectedTape(TapeResourceID)
Logger.Print("Registered tape {TapeResourceID} in VHS session")
# Mark as collected
set HasBeenCollected = true
# Turn off screen display
TurnOffScreen<private>() : void =
# Retrieve material from tape data map and turn off screen
# if (TapeData := GetTapeData[TapeResourceID]):
if (Mat := TapeData.TapeMaterial?):
if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
set VHSMat.ScreenOff = 1.0
ScreenProp.SetMaterial(Mat) # Apply the original material
else:
Logger.Print("Unable to Typecast parent material for {TapeResourceID} on TurnOffScreen", ?Level:=log_level.Warning)
set IsScreenOn = false
# Update registry
UpdateRegistryState()
Logger.Print("Screen turned off for tape {TapeResourceID}")
# Stop audio playback
StopAudio<private>() : void =
if (AudioPlayer := AudioDevice?):
AudioPlayer.Stop()
Logger.Print("Audio stopped for tape {TapeResourceID}")
# State is now stored directly on device - no registry needed
UpdateRegistryState<private>()<transacts> : void =
# Collection state stored in HasBeenCollected
Logger.Print("Updated VHS player state for tape {TapeResourceID}")
# Public method to check screen state
IsOn<public>() : logic =
IsScreenOn
# Reset the device (for testing or respawning)
Reset<public>() : void =
set HasBeenCollected = false
set IsScreenOn = true
# Turn screen back on with preview settings using material from map
#if (TapeData := GetTapeData[TapeResourceID]):
if (Mat := TapeData.TapeMaterial?):
if (VHSMat := Flipbooks.MM_VHS_PARENT_UI[Mat]):
set VHSMat.NoiseIntensity = 0.3
set VHSMat.ScreenOff = 0.0
set VHSMat.FlipbookUVs = 0.0
set VHSMat.FPS = 4.0
ScreenProp.SetMaterial(Mat) # Apply the original material
# Restart audio
if (AudioPlayer := AudioDevice?):
AudioPlayer.Play()
Logger.Print("Audio started for tape {TapeResourceID}")
# Hide collectible
TapeCollectible.Hide()
# Update registry
UpdateRegistryState()
Logger.Print("VHS Player {TapeResourceID} device reset")
This is the exact MD5 checksum verified file that is currently uploaded and working however the Validation is still failing with the same error -
VerseBuild: Error: C:/build/FortniteGame/Plugins/GameFeatures/55bb032d-4f01-350e-2e20-ac9e49b43ac4/sys/published/Content/Verse/Scripts/VhsSystem/vhs_player_device.verse(362,22, 362,31): Script error 3588: Ambiguous identifier; could be (/UnrealEngine.com:)Temporary or (/Verse.org:)Temporary
errors.com.epicgames.cookplugin.versebuildscripterror
No matter what I try it seams to still use the same path over multiple days of attempting to Publish, it’s always the same - 55bb032d-4f01-350e-2e20-ac9e49b43ac4 - with the same file and error.
Please let me know what other information I can provide to assist.