down_time.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. // package down_time -- счётчик обратного времени в мсек
  2. package down_time
  3. import (
  4. "context"
  5. "fmt"
  6. // "log"
  7. "sync"
  8. "time"
  9. "wartank/pkg/alias"
  10. "wartank/pkg/components/parser_time"
  11. "wartank/pkg/components/safe_bool"
  12. "wartank/pkg/components/safe_int"
  13. "wartank/pkg/types"
  14. )
  15. const (
  16. спатьИнтервал = time.Millisecond * 100 // Малый интервал сна в 100 мсек
  17. )
  18. // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
  19. type ВремОбрат struct {
  20. сцена types.ИСцена // Сцена, которой принадлежит отсчёт времени
  21. остатПарсер types.ИПарсерВремя // Парсер значения (мсек)
  22. текущ *safe_int.БезопЦелое // Фактическое значение счётчика в мсек
  23. лимит *safe_int.БезопЦелое // Целевое время срабатывания в мсек
  24. еслиРаботает *safe_bool.БезопБул // Признак работы
  25. канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
  26. кнт context.Context // Контекст для счётчика времени
  27. фнОтмена func() // Функция отмены контекста для счётчика времени
  28. блок sync.RWMutex
  29. }
  30. // НовВремОбрат -- возвращает новый *CountTime
  31. func НовВремОбрат(сцена types.ИСцена, время alias.МилСек) *ВремОбрат {
  32. if сцена == nil {
  33. panic("НовВремОбрат(): ИСцена == nil")
  34. }
  35. кнт, фнОтмена := context.WithCancel(сцена.Кнт())
  36. сам := &ВремОбрат{
  37. сцена: сцена,
  38. текущ: safe_int.НовБезопЦелое(),
  39. канВызов: make(chan int, 2),
  40. еслиРаботает: safe_bool.НовБезопБул(),
  41. остатПарсер: parser_time.НовПарсерВремя(),
  42. лимит: safe_int.НовБезопЦелое(),
  43. кнт: кнт,
  44. фнОтмена: фнОтмена,
  45. }
  46. мСек := alias.МилСек(time.Now().UTC().UnixMilli()) + время
  47. сам.лимит.Уст(int(мСек))
  48. сам.еслиРаботает.Уст()
  49. go сам.пуск()
  50. go сам.закрыть()
  51. _ = types.ИВремяОстат(сам)
  52. return сам
  53. }
  54. // ПолучМилСек -- возвращает оставшееся хранимое время остатка
  55. func (сам *ВремОбрат) ПолучМилСек() alias.МилСек {
  56. return сам.остатПарсер.ПолучМилСек()
  57. }
  58. // Запускает тикер для интервалов сна (через каждые 100 мСек)
  59. func (сам *ВремОбрат) пуск() {
  60. defer close(сам.канВызов)
  61. фнЖдать := func() {
  62. timeNow := time.Now().UTC().UnixMilli()
  63. if сам.лимит.Получ() > int(timeNow) {
  64. return
  65. }
  66. сам.канВызов <- 1
  67. сам.лимит.Уст(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
  68. }
  69. for {
  70. select {
  71. case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
  72. return
  73. default:
  74. time.Sleep(спатьИнтервал)
  75. фнЖдать()
  76. }
  77. }
  78. }
  79. // Сброс -- сбрасывает оставшееся время в ноль
  80. func (сам *ВремОбрат) Сброс() {
  81. сам.остатПарсер.Сброс()
  82. сам.текущ.Сброс()
  83. сам.лимит.Сброс()
  84. }
  85. // Стоп -- останавливает работу cчётчика
  86. func (сам *ВремОбрат) Стоп() {
  87. сам.фнОтмена()
  88. }
  89. // Уст -- устанавливает число оставшихся сек
  90. func (сам *ВремОбрат) Уст(время alias.Время) error {
  91. сам.блок.Lock()
  92. defer сам.блок.Unlock()
  93. if ош := сам.остатПарсер.Уст(время); ош != nil {
  94. return fmt.Errorf("ВремОбрат(): ошибка при установке времени, ош=\n\t%w", ош)
  95. }
  96. _val := сам.остатПарсер.ПолучМилСек()
  97. сам.текущ.Уст(int(_val))
  98. val := int(time.Now().UTC().UnixMilli()) + сам.текущ.Получ()
  99. сам.лимит.Уст(val)
  100. return nil
  101. }
  102. // String -- возвращает строковое представление оставшихся сек
  103. func (сам *ВремОбрат) String() string {
  104. сам.блок.RLock()
  105. defer сам.блок.RUnlock()
  106. return сам.остатПарсер.String()
  107. }
  108. // КаналСиг -- возвращает канал чтения тиков
  109. func (сам *ВремОбрат) КаналСиг() <-chan int {
  110. return сам.канВызов
  111. }
  112. func (сам *ВремОбрат) закрыть() {
  113. <-сам.кнт.Done()
  114. сам.блок.Lock()
  115. defer сам.блок.Unlock()
  116. if !сам.еслиРаботает.Получ() {
  117. return
  118. }
  119. сам.еслиРаботает.Сброс()
  120. }