using System; using RR.Game2D.Core.Managers; using UnityEngine; using UnityEngine.Events; namespace RR.Common.Controls { /// /// Публичный компонент COM-системы. /// [AddComponentMenu("RR/Controls/Com System Manager")] public class ComSystemManager : MonoBehaviour { private enum ControllerHandlerType { Default = 0, PrizeStand = 1 } [Serializable] private sealed class RoleHandlerBinding { public string role; public ControllerHandlerType handlerType = ControllerHandlerType.Default; [Min(0)] public int prizeStandBoxesCount; } [Header("Основные настройки")] [SerializeField] private bool saveMoneyBetweenSessions; [SerializeField] private bool enablePrizeChecking; [SerializeField] private bool keepAlive = true; [Header("Авторизация")] [SerializeField] private bool enableEncryptedHandshake = true; [SerializeField] private int handshakeTimeoutMs = 5000; [Header("Роль контроллера (после handshake)")] [SerializeField] private string roleRequestCommand = "97"; [SerializeField] private string roleResponsePrefix = "ROLE"; [SerializeField] private int roleRequestTimeoutMs = 1500; [SerializeField] private string mainControllerRole = "main_controller"; [Header("Мульти-устройства")] [SerializeField] private bool useAllDetectedPorts = true; [SerializeField] private string[] explicitPorts = Array.Empty(); [SerializeField] private string defaultDevicePort = ""; [Header("Фабрика обработчиков по ROLE")] [SerializeField] private RoleHandlerBinding[] roleHandlerBindings = { new RoleHandlerBinding { role = "prize_stand", handlerType = ControllerHandlerType.PrizeStand, prizeStandBoxesCount = 24 }, new RoleHandlerBinding { role = "prize_stand_controller", handlerType = ControllerHandlerType.PrizeStand, prizeStandBoxesCount = 24 } }; public static UnityEvent OnCreditAdded; public static UnityEvent OnCreditRemoved; public static UnityEvent OnInputReceived; public static Action OnInitialize; public static UnityEvent OnDeviceInputReceived; public static UnityEvent OnDeviceAuthorized; private static ComSystemManager _instance; private static int _creditsCount; private ComHub _manager; private LightController _light; private bool _initializeInvoked; public LightController Light => _light; public void Initialize() { _instance = this; OnCreditAdded = new UnityEvent(); OnCreditRemoved = new UnityEvent(); OnInputReceived = new UnityEvent(); OnDeviceInputReceived = new UnityEvent(); OnDeviceAuthorized = new UnityEvent(); if (!saveMoneyBetweenSessions) PlayerPrefs.SetInt("CoinsCount", 0); _creditsCount = PlayerPrefs.GetInt("CoinsCount", 0); _light = GetComponent() ?? gameObject.AddComponent(); _manager = new ComHub( useAllDetectedPorts, explicitPorts, defaultDevicePort, enableEncryptedHandshake, handshakeTimeoutMs, keepAlive || enablePrizeChecking, roleRequestCommand, roleResponsePrefix, roleRequestTimeoutMs, mainControllerRole, CreateRoleHandler); _manager.InitializeDevices(); } public static void AddCredit() { _creditsCount++; PlayerPrefs.SetInt("CoinsCount", _creditsCount); OnCreditAdded?.Invoke(_creditsCount); } public static bool Pay(int amount = 1) { if (_creditsCount < amount) { return false; } _creditsCount -= amount; PlayerPrefs.SetInt("CoinsCount", _creditsCount); PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1); PlayerPrefs.SetInt(PrizeSystem.key_startsNul, PlayerPrefs.GetInt(PrizeSystem.key_startsNul) + 1); OnCreditRemoved?.Invoke(_creditsCount); return true; } public static int GetCredits() => _creditsCount; public static void SetCredits(int amount) { int oldCredits = _creditsCount; _creditsCount = Mathf.Max(0, amount); PlayerPrefs.SetInt("CoinsCount", _creditsCount); if (_creditsCount > oldCredits) { OnCreditAdded?.Invoke(_creditsCount); } else if (_creditsCount < oldCredits) { OnCreditRemoved?.Invoke(_creditsCount); } } public static void Send(string message) { if (_instance == null || string.IsNullOrWhiteSpace(message)) return; _instance._manager?.SendToDefault(message); } public static void SendToDevice(string deviceId, string message) { if (_instance == null || string.IsNullOrWhiteSpace(message)) return; _instance._manager?.SendToDevice(deviceId, message); } public static void SendToAll(string message) { if (_instance == null || string.IsNullOrWhiteSpace(message)) return; _instance._manager?.SendToAll(message); } public static string GetLastInputState() { if (_instance?._manager == null) return "11111111111111111111111111"; string defaultId = _instance._manager.DefaultDeviceId; return string.IsNullOrWhiteSpace(defaultId) ? "11111111111111111111111111" : _instance._manager.GetLastInputState(defaultId); } public static string GetLastInputState(string deviceId) { return _instance?._manager?.GetLastInputState(deviceId) ?? "11111111111111111111111111"; } public static string[] GetConnectedDeviceIds() { return _instance?._manager?.ConnectedDeviceIds ?? Array.Empty(); } public static string GetDefaultDeviceId() { return _instance?._manager?.DefaultDeviceId ?? string.Empty; } public static string GetDeviceRole(string deviceId) { return _instance?._manager?.GetDeviceRole(deviceId) ?? string.Empty; } public static PrizeStandController GetPrizeStandController(string deviceId = null) { return _instance?._manager?.GetPrizeStandController(deviceId); } private IComControllerHandler CreateRoleHandler(string deviceId, string role) { if (string.IsNullOrWhiteSpace(role) || roleHandlerBindings == null || roleHandlerBindings.Length == 0) { return null; } RoleHandlerBinding binding = FindRoleBinding(role); if (binding == null) { return null; } if (binding.handlerType == ControllerHandlerType.PrizeStand) { PrizeStandController controller = new PrizeStandController( deviceId, (id, message) => _manager != null && _manager.SendToDevice(id, message), message => GameManager.Logger?.Log("PrizeStandController", $"[{deviceId}] {message}")); controller.SetBoxesCount(binding.prizeStandBoxesCount); return controller; } return null; } private RoleHandlerBinding FindRoleBinding(string role) { for (int i = 0; i < roleHandlerBindings.Length; i++) { RoleHandlerBinding binding = roleHandlerBindings[i]; if (binding == null || string.IsNullOrWhiteSpace(binding.role)) { continue; } if (string.Equals(binding.role.Trim(), role.Trim(), StringComparison.OrdinalIgnoreCase)) { return binding; } } return null; } public void Blink() => _light.Blink(); public void StopBlink(bool enableHat = true) => _light.StopBlink(enableHat); private void Update() { if (_manager == null) return; int coinsProcessed = 0; while (_manager.TryDequeueCoin(out _)) { AddCredit(); coinsProcessed++; } while (_manager.TryDequeueInput(out DeviceInputMessage msg)) { OnDeviceInputReceived?.Invoke(msg.DeviceId, msg.InputBits); if (msg.DeviceId == _manager.DefaultDeviceId) { OnInputReceived?.Invoke(msg.InputBits); } } while (_manager.TryDequeueAuthorized(out string deviceId)) { OnDeviceAuthorized?.Invoke(deviceId); if (!_initializeInvoked) { _initializeInvoked = true; OnInitialize?.Invoke(); } if (deviceId == _manager.DefaultDeviceId) { _light.Blink(); } } if (coinsProcessed > 0) { GameManager.Logger?.Log("ComSystemManager", $"Обработано монет: {coinsProcessed}"); } } private void OnDisable() { _manager?.ShutdownAll(false); _manager = null; _initializeInvoked = false; } private void OnApplicationQuit() { _manager?.ShutdownAll(true); _manager = null; _initializeInvoked = false; } } }