using UnityEngine; using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Collections; // DTOs (make sure these match your existing definitions) [Serializable] public class TileDto { public int x, y; public bool isRevealed, isFlagged; public int nearbyMines; public string aura; } [Serializable] public class PieceDto { public string type; public List cells; } [Serializable] public class BoardStateDto { public string gameId; public List board; public PieceDto nextPiece; public PieceDto heldPiece; public List ghostCells; } public class RemoteStatePusher : MonoBehaviour { // WebGL plugin externs [DllImport("__Internal")] static extern void WSInit(string url); [DllImport("__Internal")] static extern void WSSend(string json); GameManager _gm; TetrominoSpawner _spawner; void Awake() { _gm = GameObject.FindWithTag("GameController").GetComponent(); _spawner = FindObjectOfType(); } IEnumerator Start() { #if UNITY_WEBGL && !UNITY_EDITOR var gameId = GameSession.Instance.GameId; WSInit($"ws://localhost:3250/ws?gameId={gameId}"); yield return new WaitForSeconds(0.1f); Debug.Log("[RemoteStatePusher] WS initialized"); #endif // hook into your game events GameManager.OnNewPieceEvent += PushCurrentState; GameManager.OnHardDropEvent += PushCurrentState; GameManager.OnLeftStuckEvent += PushCurrentState; GameManager.OnRightStuckEvent += PushCurrentState; GameManager.OnMinoLockEvent += PushCurrentState; yield break; } void OnDestroy() { // unhook events if you like GameManager.OnNewPieceEvent -= PushCurrentState; GameManager.OnHardDropEvent -= PushCurrentState; GameManager.OnLeftStuckEvent -= PushCurrentState; GameManager.OnRightStuckEvent -= PushCurrentState; GameManager.OnMinoLockEvent -= PushCurrentState; // clean up WebSocket connection if neede } void PushCurrentState() { var dto = GetBoardState(); dto.gameId = GameSession.Instance.GameId; var json = JsonUtility.ToJson(dto); Debug.Log($"[RemoteStatePusher] Sending state: {json}"); #if UNITY_WEBGL && !UNITY_EDITOR WSSend(json); #endif } BoardStateDto GetBoardState() { var dto = new BoardStateDto { board = new List(), gameId = GameSession.Instance.GameId }; for (int x = 0; x < _gm.sizeX; x++) { for (int y = 0; y < _gm.sizeY; y++) { var go = GameManager.gameBoard[x][y]; if (go == null) continue; var tile = go.GetComponent(); dto.board.Add(new TileDto { x = x, y = y, isRevealed = tile.isRevealed, isFlagged = tile.isFlagged, nearbyMines = tile.isRevealed ? tile.nearbyMines : -1, aura = tile.aura.ToString() }); } } dto.nextPiece = _spawner.tetrominoPreviewList.Count > 0 ? BuildPieceDto(_spawner.tetrominoPreviewList[0]) : null; var holdComp = UnityEngine.Object.FindFirstObjectByType(); dto.heldPiece = (holdComp != null && holdComp.heldTetromino != null) ? BuildPieceDto(holdComp.heldTetromino) : null; return dto; } PieceDto BuildPieceDto(GameObject piece) { var group = piece.GetComponent(); var cells = new List(); foreach (var t in group.GetChildTiles()) cells.Add(new Vector2Int(t.coordX, t.coordY)); return new PieceDto { type = group.tetrominoType.ToString(), cells = cells }; } }