using System; using System.Net; using System.Net.Sockets; using FloodSWE.Streaming; using FloodSWE.TileGraph; using UnityEngine; namespace FloodSWE.Networking { public sealed class SweQuestStreamClient : MonoBehaviour { [Header("Network")] public int listenPort = 29011; [Header("Interpolation")] public float frameDurationSeconds = 0.1f; [Header("Filter")] public bool filterByTile = false; public int expectedLod = 1; public int expectedTileX = 1; public int expectedTileY = 1; [Header("Output")] public Material targetMaterial; public string textureProperty = "_HeightTex"; public Texture2D outputTexture; public HeightmapPacket lastPacket; private readonly HeightmapInterpolator interpolator = new HeightmapInterpolator(); private UdpClient socket; private IPEndPoint endpoint; private int outputResolution; private void OnEnable() { interpolator.Reset(); interpolator.FrameDurationSeconds = Mathf.Max(0.01f, frameDurationSeconds); try { socket = new UdpClient(listenPort); socket.Client.Blocking = false; endpoint = new IPEndPoint(IPAddress.Any, 0); } catch (Exception ex) { Debug.LogError($"SweQuestStreamClient: failed to open UDP port {listenPort}. {ex.Message}"); enabled = false; } } private void OnDisable() { if (socket != null) { socket.Close(); socket = null; } } private void Update() { PollFrames(); interpolator.Step(Time.deltaTime); TryUpdateOutputTexture(); } private void PollFrames() { if (socket == null) { return; } while (socket.Available > 0) { byte[] payload; try { payload = socket.Receive(ref endpoint); } catch (SocketException) { break; } if (!SweUdpProtocol.TryDecodeFrame(payload, out HeightmapPacket packet)) { continue; } if (filterByTile) { TileId expected = new TileId(expectedLod, expectedTileX, expectedTileY); if (packet.Tile != expected) { continue; } } interpolator.PushPacket(packet); lastPacket = packet; } } private void TryUpdateOutputTexture() { if (!interpolator.TryGetHeights(out float[] heights) || heights == null || heights.Length == 0) { return; } int resolution = Mathf.RoundToInt(Mathf.Sqrt(heights.Length)); if (resolution <= 0 || resolution * resolution != heights.Length) { return; } EnsureOutputTexture(resolution); outputTexture.SetPixelData(heights, 0); outputTexture.Apply(false, false); if (targetMaterial != null) { targetMaterial.SetTexture(textureProperty, outputTexture); } } private void EnsureOutputTexture(int resolution) { if (outputTexture != null && outputResolution == resolution) { return; } if (outputTexture != null) { Destroy(outputTexture); } outputTexture = new Texture2D(resolution, resolution, TextureFormat.RFloat, false, true) { wrapMode = TextureWrapMode.Clamp, filterMode = FilterMode.Bilinear, name = "SWE_StreamedHeight" }; outputResolution = resolution; } } }