diff --git a/GreatGamedevVault/Библиотека скриптов/2. Периферия/COMInput (бывш. ComEnjoyer 🍆).md b/GreatGamedevVault/Библиотека скриптов/2. Периферия/COMInput (бывш. ComEnjoyer 🍆).md index 81c9513..0e03344 100644 --- a/GreatGamedevVault/Библиотека скриптов/2. Периферия/COMInput (бывш. ComEnjoyer 🍆).md +++ b/GreatGamedevVault/Библиотека скриптов/2. Периферия/COMInput (бывш. ComEnjoyer 🍆).md @@ -5,9 +5,10 @@ by: \_sega > [!info] 🚷Зачем оно? >Чтобы билдить IL2CPP и не терять контакт с периферийной платой потому что IL2CPP не имеет SerialPort. То ли он его отрезает то ли разрабы все предусмотрели🤡 -[Линки в библиотеке](file:///B:/!ФайлОбменник!/Юнити_Разработка/Библиотека/Периферия/UnityToEsp): -1. [Плагин](file:///B:/!ФайлОбменник!/Юнити_Разработка/Библиотека/Периферия/UnityToEsp/Assets/Plugins/x86_64) -2. [Ассеты плагина](file:///B:/!ФайлОбменник!/Юнити_Разработка/Библиотека/Периферия/UnityToEsp/Assets/Scripts) +[Линки в библиотеке](http://172.11.0.14:3000/Robotail/gamedev-lib/src/branch/master/%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0/%D0%9F%D0%B5%D1%80%D0%B8%D1%84%D0%B5%D1%80%D0%B8%D1%8F): +1. [Плагин](http://172.11.0.14:3000/Robotail/gamedev-lib/src/branch/master/%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0/%D0%9F%D0%B5%D1%80%D0%B8%D1%84%D0%B5%D1%80%D0%B8%D1%8F/%D0%92%D1%81%D1%82%D1%80%D0%B0%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5/Assets/Plugins/x86_64) +2. [Ассеты плагина](http://172.11.0.14:3000/Robotail/gamedev-lib/src/branch/master/%D0%91%D0%B8%D0%B1%D0%BB%D0%B8%D0%BE%D1%82%D0%B5%D0%BA%D0%B0/%D0%9F%D0%B5%D1%80%D0%B8%D1%84%D0%B5%D1%80%D0%B8%D1%8F/%D0%92%D1%81%D1%82%D1%80%D0%B0%D0%B8%D0%B2%D0%B0%D0%B5%D0%BC%D1%8B%D0%B5/Assets/Scripts) +3. [Исходник ComNative]() # Иструкция по внедрению ## Шаг I. Добавление файлов в проект diff --git a/GreatGamedevVault/Библиотека скриптов/2. Периферия/Система команд платы v2.x.md b/GreatGamedevVault/Библиотека скриптов/2. Периферия/Система команд платы v2.x.md index 17b2e4f..0014dfc 100644 --- a/GreatGamedevVault/Библиотека скриптов/2. Периферия/Система команд платы v2.x.md +++ b/GreatGamedevVault/Библиотека скриптов/2. Периферия/Система команд платы v2.x.md @@ -155,6 +155,34 @@ by: Паша Николаевич | 81 | зеленый | | 82 | синий | +##### Отдельный раздел для Данро + +`85XXYY`: +- XX – команда; +- YY – время в секундах. + +***Пример:*** `854505` – включить спецэффект ЗАРЯДКА на 5 секунд + +`85` – без параметра отключает процесс рендеринга эффектов (чтобы освободить оперативную память). + +###### Список эффектов + +| Команда (`XX`) | Название эффекта | +| :------------: | :----------------------: | +| 40 | Горизонтальная волна | +| 41 | Вертикальная волна | +| 42 | Диагональная волна | +| 43 | Радуга | +| 44 | Волна вверх | +| 45 | Вертикальная волна вверх | +| 46 | Конечная волна | +| 47 | Синяя волна | +| 48 | Диагональная волна 2 | +| 49 | Падающие ряды | +| 50 | Оранжевая волна | +| 51 | Морская волна | +| 52 | Диагональная волна 3 | + ## Список команд | Команда | Назначение, примеры, ответы | diff --git a/GreatGamedevVault/Библиотека/Карантин/AudioManager.cs b/GreatGamedevVault/Библиотека/Карантин/AudioManager.cs new file mode 100644 index 0000000..c84c7ee --- /dev/null +++ b/GreatGamedevVault/Библиотека/Карантин/AudioManager.cs @@ -0,0 +1,345 @@ +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Audio; + +namespace Managers +{ + + public class AudioManager : MonoBehaviour + { + // ==== Singleton ==== + public static AudioManager Instance { get; private set; } + + [Header("Music")] + [SerializeField] private AudioSource musicSource; // child: MusicSource + [SerializeField] private AudioSource tempMusicSource; // child: TempMusicSource + + [Tooltip("Группа микшера для музыки (необязательно)")] + [SerializeField] private AudioMixerGroup musicMixerGroup; + + [Tooltip("Целевой громкость музыки (после фейдов)")] + [Range(0f, 1.5f)] public float musicTargetVolume = 1f; + + [Tooltip("Длительность фейда по умолчанию (сек)")] + [SerializeField] private float defaultFadeDuration = 0.8f; + + [Header("SFX Pool")] + [SerializeField] private int sfxPoolSize = 8; + [SerializeField] private Transform sfxPoolParent; // child: SFX Pool + [Tooltip("Группа микшера для SFX (необязательно)")] + [SerializeField] private AudioMixerGroup sfxMixerGroup; + [Range(0f, 1.5f)] public float sfxDefaultVolume = 1f; + + private readonly List sfxPool = new List(64); + private int sfxRoundRobinIndex = 0; + + // Внутреннее + private Coroutine musicFadeCoroutine; + private Coroutine crossfadeCoroutine; + private float storedMusicVolumeBeforePause = 1f; + private bool isPausedFaded = false; + + // ==== Unity Lifetime ==== + public void Initialize() + { + // Singleton init + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + Instance = this; + DontDestroyOnLoad(gameObject); + + EnsureMusicSources(); + EnsureSfxPool(); + + } + //private void Awake() + //{ + //} + + // Создаёт/находит MusicSource и TempMusicSource + private void EnsureMusicSources() + { + if (musicSource == null) + { + var t = transform.Find("MusicSource"); + if (t != null) musicSource = t.GetComponent(); + } + if (tempMusicSource == null) + { + var t = transform.Find("TempMusicSource"); + if (t != null) tempMusicSource = t.GetComponent(); + } + + if (musicSource == null) musicSource = CreateChildAudioSource("MusicSource", loop: true, output: musicMixerGroup); + if (tempMusicSource == null) tempMusicSource = CreateChildAudioSource("TempMusicSource", loop: true, output: musicMixerGroup); + + PrepareMusicSource(musicSource); + PrepareMusicSource(tempMusicSource); + tempMusicSource.volume = 0f; // запасной источник молчит + musicSource.volume = Mathf.Clamp01(musicTargetVolume); + } + + private void PrepareMusicSource(AudioSource src) + { + src.playOnAwake = false; + src.loop = true; + src.spatialBlend = 0f; // 2D музыка + if (musicMixerGroup != null) src.outputAudioMixerGroup = musicMixerGroup; + } + + private AudioSource CreateChildAudioSource(string name, bool loop, AudioMixerGroup output) + { + var go = new GameObject(name); + go.transform.SetParent(transform, false); + var src = go.AddComponent(); + src.loop = loop; + src.playOnAwake = false; + src.spatialBlend = 0f; + if (output != null) src.outputAudioMixerGroup = output; + return src; + } + + // Создаёт/находит контейнер и пул SFX источников + private void EnsureSfxPool() + { + if (sfxPoolParent == null) + { + var t = transform.Find("SFX Pool"); + if (t != null) sfxPoolParent = t; + } + if (sfxPoolParent == null) + { + var go = new GameObject("SFX Pool"); + sfxPoolParent = go.transform; + sfxPoolParent.SetParent(transform, false); + } + + // Если уже есть дети-аудиоисточники — используем их как пул. + sfxPool.Clear(); + var existing = sfxPoolParent.GetComponentsInChildren(includeInactive: true); + foreach (var src in existing) + { + SetupSfxSource(src); + sfxPool.Add(src); + } + + // Добираем до нужного размера + for (int i = sfxPool.Count; i < sfxPoolSize; i++) + { + var src = CreateSfxSource($"SFX_{i:00}"); + sfxPool.Add(src); + } + } + + private AudioSource CreateSfxSource(string name) + { + var go = new GameObject(name); + go.transform.SetParent(sfxPoolParent, false); + var src = go.AddComponent(); + SetupSfxSource(src); + return src; + } + + private void SetupSfxSource(AudioSource src) + { + src.playOnAwake = false; + src.loop = false; + src.spatialBlend = 0f; // по умолчанию 2D + src.volume = sfxDefaultVolume; + if (sfxMixerGroup != null) src.outputAudioMixerGroup = sfxMixerGroup; + } + + // ==== Public API ==== + + /// + /// Мгновенно запустить музыку (без фейда). + /// + public void PlayMusicImmediate(AudioClip clip, bool loop = true, float targetVolume = -1f) + { + if (clip == null) return; + + StopCrossfadeAndFade(); + musicSource.clip = clip; + musicSource.loop = loop; + musicSource.volume = (targetVolume >= 0f ? targetVolume : musicTargetVolume); + musicSource.Play(); + + tempMusicSource.Stop(); + tempMusicSource.clip = null; + tempMusicSource.volume = 0f; + } + + /// + /// 2) Плавно сменить музыку (кроссфейд A->B). + /// + public void CrossfadeMusic(AudioClip newClip, float duration = -1f, bool loop = true, float newTargetVolume = -1f) + { + if (newClip == null) return; + + float d = (duration > 0f ? duration : defaultFadeDuration); + float toVol = (newTargetVolume >= 0f ? newTargetVolume : musicTargetVolume); + + StopCrossfadeAndFade(); + crossfadeCoroutine = StartCoroutine(CrossfadeRoutine(newClip, d, loop, toVol)); + } + + /// + /// 1) Плавно приостановить музыку (для паузы). + /// pause=true: плавный фейд к 0 и Pause(). pause=false: UnPause() и плавный фейд к musicTargetVolume. + /// + public void SmoothPauseMusic(bool pause, float duration = -1f) + { + float d = (duration > 0f ? duration : defaultFadeDuration); + if (pause) + { + if (isPausedFaded) return; + storedMusicVolumeBeforePause = musicSource.volume; + StopCrossfadeAndFade(); + musicFadeCoroutine = StartCoroutine(FadeOutThenPause(musicSource, d)); + isPausedFaded = true; + } + else + { + if (!isPausedFaded) return; + StopCrossfadeAndFade(); + musicSource.UnPause(); + musicFadeCoroutine = StartCoroutine(Fade(musicSource, from: 0f, to: Mathf.Clamp01(musicTargetVolume), d)); + isPausedFaded = false; + } + } + + /// + /// 3) Воспроизвести SFX из пула (2D). + /// + public void PlaySfx(AudioClip clip, float volume = 1f, float pitch = 1f) + { + if (clip == null) return; + var src = GetFreeSfxSource(); + ConfigureAndPlaySfx(src, clip, volume, pitch, spatialBlend: 0f, position: null); + } + + /// + /// 3) Воспроизвести SFX из пула в мире (3D). + /// + public void PlaySfxAt(AudioClip clip, Vector3 position, float volume = 1f, float pitch = 1f, float spatialBlend = 1f) + { + if (clip == null) return; + var src = GetFreeSfxSource(); + ConfigureAndPlaySfx(src, clip, volume, pitch, Mathf.Clamp01(spatialBlend), position); + } + + // ==== Internal helpers ==== + + private void StopCrossfadeAndFade() + { + if (crossfadeCoroutine != null) { StopCoroutine(crossfadeCoroutine); crossfadeCoroutine = null; } + if (musicFadeCoroutine != null) { StopCoroutine(musicFadeCoroutine); musicFadeCoroutine = null; } + } + + private IEnumerator CrossfadeRoutine(AudioClip newClip, float duration, bool loop, float targetVol) + { + // Готовим второй источник + tempMusicSource.clip = newClip; + tempMusicSource.loop = loop; + tempMusicSource.volume = 0f; + tempMusicSource.Play(); + + float startVolA = musicSource.volume; + float t = 0f; + + while (t < duration) + { + t += Time.unscaledDeltaTime; // не зависим от timescale (музыка при паузе всё равно может фейдиться) + float k = Mathf.Clamp01(t / duration); + + // кроссфейд + musicSource.volume = Mathf.Lerp(startVolA, 0f, k); + tempMusicSource.volume = Mathf.Lerp(0f, targetVol, k); + + yield return null; + } + + // Завершение: стопим A, меняем роли (чтобы основной всегда указывал на текущую музыку) + musicSource.Stop(); + musicSource.clip = null; + musicSource.volume = targetVol; + + // Меняем ссылки местами + var swap = musicSource; + musicSource = tempMusicSource; + tempMusicSource = swap; + + // Убедимся, что запасной источник молчит + tempMusicSource.volume = 0f; + tempMusicSource.Stop(); + tempMusicSource.clip = null; + + crossfadeCoroutine = null; + } + + private IEnumerator FadeOutThenPause(AudioSource src, float duration) + { + float startVol = src.volume; + yield return Fade(src, startVol, 0f, duration); + src.Pause(); + musicFadeCoroutine = null; + } + + private IEnumerator Fade(AudioSource src, float from, float to, float duration) + { + float t = 0f; + src.volume = from; + if (!src.isPlaying) src.Play(); + + while (t < duration) + { + t += Time.unscaledDeltaTime; + float k = Mathf.Clamp01(t / duration); + src.volume = Mathf.Lerp(from, to, k); + yield return null; + } + src.volume = to; + } + + private AudioSource GetFreeSfxSource() + { + // Сначала ищем свободный + for (int i = 0; i < sfxPool.Count; i++) + { + int idx = (sfxRoundRobinIndex + i) % sfxPool.Count; + if (!sfxPool[idx].isPlaying) + { + sfxRoundRobinIndex = (idx + 1) % sfxPool.Count; + return sfxPool[idx]; + } + } + + // Если все заняты — берём по round-robin (steal) + var src = sfxPool[sfxRoundRobinIndex]; + sfxRoundRobinIndex = (sfxRoundRobinIndex + 1) % sfxPool.Count; + return src; + } + + private void ConfigureAndPlaySfx(AudioSource src, AudioClip clip, float volume, float pitch, float spatialBlend, Vector3? position) + { + src.clip = clip; + src.volume = Mathf.Clamp(volume * sfxDefaultVolume, 0f, 1.5f); + src.pitch = Mathf.Clamp(pitch, -3f, 3f); + src.spatialBlend = spatialBlend; + + if (position.HasValue) + { + src.transform.position = position.Value; + src.rolloffMode = AudioRolloffMode.Linear; + src.minDistance = 1f; + src.maxDistance = 20f; + } + + src.Play(); + } + } +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Карантин/GameManager.cs b/GreatGamedevVault/Библиотека/Карантин/GameManager.cs new file mode 100644 index 0000000..d344430 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Карантин/GameManager.cs @@ -0,0 +1,306 @@ +using System; +using System.IO; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace RR.Game2D.Core +{ + [DefaultExecutionOrder(-1000)] + public class GameManager : MonoBehaviour + { + public static GameManager Instance { get; private set; } + + [Header("Пауза")] + public bool IsPaused { get; private set; } + public event Action PauseStateChanged; + + [Header("Информация для игры")] + public int World { get; private set; } = 1; + public int Stage { get; private set; } = 1; + public int StagesByWorld { get; private set; } = 5; + public int Lives { get; private set; } = 3; + public int Coins { get; private set; } = 0; + + [Header("Игрок")] + [SerializeField] private GameObject playerPrefab; + [SerializeField] private string playerPrefabResourcePath = "Prefabs/Player"; + public GameObject PlayerObj { get; private set; } + private bool _shouldSpawnPlayer; + + private void OnEnable() => SceneManager.sceneLoaded += OnSceneLoaded; + private void OnDisable() => SceneManager.sceneLoaded -= OnSceneLoaded; + + public event Action PlayerSpawned; + + public event Action CoinsChanged; + + //--------------------------- + // Singleton + //--------------------------- + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + private static void Bootstrap() + { + if (Instance == null) + { + var go = new GameObject(nameof(GameManager)); + Instance = go.AddComponent(); + DontDestroyOnLoad(go); + } + } + + private void Awake() + { + if (Instance != null && Instance != this) + { + Destroy(gameObject); + return; + } + + Instance = this; + DontDestroyOnLoad(gameObject); + + if (SceneManager.GetActiveScene().name != "MainMenu") _shouldSpawnPlayer = true; + } + + private void OnDestroy() + { + if (Instance == this) Instance = null; + } + //--------------------------- + // + //--------------------------- + + //private void Start() + //{ + // //Application.targetFrameRate = 60; + // //LoadScene("MainMenu", false); + // //NewGame(); + // if (SceneManager.GetActiveScene().name != "MainMenu") + // { + // TrySpawnPlayerForActiveScene(); + // } + //} + + /// + /// Поиск префаба игрока. + /// Если не найдет префаб из инспектора, ищет в "Resources" + /// + /// Префаб игрока + private GameObject ResolvePlayerPrefab() + { + if (playerPrefab != null) return playerPrefab; + + var loaded = Resources.Load(playerPrefabResourcePath); + if (loaded == null) + { + Debug.LogError( + $"GameManager: playerPrefab is null. Resources.Load('{playerPrefabResourcePath}') вернул null. " + + $"Ожидается файл: Assets/Resources/{playerPrefabResourcePath}.prefab (точное совпадение регистра)."); + } + return loaded; + } + + /// + /// Начало новой игры. + /// Загружает первый уровень. + /// Устанавливает жизни на 3, монеты на 0 + /// + public void NewGame() + { + Lives = 3; + Coins = 0; + CoinsChanged?.Invoke(Coins); + LoadLevel(1, 1); + } + + // Можно заменить на другое поведение + /// + /// Окончание игры. + /// Загружает сцену главного меню + /// + public void GameOver() => LoadScene("MainMenu", false); + // -- + + /// + /// Загрузка уровня по данным. + /// Загружает уровень по номерам мира и уровня + /// + /// Номер мира + /// Номер уровня + public void LoadLevel(int world, int stage) + { + this.World = world; + this.Stage = stage; + + string sceneName = $"{world}-{stage}"; + if (SceneInBuildSettingsExists(sceneName)) + { + LoadScene(sceneName); + } + else + { + //NewGame(); + // Можно заменить на другое поведение + print("WIN"); + LoadScene("MainMenu", false); + // -- + } + } + + /// + /// Загрузка сцены по названию. + /// Вызывает одноименный метод в SceneManager. + /// Может загружать сцены с UI и без. + /// В сценах без UI игрок не спавнится + /// + /// Строковое название сцены + /// Использовать ли UI + public void LoadScene(string sceneName, bool useUI = true) + { + _shouldSpawnPlayer = true; + + SceneManager.LoadScene(sceneName); + + if (useUI) + { + SceneManager.LoadScene("Level UI", LoadSceneMode.Additive); + } + + else _shouldSpawnPlayer = false; + } + + private void OnSceneLoaded(Scene scene, LoadSceneMode mode) + { + string currentSceneName = SceneManager.GetActiveScene().name; + + TrySpawnPlayerForActiveScene(); + + if (currentSceneName != $"{World}-{Stage}" && currentSceneName.Contains("-")) + { + string[] levelInfo = currentSceneName.Split("-"); + World = Convert.ToInt32(levelInfo[0]); + World = Convert.ToInt32(levelInfo[1]); + } + } + + /// + /// Метод для спавна игрока. + /// Ищет первый спавнпоинт, спавнит игрока на его позиции. + /// Если на карте есть объект игрока, удаляет его и спавнит свой + /// + private void TrySpawnPlayerForActiveScene() + { + if (!_shouldSpawnPlayer) return; + PlayerSpawn spawn = FindFirstObjectByType(); + Vector3 pos = spawn ? spawn.transform.position : new Vector3(0, 1.3f, 0); + Quaternion rot = spawn ? spawn.transform.rotation : Quaternion.identity; + + PlayerObj = GameObject.FindGameObjectWithTag("Player"); + if (PlayerObj != null) Destroy(PlayerObj); + + var prefab = ResolvePlayerPrefab(); + if (prefab == null) return; + + PlayerObj = Instantiate(prefab, pos, rot); + SceneManager.MoveGameObjectToScene(PlayerObj, SceneManager.GetActiveScene()); + + PlayerSpawned?.Invoke(PlayerObj); + } + + + /// + /// Метод переход на следующий уровень. + /// Может загружать уровни и миры в прямой зависимости от + /// Количества уровней на мир(StagesByWorld) + /// + public void NextLevel() + { + if (Stage >= StagesByWorld) + { + World++; + Stage = 1; + } + else Stage++; + LoadLevel(World, Stage); + } + + /// + /// Метод сброса уровня. + /// При сбросе также сбрасывает монеты и отнимает жизни + /// + public void ResetLevel() + { + Lives--; + + Coins = 0; + CoinsChanged?.Invoke(Coins); + + //print($"Lives: {lives}"); + if (Lives > 0) LoadLevel(World, Stage); + else GameOver(); + } + + /// + /// Метод добавления монеты + /// + public void AddCoin() + { + Coins++; + // Можно заменить на другое поведение + if (Coins >= 100) + { + Coins = 0; + AddLife(); + } + // -- + CoinsChanged?.Invoke(Coins); + } + + /// + /// Метод паузы игры. + /// Ставит на паузу все SFX и саму игру + /// + /// Ставить ли на паузу + public void SetPause(bool pause) + { + if (IsPaused == pause) return; + IsPaused = pause; + + Time.timeScale = pause ? 0f : 1f; + AudioListener.pause = pause; + + PauseStateChanged?.Invoke(IsPaused); + } + + // Можно заменить на другое поведение + /// + /// Метод переключателя паузы + /// + public void TogglePause() => SetPause(!IsPaused); + // -- + + // Можно заменить на другое поведение + /// + /// Метод для добавления жизни. + /// + public void AddLife() => Lives++; + // -- + + /// + /// Метод проверки сцены на существование в настройках билда + /// + /// Строковое название сцены + /// + private static bool SceneInBuildSettingsExists(string sceneName) + { + int count = SceneManager.sceneCountInBuildSettings; + for (int i = 0; i < count; i++) + { + string path = SceneUtility.GetScenePathByBuildIndex(i); + if (Path.GetFileNameWithoutExtension(path) == sceneName) + return true; + } + return false; + } + } +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative.sln b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative.sln new file mode 100644 index 0000000..cbb102c --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36603.0 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComNative", "ComNative\ComNative.vcxproj", "{BCC43995-EF98-4066-BB8D-58819B0AFB01}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x64.ActiveCfg = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x64.Build.0 = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x86.ActiveCfg = Debug|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x86.Build.0 = Debug|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x64.ActiveCfg = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x64.Build.0 = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x86.ActiveCfg = Release|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {022B4E09-68D9-43CB-B2A3-E6ABE4525C96} + EndGlobalSection +EndGlobal diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj new file mode 100644 index 0000000..1e2c5d6 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {bcc43995-ef98-4066-bb8d-58819b0afb01} + ComNative + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + _DEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters new file mode 100644 index 0000000..6216c47 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Файлы заголовков + + + Файлы заголовков + + + Файлы заголовков + + + + + Исходные файлы + + + Исходные файлы + + + \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp new file mode 100644 index 0000000..34f0a2c --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp @@ -0,0 +1,261 @@ +// SerialNative.cpp +#include "SerialNative.h" +#include +#include +#include +#include +#include + +static std::mutex gMutex; +static std::unordered_map gHandles; +static int gNextId = 1; + +static thread_local std::wstring gLastError; + +static void SetLastErrFromWin32(const wchar_t* where, DWORD err = GetLastError()) { + wchar_t* msg = nullptr; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0, nullptr); + if (msg) { gLastError = std::wstring(where) + L": (" + std::to_wstring(err) + L") " + msg; LocalFree(msg); } + else { gLastError = std::wstring(where) + L": error " + std::to_wstring(err); } +} + +static std::wstring BuildDevicePath(const std::wstring& portName) { + if (portName.rfind(L"\\\\.\\", 0) == 0) return portName; // уже с префиксом + return L"\\\\.\\" + portName; +} + +// --- аккуратный enumerator COM-портов --- +static std::wstring EnumerateComPortsJoined() { + std::vector found; + wchar_t target[4096]; + for (int i = 1; i <= 256; ++i) { + std::wstring name = L"COM" + std::to_wstring(i); + DWORD r = QueryDosDeviceW(name.c_str(), target, static_cast(std::size(target))); + if (r != 0) found.push_back(name); + } + std::wstring joined; + for (size_t i = 0; i < found.size(); ++i) { + if (i) joined += L";"; + joined += found[i]; + } + return joined; +} + +// --- безопасная выдача HANDLE по id --- +static HANDLE GetHandle(int handleId) { + std::lock_guard lk(gMutex); + auto it = gHandles.find(handleId); + return (it == gHandles.end()) ? nullptr : it->second; +} + +// --- внутренняя запись байтов (без экспорта) --- +static int WriteBytes(HANDLE h, const uint8_t* data, int length) { + if (!h || !data || length < 0) { gLastError = L"WriteBytes: invalid args"; return -1; } + DWORD written = 0; + if (!WriteFile(h, data, static_cast(length), &written, nullptr)) { + SetLastErrFromWin32(L"WriteFile"); + return -1; + } + return static_cast(written); +} + +// --- гарантированная зачистка при выгрузке процесса (Alt+F4 и т.п.) --- +BOOL APIENTRY DllMain(HINSTANCE, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_DETACH) { + std::lock_guard lk(gMutex); + for (auto& kv : gHandles) { + HANDLE h = kv.second; + if (h && h != INVALID_HANDLE_VALUE) CloseHandle(h); + } + gHandles.clear(); + } + return TRUE; +} + +// ---------- Перечень портов ---------- +SERIAL_API +int __cdecl Serial_ListPortsLength() { + std::wstring s = EnumerateComPortsJoined(); + return static_cast(s.size() + 1); +} + +SERIAL_API +int __cdecl Serial_ListPorts(wchar_t* buffer, int bufferChars) { + std::wstring s = EnumerateComPortsJoined(); + int need = static_cast(s.size() + 1); + if (!buffer || bufferChars <= 0) return need; + if (bufferChars < need) return -1; + wcsncpy_s(buffer, bufferChars, s.c_str(), _TRUNCATE); + return need; +} + +// ---------- Работа с портом ---------- +SERIAL_API +int __cdecl Serial_Open(const wchar_t* portName, + int baudRate, int dataBits, int parity, int stopBits, + int readTimeoutMs, int writeTimeoutMs) +{ + if (!portName || !*portName) { gLastError = L"Serial_Open: empty portName"; return 0; } + + std::wstring dev = BuildDevicePath(portName); + + // Retry-цикл, лечит ACCESS_DENIED/SHARING_VIOLATION сразу после Alt+F4 + const int kMaxTries = 20; // до ~2 сек + const DWORD kSleepMs = 100; + HANDLE h = INVALID_HANDLE_VALUE; + DWORD lastErr = 0; + + for (int i = 0; i < kMaxTries; ++i) { + h = CreateFileW(dev.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (h != INVALID_HANDLE_VALUE) break; + + lastErr = GetLastError(); + if (lastErr == ERROR_ACCESS_DENIED || lastErr == ERROR_SHARING_VIOLATION) { + Sleep(kSleepMs); + continue; + } + else { + break; + } + } + if (h == INVALID_HANDLE_VALUE) { SetLastErrFromWin32(L"CreateFileW", lastErr); return 0; } + + SetupComm(h, 1 << 15, 1 << 15); // 32KB RX/TX + + // DCB + DCB dcb{}; dcb.DCBlength = sizeof(DCB); + if (!GetCommState(h, &dcb)) { SetLastErrFromWin32(L"GetCommState"); CloseHandle(h); return 0; } + dcb.BaudRate = static_cast(baudRate); + dcb.ByteSize = static_cast(dataBits > 0 ? dataBits : 8); + dcb.fBinary = TRUE; + dcb.fParity = (parity != 0); + + switch (parity) { + case 0: dcb.Parity = NOPARITY; break; + case 1: dcb.Parity = ODDPARITY; break; + case 2: dcb.Parity = EVENPARITY; break; + case 3: dcb.Parity = MARKPARITY; break; + case 4: dcb.Parity = SPACEPARITY;break; + default: dcb.Parity = NOPARITY; break; + } + dcb.StopBits = (stopBits == 2) ? TWOSTOPBITS : ONESTOPBIT; + dcb.fDtrControl = DTR_CONTROL_DISABLE; // как раньше + dcb.fRtsControl = RTS_CONTROL_DISABLE; // как раньше + + if (!SetCommState(h, &dcb)) { SetLastErrFromWin32(L"SetCommState"); CloseHandle(h); return 0; } + + // Таймауты + COMMTIMEOUTS to{}; + to.ReadIntervalTimeout = 50; + to.ReadTotalTimeoutConstant = (readTimeoutMs >= 0) ? static_cast(readTimeoutMs) : 0; + to.ReadTotalTimeoutMultiplier = 0; + to.WriteTotalTimeoutConstant = (writeTimeoutMs >= 0) ? static_cast(writeTimeoutMs) : 0; + to.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts(h, &to)) { SetLastErrFromWin32(L"SetCommTimeouts"); CloseHandle(h); return 0; } + + // Полная очистка очередей на старте + PurgeComm(h, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); + + // Регистрируем handle + { + std::lock_guard lk(gMutex); + int id = gNextId++; + gHandles[id] = h; + return id; + } +} + +SERIAL_API +int __cdecl Serial_Close(int handleId) { + std::lock_guard lk(gMutex); + auto it = gHandles.find(handleId); + if (it == gHandles.end()) { gLastError = L"Serial_Close: invalid handleId"; return 0; } + HANDLE h = it->second; + gHandles.erase(it); + if (h && h != INVALID_HANDLE_VALUE) { CloseHandle(h); return 1; } + return 0; +} + +SERIAL_API +int __cdecl Serial_CloseAll() { + std::lock_guard lk(gMutex); + int closed = 0; + for (auto& kv : gHandles) { + HANDLE h = kv.second; + if (h && h != INVALID_HANDLE_VALUE) { CloseHandle(h); ++closed; } + } + gHandles.clear(); + return closed; +} + +// ---------- Запись ---------- +SERIAL_API +int __cdecl Serial_WriteStringUTF8(int handleId, const char* utf8, int appendNewline) { + if (!utf8) { gLastError = L"Serial_WriteStringUTF8: utf8 is null"; return -1; } + HANDLE h = GetHandle(handleId); + if (!h) { gLastError = L"Serial_WriteStringUTF8: invalid handleId"; return -1; } + + const uint8_t* bytes = reinterpret_cast(utf8); + int len = static_cast(strlen(utf8)); + + int total = 0; + int w = WriteBytes(h, bytes, len); + if (w < 0) return -1; + total += w; + + if (appendNewline) { + const uint8_t nl = '\n'; + w = WriteBytes(h, &nl, 1); + if (w < 0) return -1; + total += w; + } + return total; +} + +// ---------- Чтение ---------- +SERIAL_API +int __cdecl Serial_ReadLineUTF8(int handleId, char* buffer, int bufferSize) { + HANDLE h = GetHandle(handleId); + if (!h) { gLastError = L"handle incorrect"; return -1; } + if (buffer == nullptr) { gLastError = L"Serial_ReadLineUTF8: buffer is null"; return -1; } + if (bufferSize <= 1) { + gLastError = L"Serial_ReadLineUTF8: bufferSize too small (<=1). Got " + std::to_wstring(bufferSize); + buffer[0] = 0; return -1; + } + + int pos = 0; + DWORD read = 0; + char ch = 0; + + while (pos < bufferSize - 1) { + BOOL ok = ReadFile(h, &ch, 1, &read, nullptr); + if (!ok) { SetLastErrFromWin32(L"ReadFile"); return -1; } + if (read == 0) { // таймаут + if (pos == 0) { buffer[0] = 0; return 0; } + break; + } + if (ch == '\n') break; + if (ch != '\r') buffer[pos++] = ch; + } + + if (pos >= bufferSize - 1) { + buffer[bufferSize - 1] = 0; + return -2; // усечено + } + + buffer[pos] = 0; + return pos; +} + +// ---------- Диагностика ---------- +SERIAL_API +int __cdecl Serial_GetLastErrorW(wchar_t* buffer, int bufferChars) { + int need = static_cast(gLastError.size() + 1); + if (!buffer || bufferChars <= 0) return need; + if (bufferChars < need) return -1; + wcsncpy_s(buffer, bufferChars, gLastError.c_str(), _TRUNCATE); + return need; +} diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h new file mode 100644 index 0000000..8cb17e7 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h @@ -0,0 +1,29 @@ +// SerialNative.h +#pragma once +#include + +#ifdef _WIN32 + #define SERIAL_API extern "C" __declspec(dllexport) +#else + #define SERIAL_API extern "C" +#endif + +// --- Перечень портов --- +SERIAL_API int __cdecl Serial_ListPortsLength(); +SERIAL_API int __cdecl Serial_ListPorts(wchar_t* buffer, int bufferChars); + +// --- Работа с портом --- +SERIAL_API int __cdecl Serial_Open(const wchar_t* portName, + int baudRate, int dataBits, int parity, int stopBits, + int readTimeoutMs, int writeTimeoutMs); +SERIAL_API int __cdecl Serial_Close(int handleId); +SERIAL_API int __cdecl Serial_CloseAll(); // NEW: глобальная зачистка хэндлов + +// --- Запись (оставили только строковую; байтовую убрали из экспорта) --- +SERIAL_API int __cdecl Serial_WriteStringUTF8(int handleId, const char* utf8, int appendNewline); + +// --- Чтение (оставили только построчное) --- +SERIAL_API int __cdecl Serial_ReadLineUTF8(int handleId, char* buffer, int bufferSize); + +// --- Диагностика --- +SERIAL_API int __cdecl Serial_GetLastErrorW(wchar_t* buffer, int bufferChars); diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp new file mode 100644 index 0000000..f840505 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Определяет точку входа для приложения DLL. +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/framework.h b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/framework.h new file mode 100644 index 0000000..61ca0b6 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Исключите редко используемые компоненты из заголовков Windows +// Файлы заголовков Windows +#include diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp new file mode 100644 index 0000000..9211a5e --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: файл исходного кода, соответствующий предварительно скомпилированному заголовочному файлу + +#include "pch.h" + +// При использовании предварительно скомпилированных заголовочных файлов необходим следующий файл исходного кода для выполнения сборки. diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.h b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.h new file mode 100644 index 0000000..42d0757 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/pch.h @@ -0,0 +1,13 @@ +// pch.h: это предварительно скомпилированный заголовочный файл. +// Перечисленные ниже файлы компилируются только один раз, что ускоряет последующие сборки. +// Это также влияет на работу IntelliSense, включая многие функции просмотра и завершения кода. +// Однако изменение любого из приведенных здесь файлов между операциями сборки приведет к повторной компиляции всех(!) этих файлов. +// Не добавляйте сюда файлы, которые планируете часто изменять, так как в этом случае выигрыша в производительности не будет. + +#ifndef PCH_H +#define PCH_H + +// Добавьте сюда заголовочные файлы для предварительной компиляции +#include "framework.h" + +#endif //PCH_H diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe new file mode 100644 index 0000000..c4fa5f5 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe @@ -0,0 +1,11 @@ + + + + + C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.dll + + + + + + \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj new file mode 100644 index 0000000..0a544c0 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb new file mode 100644 index 0000000..9272437 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log new file mode 100644 index 0000000..8898563 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log @@ -0,0 +1,8 @@ + SerialNative.cpp + Создается библиотека C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.lib и объект C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.exp + Создание кода + 1 of 376 functions ( 0.3%) were compiled, the rest were copied from previous compilation. + 0 functions were new in current compilation + 0 functions had inline decision re-evaluated but remain unchanged + Создание кода завершено + ComNative.vcxproj -> C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.dll diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch new file mode 100644 index 0000000..1beffe4 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog new file mode 100644 index 0000000..558e29a Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog new file mode 100644 index 0000000..9272a88 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog new file mode 100644 index 0000000..17fb5c0 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog new file mode 100644 index 0000000..21e6b56 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog @@ -0,0 +1,2 @@ +C:\Users\Programmer\source\repos\ComNative\ComNative\SerialNative.cpp;C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\SerialNative.obj +C:\Users\Programmer\source\repos\ComNative\ComNative\pch.cpp;C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\pch.obj diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate new file mode 100644 index 0000000..135a445 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.44.35207:TargetPlatformVersion=10.0.26100.0: +Release|x64|C:\Users\Programmer\source\repos\ComNative\| diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog new file mode 100644 index 0000000..23eaf57 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog new file mode 100644 index 0000000..e447ff8 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog new file mode 100644 index 0000000..f0e2c69 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog @@ -0,0 +1,5 @@ +^C:\USERS\PROGRAMMER\SOURCE\REPOS\COMNATIVE\COMNATIVE\X64\RELEASE\PCH.OBJ|C:\USERS\PROGRAMMER\SOURCE\REPOS\COMNATIVE\COMNATIVE\X64\RELEASE\SERIALNATIVE.OBJ +C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.lib +C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.EXP +C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\ComNative.IPDB +C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\ComNative.iobj diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog new file mode 100644 index 0000000..02688e0 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj new file mode 100644 index 0000000..32697bc Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj new file mode 100644 index 0000000..60b69c1 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj new file mode 100644 index 0000000..ae1e334 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb new file mode 100644 index 0000000..bae206d Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll new file mode 100644 index 0000000..e0f6d6b Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp new file mode 100644 index 0000000..ccb093f Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib new file mode 100644 index 0000000..ba0439c Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib differ diff --git a/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb new file mode 100644 index 0000000..508efef Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb differ diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Plugins/x86_64/ComNative.dll b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Plugins/x86_64/ComNative.dll new file mode 100644 index 0000000..e0f6d6b Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Plugins/x86_64/ComNative.dll differ diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ButtonPinMapping.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ButtonPinMapping.cs new file mode 100644 index 0000000..9b5e564 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ButtonPinMapping.cs @@ -0,0 +1,43 @@ +/// +/// Соответствие между кнопками игрового ввода и цифровыми выходами периферийной платы (пины 0-15). +/// Содержит mapping для управления двух игроков, служебных кнопок и датчиков хопера. +/// +public enum ButtonPinMapping +{ + /// Кнопка движения вверх для первого игрока + up1 = 0, + /// Кнопка движения вправо для первого игрока + right1 = 1, + /// Кнопка движения вниз для первого игрока + down1 = 2, + /// Кнопка движения влево для первого игрока + left1 = 3, + /// Действие/огонь для первого игрока + action1 = 4, + + /// Кнопка движения вверх для второго игрока + up2 = 5, + /// Кнопка движения вправо для второго игрока + right2 = 6, + /// Кнопка движения вниз для второго игрока + down2 = 7, + /// Кнопка движения влево для второго игрока + left2 = 8, + /// Действие/огонь для второго игрока + action2 = 9, + + /// Запуск игры для одного игрока + startOnePlayer = 10, + /// Запуск игры для двух игроков + startTwoPlayers = 11, + /// Секретная/служебная кнопка для захода в настройки + secret = 12, + + /// Дополнительная/резервная кнопка + extra = 13, + + /// Датчик наличия приза в хопере + hasPrize = 14, + /// Датчик выдачи приза из хопера + prizeGiven = 15 +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/COMinput.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/COMinput.cs new file mode 100644 index 0000000..d4e3694 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/COMinput.cs @@ -0,0 +1,253 @@ +using System; +using UnityEngine; + +public class COMinput : InputSource +{ + //Количество = цифровые + 2 датчика. + //На момент написания количество цифровых входов 7*N. + //Настроить количество пинов под свою настройку платы + private const int PIN_COUNT = 16; + + private const char POSITIVE = '0'; + private const char NEGATIVE = '1'; + + private bool[] buttonState = new bool[PIN_COUNT]; + private bool[] buttonDown = new bool[PIN_COUNT]; + private bool[] buttonUp = new bool[PIN_COUNT]; + + //Свойство для определения текущего игрока + //Должно возвращать 1 или 2 + //Используется для получения состояния кнопок с пометкой ...CurrentPlayer + private int currentPlayer => gm.currentPlayer == CellState.Player1 ? 1 : 2;//ПЕРЕОПРЕДЕЛИТЕ ОПРЕДЕЛЕНИЕ ТЕКУЩЕГО ИГРОКА ПОД СВОЮ СИСТЕМУ. + private GameManager gm;//я получаю кто сейчас ходит через этого менеджера. Если у вас другое, допишите. + + public override bool Initialize() + { + try + { + gm = GameManager.instance; + + //ОБЯЗАТЕЛЬНЫЙ БЛОК + ComWrapper.onInputRecieved.AddListener(UpdateInputState); + + } + catch (Exception e) + { + Debug.Log($"Что то в COMinput {e.ToString()}"); + return false; + } + return true; + } + + #region PUBLIC_API + + //ЭТА ГРУППА МЕТОДОВ ДЛЯ ПОЛУЧЕНИЯ СОСТОЯНИЯ ЛЮБОЙ КНОПКИ ПО ПИНУ (enum ButtonPinMapping) + + /// + /// Получает состояние конкретной кнопки по её физическому пину. + /// Возвращает true если кнопка в данный момент нажата. + /// + /// Пин кнопки на периферийной плате + /// True если кнопка нажата в текущем кадре + public override bool GetButton(ButtonPinMapping button) => buttonState[(int)button]; + + /// + /// Проверяет, была ли кнопка нажата в начале текущего кадра. + /// Удерживает true до тех пор, пока кто то не проверит, затем сбрасывает. + /// + /// Пин кнопки на периферийной плате + /// True если кнопка была нажата в этом кадре + public override bool GetButtonDown(ButtonPinMapping button) => ConsumeButtonEvent(buttonDown, button); + + /// + /// Проверяет, была ли кнопка отпущена в начале текущего кадра. + /// Удерживает true до тех пор, пока кто то не проверит, затем сбрасывает. + /// + /// Пин кнопки на периферийной плате + /// True если кнопка была отпущена в этом кадре + public override bool GetButtonUp(ButtonPinMapping button) => ConsumeButtonEvent(buttonUp, button); + + + /// + /// Получает состояние кнопки по числовому индексу пина напрямую из буфера состояний. + /// Обходит перечисление ButtonPinMapping для прямого доступа к пинам по индексу. + /// + /// Числовой индекс пина (0-15) + /// true если кнопка нажата в текущий момент + /// + /// Используется когда необходим прямой доступ к пинам без приведения к перечислению. + /// Подходит для сценариев где количество пинов может превышать размер перечисления. + /// + public override bool GetButton(int pinIndex) => buttonState[pinIndex]; + + /// + /// Проверяет, была ли кнопка нажата в начале текущего кадра по числовому индексу пина. + /// Возвращает true только в первом кадре после нажатия и сбрасывает флаг события. + /// + /// Числовой индекс пина (0-15) + /// true если кнопка была нажата в этом кадре + /// + /// Метод "потребляет" событие - после чтения флаг сбрасывается. + /// Использует общий механизм ConsumeButtonEvent для обработки одноразовых событий. + /// + public override bool GetButtonDown(int pinIndex) => ConsumeButtonEvent(buttonDown, pinIndex); + + /// + /// Проверяет, была ли кнопка отпущена в начале текущего кадра по числовому индексу пина. + /// Возвращает true только в первом кадре после отпускания и сбрасывает флаг события. + /// + /// Числовой индекс пина (0-15) + /// true если кнопка была отпущена в этом кадре + /// + /// Метод "потребляет" событие - после чтения флаг сбрасывается. + /// Использует общий механизм ConsumeButtonEvent для обработки одноразовых событий. + /// + public override bool GetButtonUp(int pinIndex) => ConsumeButtonEvent(buttonUp, pinIndex); + // ------------------------------------------------------------------------------------ + //ЭТА ГРУППА МЕТОДОВ ПОЗВОЛЯЕТ УЗНАТЬ СОСТОЯНИЯ КНОПОК АКТИВНОГО ИГРОКА (проверка по enum PlayerAction) + //Служебные кнопки типа startOnePlayer, startTwoPlayers, secret, extra, hasPrize, prizeGiven здесь не проверяются + + /// + /// Получает состояние действия для текущего игрока. + /// Возвращает true если действие активно (кнопка нажата) у текущего игрока. + /// + /// Действие игрока (up, down, left, right, action, extra) + /// True если действие активно у текущего игрока + public override bool GetButtonCurrentPlayer(PlayerAction button) + => GetButton(GetButtonForCurrentPlayer(button)); + + /// + /// Проверяет, было ли действие начато текущим игроком в этом кадре. + /// Возвращает true только в первом кадре после начала действия. + /// + /// Действие игрока + /// True если действие было начато в этом кадре + public override bool GetButtonDownCurrentPlayer(PlayerAction button) + => GetButtonDown(GetButtonForCurrentPlayer(button)); + + /// + /// Проверяет, было ли действие завершено текущим игроком в этом кадре. + /// Возвращает true только в первом кадре после завершения действия. + /// + /// Действие игрока + /// True если действие было завершено в этом кадре + public override bool GetButtonUpCurrentPlayer(PlayerAction button) + => GetButtonUp(GetButtonForCurrentPlayer(button)); + + // ------------------------------------------------------------------------------------ + //ЭТА ГРУППА МЕТОДОВ ПОЗВОЛЯЕТ УЗНАТЬ СОСТОЯНИЯ КНОПОК ЛЮБОГО ИГРОКА (проверка по enum PlayerAction) + //Служебные кнопки типа startOnePlayer, startTwoPlayers, secret, extra, hasPrize, prizeGiven здесь не проверяются + + /// + /// Получает состояние действия для любого из игроков. + /// Возвращает true если действие активно у первого ИЛИ второго игрока. + /// + /// Действие игрока + /// True если действие активно у любого игрока + public override bool GetButtonAnyPlayer(PlayerAction button) + => CheckBothPlayers(button, GetButton); + + /// + /// Проверяет, было ли действие начато любым игроком в этом кадре. + /// Возвращает true если первый ИЛИ второй игрок начали действие в этом кадре. + /// + /// Действие игрока + /// True если действие было начато любым игроком в этом кадре + public override bool GetButtonDownAnyPlayer(PlayerAction button) + => CheckBothPlayers(button, GetButtonDown); + + /// + /// Проверяет, было ли действие завершено любым игроком в этом кадре. + /// Возвращает true если первый ИЛИ второй игрок завершили действие в этом кадре. + /// + /// Действие игрока + /// True если действие было завершено любым игроком в этом кадре + public override bool GetButtonUpAnyPlayer(PlayerAction button) + => CheckBothPlayers(button, GetButtonUp); + #endregion + + + //==========ХЕЛПЕРЫ========== + // Проверка нажатия с обоих контроллеров игроков + private bool CheckBothPlayers(PlayerAction action, Func buttonFunc) + { + var buttons = _buttonByAction(action); + return buttonFunc(buttons.player1button) || buttonFunc(buttons.player2button); + } + + // Проверка нажатия с контроллера текущего игрока + private ButtonPinMapping GetButtonForCurrentPlayer(PlayerAction button) + { + var buttons = _buttonByAction(button); + return currentPlayer == 1 ? buttons.player1button : buttons.player2button; + } + + // Маппер из действий игрока => пины платы + private (ButtonPinMapping player1button, ButtonPinMapping player2button) _buttonByAction(PlayerAction playerAction) => + playerAction switch + { + PlayerAction.up => (ButtonPinMapping.up1, ButtonPinMapping.up2), + PlayerAction.down => (ButtonPinMapping.down1, ButtonPinMapping.down2), + PlayerAction.left => (ButtonPinMapping.left1, ButtonPinMapping.left2), + PlayerAction.right => (ButtonPinMapping.right1, ButtonPinMapping.right2), + PlayerAction.action => (ButtonPinMapping.action1, ButtonPinMapping.action2), + PlayerAction.extra => (ButtonPinMapping.extra, ButtonPinMapping.extra), // одинаковая кнопка + _ => throw new ArgumentException($"Unknown player action: {playerAction}") + + }; + + // Обновляет состояния инпута когда приходит новый ввод с платы. Дергается событием. + private void UpdateInputState(string input) + { + // Будем принимать в нативном формате by Pasha. 0 - нажато 1 - не нажато (чтобы преобразовывать циклом) + // В параметрприходит строка вида "1111011111011111" + for (int i = 0; i < input.Length; i++) + { + // Получаем новое состояние пина + bool newState = input[i] == POSITIVE; + + // Состояние пина изменилось => пишем в буферы + if (newState != buttonState[i]) + { + if(newState) // Пин был зажат + { + buttonDown[i] = true; + buttonUp[i] = false; + } + else // Пин был отжат + { + buttonDown[i] = false; + buttonUp[i] = true; + } + } + + buttonState[i] = newState; + + } + if (input[^1] == POSITIVE) PlayerPrefs.SetInt("PrizeGivenBuffer", 1); //пишем в буфер чтобы между кадрами и порядком вызова не проебать + + DebugWindow.Message("Инпуты: " + string.Join("", buttonState));//можно вырезать + } + + // Потребление "Нажатия"/"Отжатия". Возвращает что там было и чистит флаг из буфера + private bool ConsumeButtonEvent(bool[] buffer, ButtonPinMapping button) + { + bool result = buffer[(int)button]; + buffer[(int)button] = false; + return result; + } + + /// + /// Вспомогательный метод для обработки одноразовых событий кнопок по числовому индексу. + /// Возвращает текущее значение флага и сбрасывает его для предотвращения повторной обработки. + /// + /// Буфер событий (buttonDown или buttonUp) + /// Числовой индекс пина + /// Состояние флага события до сброса + private bool ConsumeButtonEvent(bool[] buffer, int pinIndex) + { + bool result = buffer[pinIndex]; + buffer[pinIndex] = false; + return result; + } +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComNativeWrapper.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComNativeWrapper.cs new file mode 100644 index 0000000..eea135e --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComNativeWrapper.cs @@ -0,0 +1,57 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; + +public static class ComNativeWrapper +{ + private const string Dll = "ComNative"; // имя файла в Plugins/x86_64 без .dll + + // --- Порты (Unicode) --- + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_ListPortsLength(); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_ListPorts(StringBuilder buffer, int bufferChars); + + // --- Открытие/закрытие --- + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_Open(string portName, int baud, int dataBits, int parity, int stopBits, + int readTimeoutMs, int writeTimeoutMs); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Serial_Close(int handleId); + + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] + public static extern int Serial_CloseAll(); // NEW + + // --- Запись/чтение (строкой) --- + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] + public static extern int Serial_WriteStringUTF8(int handleId, string utf8, int appendNewline); + + // byte[]-вариант к той же native-функции (EntryPoint), чтобы избежать маршаллинга StringBuilder + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Serial_ReadLineUTF8")] + public static extern int Serial_ReadLineUTF8_bytes(int handleId, byte[] buffer, int bufferSize); + + // --- Ошибка (Unicode) --- + [DllImport(Dll, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)] + public static extern int Serial_GetLastErrorW(StringBuilder buffer, int bufferChars); + + public static string GetLastError() + { + int need = Serial_GetLastErrorW(null, 0); + var sb = new StringBuilder(Math.Max(need, 1)); + int r = Serial_GetLastErrorW(sb, sb.Capacity); + return (r < 0) ? $"err({r})" : sb.ToString(); + } + + public static string[] ListPorts() + { + int need = Serial_ListPortsLength(); + if (need <= 1) return Array.Empty(); + var sb = new StringBuilder(need); + int r = Serial_ListPorts(sb, sb.Capacity); + if (r < 0) return Array.Empty(); + string joined = sb.ToString(); + return string.IsNullOrWhiteSpace(joined) ? Array.Empty() : joined.Split(';'); + } +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs new file mode 100644 index 0000000..316df6c --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/ComWrapper.cs @@ -0,0 +1,262 @@ +using System.Text; +using UnityEngine; +using UnityEngine.Events; + +/// +/// Основной класс-обертка для работы с COM-портом и периферийной платой. +/// Управляет всей коммуникацией с аппаратным обеспечением: прием монет, чтение кнопок, управление подсветкой, отправка сигналов. +/// +public class ComWrapper : MonoBehaviour +{ + //Настраиваем из инспектора + [SerializeField] bool saveMoneyBetweenSessions; + [SerializeField] bool enablePrizeChecking = false; // Если хопер с проверкой наличия приза ставим true + + + public static UnityEvent onCoinAdded; + public static UnityEvent onCoinRemoved; + public static UnityEvent onInputRecieved; + + #region Приватные поля + private const string REQUEST_INPUT = "91"; + private const string DEFAULT_INPUT = "INPUT1111111111111111"; + private const string LIGHTSIGNAL_PREFIX = "80"; + + private static int h;//идентификатор хэндла + private static byte[] buf = new byte[1024]; //Байт-буфер куда будут приходить данные с порта + + private static int CreditsCount; + private string inputString = DEFAULT_INPUT; + #endregion + + //===========================start: PUBLIC API===========================// + + /// + /// Инициализирует систему работы с COM-портом. + /// Открывает соединение, настраивает события и запускает периодические запросы состояния. + /// + /// + /// Обязателен к вызову при старте приложения. Выполняет: + /// - Создание UnityEvent'ов для уведомлений + /// - Загрузку количества кредитов из PlayerPrefs + /// - Открытие COM-порта + /// - Запуск периодического опроса пинов (если включена проверка призов) + /// + public void Initialize() + { + //Создаем ивенты + onCoinAdded = new UnityEvent(); + onCoinRemoved = new UnityEvent(); + + //Начальная настройка + if (!saveMoneyBetweenSessions) PlayerPrefs.SetInt("CoinsCount", 0); + CreditsCount = PlayerPrefs.GetInt("CoinsCount", 0); + initPort(); //вот это важный метод который собственно порт открывает. + + //Каждые 5 сек запрашиваем инпуты у платы. Надо потому что при заходе в игру с пинов еще не пришла инфа + if (enablePrizeChecking) InvokeRepeating("requestInputs", 0, 5); + } + + /// + /// Добавляет один кредит в систему и сохраняет обновленное количество. + /// Вызывается автоматически событием при получении сигнала "COIN" из COM-порта. + /// + /// + /// Увеличивает счетчик кредитов и вызывает событие onCoinAdded для уведомления подписчиков. + /// Автоматически сохраняет состояние в PlayerPrefs. + /// + public static void AddCredit() + { + CreditsCount++; + PlayerPrefs.SetInt("CoinsCount", CreditsCount); + onCoinAdded?.Invoke(CreditsCount); + } + + /// + /// Отправляет произвольную строковую команду на периферийную плату через COM-порт. + /// + /// Команда для отправки + /// + /// Используется для отправки пользовательских команд на плату. + /// Команда должна быть в формате, ожидаемом прошивкой платы. + /// В случае ошибки отправки выводит предупреждение в DebugWindow. + /// + public static void Send(string message) + { + try + { + int w = ComNativeWrapper.Serial_WriteStringUTF8(h, message, 1); //w-количество записаных символов в порт. можно обработать + //Debug.Log("Wrote: " + w); + + } + catch { DebugWindow.Message("Ошибка отправки строкового сигнала."); } + } + + /// + /// Отправляет команду управления подсветкой на периферийную плату. + /// + /// Режим подсветки из перечисления LightMode + /// + /// Формирует команду вида "80XXX" где XXX - код режима подсветки. + /// Для добавления новых режимов необходимо обновить вложенный метод getLightString + добавить режим в перечисление. + /// + public static void Send(LightMode lightMode) + { + try + { + string signal = LIGHTSIGNAL_PREFIX + getLightString(lightMode); + Send(signal); + + //ВОТЭТОТ МЕТОД ПОД СВОИ ПОДСВЕТКИ НАДО ПОДГОНЯТЬ + string getLightString(LightMode lightMode) => + + lightMode switch + { + LightMode.Idle => "106", + LightMode.Orange => "107", + LightMode.Blue => "108", + LightMode.Win => "109", + LightMode.Lose => "110", + LightMode.Prize => "111", + _ => "106" + }; + + } + catch { DebugWindow.Message("Ошибка отправки светового сигнала."); } + } + + /// + /// Выполняет списание платы за запуск игры. + /// Проверяет наличие доступных кредитов или режим бесплатной игры. + /// При успешном списании увеличивает счетчики запусков. + /// + /// + /// true - если списание прошло успешно (бесплатный режим или есть кредиты), + /// false - если кредитов недостаточно и платный режим + /// + /// + /// В случае успеха: + /// - Увеличивает общие счетчики запусков (key_starts и key_startsNul) + /// - При платном режиме уменьшает количество кредитов на 1 + /// - Вызывает событие onCoinRemoved при списании кредита + /// + public static bool Pay() + { + //блок проверки оплаты + if (SettingsManager.instance.GetSettingValue("Бесплатная игра") == "Вкл") + { + //Счетчики пробега + PlayerPrefs.SetInt(PrizeSystem.key_starts, PlayerPrefs.GetInt(PrizeSystem.key_starts) + 1); + PlayerPrefs.SetInt(PrizeSystem.key_startsNul, PlayerPrefs.GetInt(PrizeSystem.key_startsNul) + 1); + + return true; + } + + if (CreditsCount > 0) + { + CreditsCount--; + 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); + + onCoinRemoved?.Invoke(CreditsCount); + return true; + } + + return false; + } + //===========================end: PUBLIC API===========================// + + + + + private void Update() => pollPort(); + private void OnDisable() => ClosePort(); + private void OnApplicationQuit() => ClosePort(); + + + #region Приватные методы + //Ну тут по дебаг логам все должно быть понятно + //Если интересно, то h - это хэндл. Что то на плюсовом, типа ID соединения или чтото. Если 0 то нету соед. если меньше то ошибка, больше 0 - норм. + private void initPort() + { + Debug.Log("Старт программы"); + ComNativeWrapper.Serial_CloseAll(); + var ports = ComNativeWrapper.ListPorts(); + Debug.Log("Ports: " + string.Join(", ", ports)); + if (ports.Length == 0) { Debug.Log("Нет портов, завершаю"); return; } + + string port = ports[^1]; + Debug.Log("Открываю первый с конца порт: " + port); + + + //вот эти настройки лучше не ковырять, но если очень хочется можно поменять readTimeoutMs и writeTimeoutMs + //Если read поставить слишком маленьким оно проебует кредиты когда они идут большой пачкой например при оплате через вендисту. + //Там в порт спамит "COIN" в больших количествах и процентов 10 из этих строк не успевают прочитаться при таймауте 1мс, соотв. монеты проебуются. + //Слишком большой таймаут будет тормозить игру потому что оно все выполняется синхронно и каждый кадр забирает столько мс сколько в переменной указано. + h = ComNativeWrapper.Serial_Open(port, 115200, 8, 0, 1, 5, 10); + + + Debug.Log($"handle: {h}"); + if (h <= 0) + { + Debug.Log("Open failed: " + ComNativeWrapper.GetLastError()); + return; + } + } + + private static void ClosePort() + { + if (h > 0) + { + ComNativeWrapper.Serial_Close(h); + h = 0; + } + } + + + //Тут чтение, не трогаем(опционально) + private static string ReadBufferLine() + { + + int read = ComNativeWrapper.Serial_ReadLineUTF8_bytes(h, buf, buf.Length); + if (read > 0) + { + var s = Encoding.UTF8.GetString(buf, 0, read); + //Debug.Log($"ReadLine len={read} data=[{s}]"); + return s; + } + else if (read < 0) + { + Debug.Log($"ReadLine err={read} :: {ComNativeWrapper.GetLastError()}"); + } + return ""; + } + //просим прислать состояние цифровых входов. Приходит что то вроде "INPUT111111101" + private void requestInputs() => Send(REQUEST_INPUT); + + private void pollPort() + { + //Тут все просто, читаем что нам приходит с порта и сравниваем. + string comInput = ReadBufferLine(); + if (comInput != "") + { + DebugWindow.Message("COM: " + comInput); + + if (comInput == "COIN") AddCredit(); + if (comInput.StartsWith("INPUT")) onInputRecieved?.Invoke(comInput.Substring(5)); //инвокаю сразу без префикса "INPUT" + } + } + #endregion +} +public enum LightMode +{ + Idle, + Orange, + Blue, + Win, + Lose, + Prize, +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/PrizeSystem.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/PrizeSystem.cs new file mode 100644 index 0000000..d4d3ab4 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/ComSystem/PrizeSystem.cs @@ -0,0 +1,114 @@ +using System.Collections; +using UnityEngine; + +/// +/// Система управления выдачей призов и ведения статистики игры. +/// Отвечает за процесс выдачи призов, подсчет запусков и наград. +/// +public class PrizeSystem : MonoBehaviour +{ + [SerializeField] AudioClip prizeErrorSound; + + public static string key_starts = "STARTS"; + public static string key_startsNul = "STARTSNUL"; + public static string key_prizesgiven = "PRIZESGIVEN"; + public static string key_prizesgivenNul = "PRIZESGIVENNUL"; + + private static PrizeSystem instance; + private static Coroutine prizeCoroutine; + private static bool prizing = false; + + + //===========================start: PUBLIC API===========================// + /// + /// Инициализирует систему призов и счетчики статистики. + /// Должен быть вызван один раз при запуске приложения. + /// + /// + /// Выполняет: + /// - Установку статического экземпляра (singleton) + /// - Инициализацию счетчиков в PlayerPrefs если они отсутствуют + /// - Подготовку системы к работе + /// + public void Initialize() + { + instance = this; + InitCounters(); + } + + /// + /// Запускает процесс выдачи физического приза игроку. + /// Проверяет настройки и текущее состояние перед началом выдачи. + /// + /// + /// Условия для выдачи приза: + /// - В настройках включена выдача призов при игре с ботом + /// - В данный момент не выполняется другая выдача приза + /// + /// Процесс выдачи: + /// - Включает мотор хопера + /// - Ожидает подтверждения выдачи от датчиков (10 сек таймаут) + /// - Выключает мотор по завершению + /// - Обновляет счетчики выданных призов + /// - Воспроизводит звук ошибки при неудаче + /// + public static void GivePrize() + { + bool settingEnabled = SettingsManager.instance.GetSettingValue("Выдача призов при игре с ботом") == "Выдавать"; + + if (!settingEnabled) { DebugWindow.Message("Выдача приза отключена в настройках"); return; } + if (prizing) { DebugWindow.Message("Уже идет выдача приза. Не спамь кнопку."); return; } + + DebugWindow.Message("Выдаю приз"); + if (prizeCoroutine != null) instance.StopCoroutine(prizeCoroutine); + prizeCoroutine = instance.StartCoroutine(instance.Prize()); + } + //===========================end: PUBLIC API===========================// + + #region Приватные методы + private IEnumerator Prize() + { + prizing = true; + DebugWindow.Message("Начинаю выдачу"); + + float timeout = 10; //сек + ComWrapper.Send("301");//включаем мотор + float startTime = Time.time; + bool success = false; + + PlayerPrefs.SetInt("PrizeGivenBuffer", 0);//На всякий случай подготовка буфера + //проверить где он пишется + + while (Time.time - startTime < timeout) + { + if (PlayerPrefs.GetInt("PrizeGivenBuffer", 0) == 1) + { + success = true; + IncreasePrizeCounters(); + break; + } + yield return null; + } + + //Выключаем мотор + ComWrapper.Send("300"); + DebugWindow.Message("Закончил. Успех: " + success); + prizing = false; + + if (!success) SoundManager.instance.playClip(prizeErrorSound); + } + + void IncreasePrizeCounters() + { + PlayerPrefs.SetInt(key_prizesgiven, PlayerPrefs.GetInt(key_prizesgiven) + 1); + PlayerPrefs.SetInt(key_prizesgivenNul, PlayerPrefs.GetInt(key_prizesgivenNul) + 1); + } + void InitCounters() + { + if (!PlayerPrefs.HasKey(key_starts)) PlayerPrefs.SetInt(key_starts, 0); + if (!PlayerPrefs.HasKey(key_startsNul)) PlayerPrefs.SetInt(key_startsNul, 0); + if (!PlayerPrefs.HasKey(key_prizesgiven)) PlayerPrefs.SetInt(key_prizesgiven, 0); + if (!PlayerPrefs.HasKey(key_prizesgivenNul)) PlayerPrefs.SetInt(key_prizesgivenNul, 0); + } + #endregion +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/In.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/In.cs new file mode 100644 index 0000000..db3c29a --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/In.cs @@ -0,0 +1,89 @@ +using UnityEngine; + +/// +/// Статический фасад для унифицированного доступа к системе ввода. +/// Предоставляет глобальный интерфейс для работы с управлением независимо от выбранного источника. +/// +/// +/// Этот класс является точкой входа для всей игровой логики, работающей с вводом. +/// Автоматически инициализирует выбранный источник управления (клавиатура или COM-порт) +/// и предоставляет статические методы для удобного доступа из любого места кода. +/// +/// Использование: +/// 1. Настройте тип ввода в инспекторе +/// 2. Вызовите Initialize() при старте игры +/// 3. Используйте статические методы для проверки ввода +/// +public class In : MonoBehaviour +{ + [Header("Выбор источника управления")] + [SerializeField] private InputSourceType inputType; + + static InputSource source; + + /// + /// Инициализирует систему ввода на основе выбранного типа. + /// Должен быть вызван один раз при запуске приложения. + /// + /// + /// Создает соответствующий экземпляр InputSource: + /// - KeyboardInput для тестирования на клавиатуре + /// - COMinput для работы с периферийной платой + /// + public void Initialize() + { + source = inputType switch + { + InputSourceType.Keyboard => new KeyboardInput(), + InputSourceType.COM => new COMinput(), + _ => throw new System.NotImplementedException() + }; + + var success = source.Initialize(); + if (!success) Debug.LogWarning("Не удалось проинициалзировать источник управления."); + } + + // ================================================================================ + // СТАТИЧЕСКИЕ МЕТОДЫ ДЛЯ РАБОТЫ С ФИЗИЧЕСКИМИ ПИНАМИ + // ================================================================================ + + /// Проверяет состояние конкретной кнопки по её физическому пину + public static bool GetButton(ButtonPinMapping button) => source.GetButton(button); + + /// Проверяет нажатие кнопки по пину в текущем кадре + public static bool GetButtonDown(ButtonPinMapping button) => source.GetButtonDown(button); + + /// Проверяет отпускание кнопки по пину в текущем кадре + public static bool GetButtonUp(ButtonPinMapping button) => source.GetButtonUp(button); + + /// Проверяет состояние кнопки по числовому индексу пина + public static bool GetButton(int pinindex) => source.GetButton(pinindex); + + /// Проверяет нажатие кнопки по индексу пина в текущем кадре + public static bool GetButtonDown(int pinindex) => source.GetButtonDown(pinindex); + + /// Проверяет отпускание кнопки по индексу пина в текущем кадре + public static bool GetButtonUp(int pinindex) => source.GetButtonUp(pinindex); + + // ================================================================================ + // СТАТИЧЕСКИЕ МЕТОДЫ ДЛЯ РАБОТЫ С АБСТРАКТНЫМИ ДЕЙСТВИЯМИ ИГРОКОВ + // ================================================================================ + + /// Проверяет состояние действия для любого из игроков + public static bool GetButtonAnyPlayer(PlayerAction button) => source.GetButtonAnyPlayer(button); + + /// Проверяет начало действия любым игроком в текущем кадре + public static bool GetButtonDownAnyPlayer(PlayerAction button) => source.GetButtonDownAnyPlayer(button); + + /// Проверяет завершение действия любым игроком в текущем кадре + public static bool GetButtonUpAnyPlayer(PlayerAction button) => source.GetButtonUpAnyPlayer(button); + + /// Проверяет состояние действия для текущего активного игрока + public static bool GetButtonCurrentPlayer(PlayerAction button) => source.GetButtonCurrentPlayer(button); + + /// Проверяет начало действия текущим игроком в текущем кадре + public static bool GetButtonDownCurrentPlayer(PlayerAction button) => source.GetButtonDownCurrentPlayer(button); + + /// Проверяет завершение действия текущим игроком в текущем кадре + public static bool GetButtonUpCurrentPlayer(PlayerAction button) => source.GetButtonUpCurrentPlayer(button); +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSource.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSource.cs new file mode 100644 index 0000000..d8d9323 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSource.cs @@ -0,0 +1,117 @@ +/// +/// Абстрактный базовый класс для всех источников ввода в системе. +/// Определяет унифицированный интерфейс для работы с аппаратными кнопками и виртуальными действиями игроков. +/// +/// +/// Наследники этого класса реализуют конкретные способы ввода: +/// - COMinput: работа с периферийной платой через COM-порт +/// - KeyboardInput: эмуляция ввода с клавиатуры для тестирования +/// +/// Класс предоставляет три уровня абстракции: +/// 1. Низкоуровневый доступ к физическим пинам +/// 2. Доступ к абстрактным игровым действиям +/// 3. Мультиплеерные методы для работы с несколькими игроками +/// +public abstract class InputSource +{ + /// + /// Инициализирует источник ввода. Должен быть вызван перед использованием других методов. + /// + /// true если инициализация прошла успешно, false в случае ошибки + public abstract bool Initialize(); + + //---------------------------------------------------------------------------------- + + /// + /// Получает текущее состояние конкретной кнопки по её физическому пину. + /// + /// Пин кнопки на периферийной плате + /// true если кнопка нажата в текущий момент + public abstract bool GetButton(ButtonPinMapping button); + + /// + /// Проверяет, была ли кнопка нажата в начале текущего кадра. + /// + /// Пин кнопки на периферийной плате + /// true только в первом кадре после нажатия кнопки + public abstract bool GetButtonDown(ButtonPinMapping button); + + /// + /// Проверяет, была ли кнопка отпущена в начале текущего кадра. + /// + /// Пин кнопки на периферийной плате + /// true только в первом кадре после отпускания кнопки + public abstract bool GetButtonUp(ButtonPinMapping button); + + //---------------------------------------------------------------------------------- + + /// + /// Получает состояние кнопки по числовому индексу пина. + /// Перегрузка для работы с системами, где количество пинов превышает размер перечисления. + /// + /// Числовой индекс пина (0-based) + /// true если кнопка нажата в текущий момент + public abstract bool GetButton(int pinIndex); + + /// + /// Проверяет нажатие кнопки по числовому индексу пина в текущем кадре. + /// + /// Числовой индекс пина (0-based) + /// true только в первом кадре после нажатия + public abstract bool GetButtonDown(int pinIndex); + + /// + /// Проверяет отпускание кнопки по числовому индексу пина в текущем кадре. + /// + /// Числовой индекс пина (0-based) + /// true только в первом кадре после отпускания + public abstract bool GetButtonUp(int pinIndex); + + //---------------------------------------------------------------------------------- + + /// + /// Получает состояние игрового действия для любого из игроков. + /// Используется для мультиплеерных сценариев где важно действие любого игрока. + /// + /// Абстрактное игровое действие + /// true если действие активно у первого ИЛИ второго игрока + public abstract bool GetButtonAnyPlayer(PlayerAction button); + + /// + /// Проверяет начало игрового действия любым игроком в текущем кадре. + /// + /// Абстрактное игровое действие + /// true если действие начато любым игроком в этом кадре + public abstract bool GetButtonDownAnyPlayer(PlayerAction button); + + /// + /// Проверяет завершение игрового действия любым игроком в текущем кадре. + /// + /// Абстрактное игровое действие + /// true если действие завершено любым игроком в этом кадре + public abstract bool GetButtonUpAnyPlayer(PlayerAction button); + + //---------------------------------------------------------------------------------- + + /// + /// Получает состояние игрового действия для текущего активного игрока. + /// Используется для одиночной игры или поочередного управления. + /// + /// Абстрактное игровое действие + /// true если действие активно у текущего игрока + public abstract bool GetButtonCurrentPlayer(PlayerAction button); + + /// + /// Проверяет начало игрового действия текущим игроком в текущем кадре. + /// + /// Абстрактное игровое действие + /// true если действие начато текущим игроком в этом кадре + public abstract bool GetButtonDownCurrentPlayer(PlayerAction button); + + /// + /// Проверяет завершение игрового действия текущим игроком в текущем кадре. + /// + /// Абстрактное игровое действие + /// true если действие завершено текущим игроком в этом кадре + public abstract bool GetButtonUpCurrentPlayer(PlayerAction button); +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSourceType.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSourceType.cs new file mode 100644 index 0000000..ad891ed --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/InputSourceType.cs @@ -0,0 +1,5 @@ +public enum InputSourceType +{ + COM, + Keyboard +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/KeyboardInput.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/KeyboardInput.cs new file mode 100644 index 0000000..94db5f2 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/KeyboardInput.cs @@ -0,0 +1,175 @@ +using UnityEngine; + +/// +/// Реализация системы ввода с клавиатуры для тестирования и разработки. +/// Эмулирует работу периферийной платы через стандартные клавиши клавиатуры. +/// +/// +/// Соответствие клавиш и пинов: +/// - Игрок 1: WASD + Space +/// - Игрок 2: Стрелки + RightControl +/// - Служебные: 1, 2, 3, F5 +/// - Датчики хопера: O, P +/// +public class KeyboardInput : InputSource +{ + private int currentPlayer => gm.currentPlayer == CellState.Player1 ? 1 : 2;//ПЕРЕОПРЕДЕЛИТЕ ОПРЕДЕЛЕНИЕ ТЕКУЩЕГО ИГРОКА ПОД СВОЮ СИСТЕМУ. + private GameManager gm;//я получаю кто сейчас ходит через этого менеджера. Если у вас другое, допишите. + + + + /// Проверяет состояние кнопки по её физическому пину через клавиатуру + public override bool GetButton(ButtonPinMapping button) => Input.GetKey(getKeyCode(button)); + + /// Проверяет нажатие кнопки по пину в текущем кадре через клавиатуру + public override bool GetButtonDown(ButtonPinMapping button) => Input.GetKeyDown(getKeyCode(button)); + + /// Проверяет отпускание кнопки по пину в текущем кадре через клавиатуру + public override bool GetButtonUp(ButtonPinMapping button) => Input.GetKeyUp(getKeyCode(button)); + + //---------------------------------------------------------------------------------- + + /// Проверяет состояние кнопки по числовому индексу пина через клавиатуру + public override bool GetButton(int pinIndex) => Input.GetKey(getKeyCode(pinIndex)); + + /// Проверяет нажатие кнопки по индексу пина в текущем кадре через клавиатуру + public override bool GetButtonDown(int pinIndex) => Input.GetKeyDown(getKeyCode(pinIndex)); + + /// Проверяет отпускание кнопки по индексу пина в текущем кадре через клавиатуру + public override bool GetButtonUp(int pinIndex) => Input.GetKeyUp(getKeyCode(pinIndex)); + + //---------------------------------------------------------------------------------- + + /// Проверяет состояние действия для текущего игрока (всегда игрок 1 для клавиатуры) + public override bool GetButtonCurrentPlayer(PlayerAction button) => GetButton(GetButtonForCurrentPlayer(button)); + + /// Проверяет начало действия текущим игроком в текущем кадре + public override bool GetButtonDownCurrentPlayer(PlayerAction button) => GetButtonDown(GetButtonForCurrentPlayer(button)); + + /// Проверяет завершение действия текущим игроком в текущем кадре + public override bool GetButtonUpCurrentPlayer(PlayerAction button) => GetButtonUp(GetButtonForCurrentPlayer(button)); + + //---------------------------------------------------------------------------------- + + /// Проверяет состояние действия для любого из игроков (игрок 1 ИЛИ игрок 2) + public override bool GetButtonAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButton); + + /// Проверяет начало действия любым игроком в текущем кадре + public override bool GetButtonDownAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButtonDown); + + /// Проверяет завершение действия любым игроком в текущем кадре + public override bool GetButtonUpAnyPlayer(PlayerAction button) => CheckBothPlayers(button, GetButtonUp); + + //---------------------------------------------------------------------------------- + + /// + /// Инициализирует клавиатурный ввод. Всегда возвращает true так как клавиатура всегда доступна. + /// + /// Всегда true + public override bool Initialize() => gm = GameManager.instance; + + // ================================================================================ + // ВСПОМОГАТЕЛЬНЫЕ МЕТОДЫ + // ================================================================================ + + /// + /// Определяет кнопку текущего игрока для указанного действия. + /// Для клавиатуры всегда возвращает кнопку первого игрока. + /// + private ButtonPinMapping GetButtonForCurrentPlayer(PlayerAction button) + { + var buttons = _buttonByAction(button); + return currentPlayer == 1 ? buttons.player1button : buttons.player2button; + } + + /// + /// Проверяет действие для обоих игроков используя указанную функцию проверки. + /// + private bool CheckBothPlayers(PlayerAction action, System.Func buttonFunc) + { + var buttons = _buttonByAction(action); + return buttonFunc(buttons.player1button) || buttonFunc(buttons.player2button); + } + + /// + /// Возвращает соответствие между игровыми действиями и физическими кнопками игроков. + /// + private (ButtonPinMapping player1button, ButtonPinMapping player2button) _buttonByAction(PlayerAction playerAction) => + playerAction switch + { + PlayerAction.up => (ButtonPinMapping.up1, ButtonPinMapping.up2), + PlayerAction.down => (ButtonPinMapping.down1, ButtonPinMapping.down2), + PlayerAction.left => (ButtonPinMapping.left1, ButtonPinMapping.left2), + PlayerAction.right => (ButtonPinMapping.right1, ButtonPinMapping.right2), + PlayerAction.action => (ButtonPinMapping.action1, ButtonPinMapping.action2), + PlayerAction.extra => (ButtonPinMapping.extra, ButtonPinMapping.extra), + _ => throw new System.ArgumentException($"Unknown player action: {playerAction}") + }; + + /// + /// Преобразует физический пин в соответствующую клавишу клавиатуры. + /// + private KeyCode getKeyCode(ButtonPinMapping button) + { + return button switch + { + ButtonPinMapping.up1 => KeyCode.W, + ButtonPinMapping.right1 => KeyCode.D, + ButtonPinMapping.down1 => KeyCode.S, + ButtonPinMapping.left1 => KeyCode.A, + ButtonPinMapping.action1 => KeyCode.Space, + + ButtonPinMapping.up2 => KeyCode.UpArrow, + ButtonPinMapping.right2 => KeyCode.RightArrow, + ButtonPinMapping.down2 => KeyCode.DownArrow, + ButtonPinMapping.left2 => KeyCode.LeftArrow, + ButtonPinMapping.action2 => KeyCode.RightControl, + + ButtonPinMapping.startOnePlayer => KeyCode.Alpha1, + ButtonPinMapping.startTwoPlayers => KeyCode.Alpha2, + + ButtonPinMapping.secret => KeyCode.F5, + + ButtonPinMapping.extra => KeyCode.Alpha3, + + ButtonPinMapping.hasPrize => KeyCode.O, + ButtonPinMapping.prizeGiven => KeyCode.P, + + _ => KeyCode.None, + }; + } + + /// + /// Преобразует числовой индекс пина в соответствующую клавишу клавиатуры. + /// Используется для методов с прямым доступом по индексу. + /// + private KeyCode getKeyCode(int pinIndex) + { + return pinIndex switch + { + 0 => KeyCode.W, // up1 + 1 => KeyCode.D, // right1 + 2 => KeyCode.S, // down1 + 3 => KeyCode.A, // left1 + 4 => KeyCode.Space, // action1 + + 5 => KeyCode.UpArrow, // up2 + 6 => KeyCode.RightArrow, // right2 + 7 => KeyCode.DownArrow, // down2 + 8 => KeyCode.LeftArrow, // left2 + 9 => KeyCode.RightControl, // action2 + + 10 => KeyCode.Alpha1, // startOnePlayer + 11 => KeyCode.Alpha2, // startTwoPlayers + + 12 => KeyCode.F5, // secret + + 13 => KeyCode.Alpha3, // extra + + 14 => KeyCode.O, // hasPrize + 15 => KeyCode.P, // prizeGiven + + _ => KeyCode.None, // невалидный индекс + }; + } +} \ No newline at end of file diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/PlayerAction.cs b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/PlayerAction.cs new file mode 100644 index 0000000..db8f348 --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/Scripts/Inputs/PlayerAction.cs @@ -0,0 +1,19 @@ +/// +/// Абстрактные игровые действия для управления игроком. +/// Обеспечивает независимость от физического расположения кнопок. +/// +public enum PlayerAction +{ + /// Вверх + up, + /// Вниз + down, + /// Влево + left, + /// Вправо + right, + /// Основное действие + action, + /// Дополнительное действие(ВНИМАНИЕ РАБОТАЕТ У ОБОИХ ИГРОКОВ ОДНОВРЕМЕННО) + extra +} diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe new file mode 100644 index 0000000..2e0a6f1 Binary files /dev/null and b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe differ diff --git a/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe.lic.json b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe.lic.json new file mode 100644 index 0000000..160b90a --- /dev/null +++ b/GreatGamedevVault/Библиотека/Периферия/Встраиваемые/Assets/StreamingAssets/RRLauncher.exe.lic.json @@ -0,0 +1 @@ +{"product":"RRLicensedApp","hwid_sha256":"9f67ba1daae5c0d237588a2f32dbba2a38ca763ce44898c2722e3edaa4a3ae6d","fingerprint":"d675d82f7d58bf105dd381d028ff66f776bdd7881d535e483bd1e80aaf6c3c97","expiry":0,"nonce":"I1onNRUkvn8TybL3h5Q7dAt1KIj/wJ7WSPy3LLgzAvU","issued":"2025-11-05T09:14:02.2416908Z","features":["full"],"sig":"XHEz/NOCSeIErht17gtSoYxPNyF2OeawtLHO3lqkARxf1r4rYOiIALA8tybqE/BE7w4/bB\u002B2AIHDwMyyAXVh6Q=="} \ No newline at end of file diff --git a/Библиотека/Периферия/src/ComNative/ComNative.sln b/Библиотека/Периферия/src/ComNative/ComNative.sln new file mode 100644 index 0000000..cbb102c --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.14.36603.0 d17.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ComNative", "ComNative\ComNative.vcxproj", "{BCC43995-EF98-4066-BB8D-58819B0AFB01}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x64.ActiveCfg = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x64.Build.0 = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x86.ActiveCfg = Debug|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Debug|x86.Build.0 = Debug|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x64.ActiveCfg = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x64.Build.0 = Release|x64 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x86.ActiveCfg = Release|Win32 + {BCC43995-EF98-4066-BB8D-58819B0AFB01}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {022B4E09-68D9-43CB-B2A3-E6ABE4525C96} + EndGlobalSection +EndGlobal diff --git a/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj new file mode 100644 index 0000000..1e2c5d6 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj @@ -0,0 +1,154 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {bcc43995-ef98-4066-bb8d-58819b0afb01} + ComNative + 10.0 + + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + DynamicLibrary + true + v143 + Unicode + + + DynamicLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + WIN32;NDEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + _DEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + pch.h + + + Windows + true + false + + + + + Level3 + true + true + true + NDEBUG;COMNATIVE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + NotUsing + pch.h + + + Windows + true + false + + + + + + + + + + + Create + Create + Create + Create + + + + + + \ No newline at end of file diff --git a/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters new file mode 100644 index 0000000..6216c47 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Файлы заголовков + + + Файлы заголовков + + + Файлы заголовков + + + + + Исходные файлы + + + Исходные файлы + + + \ No newline at end of file diff --git a/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/ComNative.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp b/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp new file mode 100644 index 0000000..34f0a2c --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.cpp @@ -0,0 +1,261 @@ +// SerialNative.cpp +#include "SerialNative.h" +#include +#include +#include +#include +#include + +static std::mutex gMutex; +static std::unordered_map gHandles; +static int gNextId = 1; + +static thread_local std::wstring gLastError; + +static void SetLastErrFromWin32(const wchar_t* where, DWORD err = GetLastError()) { + wchar_t* msg = nullptr; + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPWSTR)&msg, 0, nullptr); + if (msg) { gLastError = std::wstring(where) + L": (" + std::to_wstring(err) + L") " + msg; LocalFree(msg); } + else { gLastError = std::wstring(where) + L": error " + std::to_wstring(err); } +} + +static std::wstring BuildDevicePath(const std::wstring& portName) { + if (portName.rfind(L"\\\\.\\", 0) == 0) return portName; // уже с префиксом + return L"\\\\.\\" + portName; +} + +// --- аккуратный enumerator COM-портов --- +static std::wstring EnumerateComPortsJoined() { + std::vector found; + wchar_t target[4096]; + for (int i = 1; i <= 256; ++i) { + std::wstring name = L"COM" + std::to_wstring(i); + DWORD r = QueryDosDeviceW(name.c_str(), target, static_cast(std::size(target))); + if (r != 0) found.push_back(name); + } + std::wstring joined; + for (size_t i = 0; i < found.size(); ++i) { + if (i) joined += L";"; + joined += found[i]; + } + return joined; +} + +// --- безопасная выдача HANDLE по id --- +static HANDLE GetHandle(int handleId) { + std::lock_guard lk(gMutex); + auto it = gHandles.find(handleId); + return (it == gHandles.end()) ? nullptr : it->second; +} + +// --- внутренняя запись байтов (без экспорта) --- +static int WriteBytes(HANDLE h, const uint8_t* data, int length) { + if (!h || !data || length < 0) { gLastError = L"WriteBytes: invalid args"; return -1; } + DWORD written = 0; + if (!WriteFile(h, data, static_cast(length), &written, nullptr)) { + SetLastErrFromWin32(L"WriteFile"); + return -1; + } + return static_cast(written); +} + +// --- гарантированная зачистка при выгрузке процесса (Alt+F4 и т.п.) --- +BOOL APIENTRY DllMain(HINSTANCE, DWORD reason, LPVOID) { + if (reason == DLL_PROCESS_DETACH) { + std::lock_guard lk(gMutex); + for (auto& kv : gHandles) { + HANDLE h = kv.second; + if (h && h != INVALID_HANDLE_VALUE) CloseHandle(h); + } + gHandles.clear(); + } + return TRUE; +} + +// ---------- Перечень портов ---------- +SERIAL_API +int __cdecl Serial_ListPortsLength() { + std::wstring s = EnumerateComPortsJoined(); + return static_cast(s.size() + 1); +} + +SERIAL_API +int __cdecl Serial_ListPorts(wchar_t* buffer, int bufferChars) { + std::wstring s = EnumerateComPortsJoined(); + int need = static_cast(s.size() + 1); + if (!buffer || bufferChars <= 0) return need; + if (bufferChars < need) return -1; + wcsncpy_s(buffer, bufferChars, s.c_str(), _TRUNCATE); + return need; +} + +// ---------- Работа с портом ---------- +SERIAL_API +int __cdecl Serial_Open(const wchar_t* portName, + int baudRate, int dataBits, int parity, int stopBits, + int readTimeoutMs, int writeTimeoutMs) +{ + if (!portName || !*portName) { gLastError = L"Serial_Open: empty portName"; return 0; } + + std::wstring dev = BuildDevicePath(portName); + + // Retry-цикл, лечит ACCESS_DENIED/SHARING_VIOLATION сразу после Alt+F4 + const int kMaxTries = 20; // до ~2 сек + const DWORD kSleepMs = 100; + HANDLE h = INVALID_HANDLE_VALUE; + DWORD lastErr = 0; + + for (int i = 0; i < kMaxTries; ++i) { + h = CreateFileW(dev.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + if (h != INVALID_HANDLE_VALUE) break; + + lastErr = GetLastError(); + if (lastErr == ERROR_ACCESS_DENIED || lastErr == ERROR_SHARING_VIOLATION) { + Sleep(kSleepMs); + continue; + } + else { + break; + } + } + if (h == INVALID_HANDLE_VALUE) { SetLastErrFromWin32(L"CreateFileW", lastErr); return 0; } + + SetupComm(h, 1 << 15, 1 << 15); // 32KB RX/TX + + // DCB + DCB dcb{}; dcb.DCBlength = sizeof(DCB); + if (!GetCommState(h, &dcb)) { SetLastErrFromWin32(L"GetCommState"); CloseHandle(h); return 0; } + dcb.BaudRate = static_cast(baudRate); + dcb.ByteSize = static_cast(dataBits > 0 ? dataBits : 8); + dcb.fBinary = TRUE; + dcb.fParity = (parity != 0); + + switch (parity) { + case 0: dcb.Parity = NOPARITY; break; + case 1: dcb.Parity = ODDPARITY; break; + case 2: dcb.Parity = EVENPARITY; break; + case 3: dcb.Parity = MARKPARITY; break; + case 4: dcb.Parity = SPACEPARITY;break; + default: dcb.Parity = NOPARITY; break; + } + dcb.StopBits = (stopBits == 2) ? TWOSTOPBITS : ONESTOPBIT; + dcb.fDtrControl = DTR_CONTROL_DISABLE; // как раньше + dcb.fRtsControl = RTS_CONTROL_DISABLE; // как раньше + + if (!SetCommState(h, &dcb)) { SetLastErrFromWin32(L"SetCommState"); CloseHandle(h); return 0; } + + // Таймауты + COMMTIMEOUTS to{}; + to.ReadIntervalTimeout = 50; + to.ReadTotalTimeoutConstant = (readTimeoutMs >= 0) ? static_cast(readTimeoutMs) : 0; + to.ReadTotalTimeoutMultiplier = 0; + to.WriteTotalTimeoutConstant = (writeTimeoutMs >= 0) ? static_cast(writeTimeoutMs) : 0; + to.WriteTotalTimeoutMultiplier = 0; + if (!SetCommTimeouts(h, &to)) { SetLastErrFromWin32(L"SetCommTimeouts"); CloseHandle(h); return 0; } + + // Полная очистка очередей на старте + PurgeComm(h, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT); + + // Регистрируем handle + { + std::lock_guard lk(gMutex); + int id = gNextId++; + gHandles[id] = h; + return id; + } +} + +SERIAL_API +int __cdecl Serial_Close(int handleId) { + std::lock_guard lk(gMutex); + auto it = gHandles.find(handleId); + if (it == gHandles.end()) { gLastError = L"Serial_Close: invalid handleId"; return 0; } + HANDLE h = it->second; + gHandles.erase(it); + if (h && h != INVALID_HANDLE_VALUE) { CloseHandle(h); return 1; } + return 0; +} + +SERIAL_API +int __cdecl Serial_CloseAll() { + std::lock_guard lk(gMutex); + int closed = 0; + for (auto& kv : gHandles) { + HANDLE h = kv.second; + if (h && h != INVALID_HANDLE_VALUE) { CloseHandle(h); ++closed; } + } + gHandles.clear(); + return closed; +} + +// ---------- Запись ---------- +SERIAL_API +int __cdecl Serial_WriteStringUTF8(int handleId, const char* utf8, int appendNewline) { + if (!utf8) { gLastError = L"Serial_WriteStringUTF8: utf8 is null"; return -1; } + HANDLE h = GetHandle(handleId); + if (!h) { gLastError = L"Serial_WriteStringUTF8: invalid handleId"; return -1; } + + const uint8_t* bytes = reinterpret_cast(utf8); + int len = static_cast(strlen(utf8)); + + int total = 0; + int w = WriteBytes(h, bytes, len); + if (w < 0) return -1; + total += w; + + if (appendNewline) { + const uint8_t nl = '\n'; + w = WriteBytes(h, &nl, 1); + if (w < 0) return -1; + total += w; + } + return total; +} + +// ---------- Чтение ---------- +SERIAL_API +int __cdecl Serial_ReadLineUTF8(int handleId, char* buffer, int bufferSize) { + HANDLE h = GetHandle(handleId); + if (!h) { gLastError = L"handle incorrect"; return -1; } + if (buffer == nullptr) { gLastError = L"Serial_ReadLineUTF8: buffer is null"; return -1; } + if (bufferSize <= 1) { + gLastError = L"Serial_ReadLineUTF8: bufferSize too small (<=1). Got " + std::to_wstring(bufferSize); + buffer[0] = 0; return -1; + } + + int pos = 0; + DWORD read = 0; + char ch = 0; + + while (pos < bufferSize - 1) { + BOOL ok = ReadFile(h, &ch, 1, &read, nullptr); + if (!ok) { SetLastErrFromWin32(L"ReadFile"); return -1; } + if (read == 0) { // таймаут + if (pos == 0) { buffer[0] = 0; return 0; } + break; + } + if (ch == '\n') break; + if (ch != '\r') buffer[pos++] = ch; + } + + if (pos >= bufferSize - 1) { + buffer[bufferSize - 1] = 0; + return -2; // усечено + } + + buffer[pos] = 0; + return pos; +} + +// ---------- Диагностика ---------- +SERIAL_API +int __cdecl Serial_GetLastErrorW(wchar_t* buffer, int bufferChars) { + int need = static_cast(gLastError.size() + 1); + if (!buffer || bufferChars <= 0) return need; + if (bufferChars < need) return -1; + wcsncpy_s(buffer, bufferChars, gLastError.c_str(), _TRUNCATE); + return need; +} diff --git a/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h b/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h new file mode 100644 index 0000000..8cb17e7 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/SerialNative.h @@ -0,0 +1,29 @@ +// SerialNative.h +#pragma once +#include + +#ifdef _WIN32 + #define SERIAL_API extern "C" __declspec(dllexport) +#else + #define SERIAL_API extern "C" +#endif + +// --- Перечень портов --- +SERIAL_API int __cdecl Serial_ListPortsLength(); +SERIAL_API int __cdecl Serial_ListPorts(wchar_t* buffer, int bufferChars); + +// --- Работа с портом --- +SERIAL_API int __cdecl Serial_Open(const wchar_t* portName, + int baudRate, int dataBits, int parity, int stopBits, + int readTimeoutMs, int writeTimeoutMs); +SERIAL_API int __cdecl Serial_Close(int handleId); +SERIAL_API int __cdecl Serial_CloseAll(); // NEW: глобальная зачистка хэндлов + +// --- Запись (оставили только строковую; байтовую убрали из экспорта) --- +SERIAL_API int __cdecl Serial_WriteStringUTF8(int handleId, const char* utf8, int appendNewline); + +// --- Чтение (оставили только построчное) --- +SERIAL_API int __cdecl Serial_ReadLineUTF8(int handleId, char* buffer, int bufferSize); + +// --- Диагностика --- +SERIAL_API int __cdecl Serial_GetLastErrorW(wchar_t* buffer, int bufferChars); diff --git a/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp b/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp new file mode 100644 index 0000000..f840505 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Определяет точку входа для приложения DLL. +#include "pch.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/Библиотека/Периферия/src/ComNative/ComNative/framework.h b/Библиотека/Периферия/src/ComNative/ComNative/framework.h new file mode 100644 index 0000000..61ca0b6 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/framework.h @@ -0,0 +1,5 @@ +#pragma once + +#define WIN32_LEAN_AND_MEAN // Исключите редко используемые компоненты из заголовков Windows +// Файлы заголовков Windows +#include diff --git a/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp b/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp new file mode 100644 index 0000000..9211a5e --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: файл исходного кода, соответствующий предварительно скомпилированному заголовочному файлу + +#include "pch.h" + +// При использовании предварительно скомпилированных заголовочных файлов необходим следующий файл исходного кода для выполнения сборки. diff --git a/Библиотека/Периферия/src/ComNative/ComNative/pch.h b/Библиотека/Периферия/src/ComNative/ComNative/pch.h new file mode 100644 index 0000000..42d0757 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/pch.h @@ -0,0 +1,13 @@ +// pch.h: это предварительно скомпилированный заголовочный файл. +// Перечисленные ниже файлы компилируются только один раз, что ускоряет последующие сборки. +// Это также влияет на работу IntelliSense, включая многие функции просмотра и завершения кода. +// Однако изменение любого из приведенных здесь файлов между операциями сборки приведет к повторной компиляции всех(!) этих файлов. +// Не добавляйте сюда файлы, которые планируете часто изменять, так как в этом случае выигрыша в производительности не будет. + +#ifndef PCH_H +#define PCH_H + +// Добавьте сюда заголовочные файлы для предварительной компиляции +#include "framework.h" + +#endif //PCH_H diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe new file mode 100644 index 0000000..c4fa5f5 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.dll.recipe @@ -0,0 +1,11 @@ + + + + + C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.dll + + + + + + \ No newline at end of file diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj new file mode 100644 index 0000000..0a544c0 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.iobj differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb new file mode 100644 index 0000000..9272437 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.ipdb differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log new file mode 100644 index 0000000..8898563 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.log @@ -0,0 +1,8 @@ + SerialNative.cpp + Создается библиотека C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.lib и объект C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.exp + Создание кода + 1 of 376 functions ( 0.3%) were compiled, the rest were copied from previous compilation. + 0 functions were new in current compilation + 0 functions had inline decision re-evaluated but remain unchanged + Создание кода завершено + ComNative.vcxproj -> C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.dll diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch new file mode 100644 index 0000000..1beffe4 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.pch differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog new file mode 100644 index 0000000..558e29a Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.command.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog new file mode 100644 index 0000000..9272a88 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.read.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog new file mode 100644 index 0000000..17fb5c0 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/CL.write.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog new file mode 100644 index 0000000..21e6b56 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/Cl.items.tlog @@ -0,0 +1,2 @@ +C:\Users\Programmer\source\repos\ComNative\ComNative\SerialNative.cpp;C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\SerialNative.obj +C:\Users\Programmer\source\repos\ComNative\ComNative\pch.cpp;C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\pch.obj diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate new file mode 100644 index 0000000..135a445 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/ComNative.lastbuildstate @@ -0,0 +1,2 @@ +PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.44.35207:TargetPlatformVersion=10.0.26100.0: +Release|x64|C:\Users\Programmer\source\repos\ComNative\| diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog new file mode 100644 index 0000000..23eaf57 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.command.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog new file mode 100644 index 0000000..e447ff8 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.read.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog new file mode 100644 index 0000000..f0e2c69 --- /dev/null +++ b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.secondary.1.tlog @@ -0,0 +1,5 @@ +^C:\USERS\PROGRAMMER\SOURCE\REPOS\COMNATIVE\COMNATIVE\X64\RELEASE\PCH.OBJ|C:\USERS\PROGRAMMER\SOURCE\REPOS\COMNATIVE\COMNATIVE\X64\RELEASE\SERIALNATIVE.OBJ +C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.lib +C:\Users\Programmer\source\repos\ComNative\x64\Release\ComNative.EXP +C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\ComNative.IPDB +C:\Users\Programmer\source\repos\ComNative\ComNative\x64\Release\ComNative.iobj diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog new file mode 100644 index 0000000..02688e0 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/ComNative.tlog/link.write.1.tlog differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj new file mode 100644 index 0000000..32697bc Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/SerialNative.obj differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj new file mode 100644 index 0000000..60b69c1 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/dllmain.obj differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj new file mode 100644 index 0000000..ae1e334 Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/pch.obj differ diff --git a/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb new file mode 100644 index 0000000..bae206d Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/ComNative/x64/Release/vc143.pdb differ diff --git a/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll new file mode 100644 index 0000000..e0f6d6b Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.dll differ diff --git a/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp new file mode 100644 index 0000000..ccb093f Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.exp differ diff --git a/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib new file mode 100644 index 0000000..ba0439c Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.lib differ diff --git a/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb new file mode 100644 index 0000000..508efef Binary files /dev/null and b/Библиотека/Периферия/src/ComNative/x64/Release/ComNative.pdb differ