using System; using Unity.Pipeline.Config; using Unity.Pipeline.Models; using UnityEngine; namespace Unity.Pipeline { /// The Player-side , owned by a . public class RuntimePipelineServer : BasePipelineServer { private RuntimePipelineConfig m_Config; private RuntimeInstanceDescriptor m_InstanceDescriptor; /// /// Guards mutate-then-write of m_InstanceDescriptor: concurrent /api/status probes both /// reach UpdateHeartBeat now, and RuntimeInstanceDescriptor.WriteToWorkingDirectory's own /// lock only covers the write, not the field mutation feeding it. /// private readonly object m_HeartbeatLock = new object(); /// UTC time this server instance started listening. public override DateTime StartedAt => m_InstanceDescriptor == null ? new DateTime() : m_InstanceDescriptor.StartedAt; /// Create a server bound to the given configuration. /// The runtime pipeline configuration. public RuntimePipelineServer(RuntimePipelineConfig config) { m_Config = config; } /// Runtime servers use ports 7900-7949 (test runtime servers use 7950-7999). /// The inclusive port range to try when binding the listener. protected override (int basePort, int maxPort) GetPortRange() { return (7900, 7949); // Runtime production (test runtime servers use 7950-7999) } /// Write the runtime instance descriptor to the working directory for CLI discovery. protected override void CreateInstanceDescriptor() { // Create and write instance descriptor for CLI discovery m_InstanceDescriptor = RuntimeInstanceDescriptor.CreateCurrent(Port, m_Config); RuntimeInstanceDescriptor.WriteToWorkingDirectory(m_InstanceDescriptor); } /// Remove the runtime instance descriptor on shutdown. protected override void DeleteInstanceDescriptor() { // Clean up instance descriptor file if (m_InstanceDescriptor != null) { RuntimeInstanceDescriptor.RemoveFromWorkingDirectory(); } m_InstanceDescriptor = null; } /// Refresh the descriptor's heartbeat timestamp. protected override void UpdateHeartBeat() { // Update heartbeat in instance descriptor if (m_InstanceDescriptor == null) return; lock (m_HeartbeatLock) { m_InstanceDescriptor.LastHeartbeat = DateTime.UtcNow; try { RuntimeInstanceDescriptor.WriteToWorkingDirectory(m_InstanceDescriptor); } catch (Exception ex) { Debug.LogWarning($"Failed to update instance descriptor: {ex.Message}"); } } } /// Build the Player-specific status/heartbeat payload for /api/status. /// The status payload. protected override object GetServerStatus() { UpdateHeartBeat(); return new { status = m_InstanceDescriptor == null ? "error" : "ready", lastHeartbeat = m_InstanceDescriptor?.LastHeartbeat, capabilities = Capabilities }; } /// The bearer token clients must present to authenticate requests. /// The current token. protected override string GetToken() { return m_InstanceDescriptor.EvalToken; } } }