down_time.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
  27. кнт context.Context // Контекст для счётчика времени
  28. фнОтмена func() // Функция отмены контекста для счётчика времени
  29. блок sync.RWMutex
  30. }
  31. // НовВремОбрат -- возвращает новый *CountTime
  32. func НовВремОбрат(сцена types.ИСцена, время alias.МилСек) *ВремОбрат {
  33. if сцена == nil {
  34. panic("НовВремОбрат(): ИСцена == nil")
  35. }
  36. кнт, фнОтмена := context.WithCancel(сцена.Кнт())
  37. сам := &ВремОбрат{
  38. сцена: сцена,
  39. остатЗнач: safe_int.НовБезопЦелое(),
  40. канТик: make(chan int, 5),
  41. канВызов: make(chan int, 2),
  42. еслиРаботает: safe_bool.НовБезопБул(),
  43. остатПарсер: parser_time.НовПарсерВремя(),
  44. порогЗнач: safe_int.НовБезопЦелое(),
  45. кнт: кнт,
  46. фнОтмена: фнОтмена,
  47. }
  48. мСек := alias.МилСек(time.Now().UTC().UnixMilli()) + время
  49. сам.порогЗнач.Уст(int(мСек))
  50. сам.еслиРаботает.Уст()
  51. go сам.сделатьТик()
  52. go сам.пуск()
  53. go сам.закрыть()
  54. _ = types.ИВремяОстат(сам)
  55. return сам
  56. }
  57. // ПолучМилСек -- возвращает оставшееся хранимое время остатка
  58. func (сам *ВремОбрат) ПолучМилСек() alias.МилСек {
  59. return сам.остатПарсер.ПолучМилСек()
  60. }
  61. // Запускает тикер для интервалов сна (через каждые 100 мСек)
  62. func (сам *ВремОбрат) сделатьТик() {
  63. defer close(сам.канТик)
  64. for {
  65. select {
  66. case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
  67. return
  68. default:
  69. time.Sleep(спатьИнтервал)
  70. if !сам.еслиРаботает.Получ() {
  71. сам.фнОтмена()
  72. return
  73. }
  74. timeNow := time.Now().UTC().Unix()
  75. if сам.порогЗнач.Получ() < int(timeNow) {
  76. continue
  77. }
  78. сам.канТик <- 1
  79. сам.фнОтмена()
  80. сам.еслиРаботает.Сброс()
  81. return
  82. }
  83. }
  84. }
  85. // Главный цикл обратного отсчёта
  86. func (сам *ВремОбрат) пуск() {
  87. for range сам.канТик {
  88. close(сам.канВызов)
  89. сам.фнОтмена()
  90. return
  91. }
  92. }
  93. // Сброс -- сбрасывает оставшееся время в ноль
  94. func (сам *ВремОбрат) Сброс() {
  95. сам.остатПарсер.Сброс()
  96. сам.остатЗнач.Сброс()
  97. сам.порогЗнач.Сброс()
  98. }
  99. // Стоп -- останавливает работу cчётчика
  100. func (сам *ВремОбрат) Стоп() {
  101. сам.фнОтмена()
  102. }
  103. // Уст -- устанавливает число оставшихся сек
  104. func (сам *ВремОбрат) Уст(время alias.Время) error {
  105. сам.блок.Lock()
  106. defer сам.блок.Unlock()
  107. if ош := сам.остатПарсер.Уст(время); ош != nil {
  108. return fmt.Errorf("ВремОбрат(): ошибка при установке времени, ош=\n\t%w", ош)
  109. }
  110. _val := сам.остатПарсер.ПолучМилСек()
  111. сам.остатЗнач.Уст(int(_val))
  112. val := int(time.Now().UTC().UnixMilli()) + сам.остатЗнач.Получ()
  113. сам.порогЗнач.Уст(val)
  114. return nil
  115. }
  116. // устанавливает число оставшихся сек
  117. func (сам *ВремОбрат) set_val(val int) error {
  118. сам.блок.Lock()
  119. defer сам.блок.Unlock()
  120. if val < 0 {
  121. return fmt.Errorf("CountTime.set_val(): val(%v)<0", val)
  122. }
  123. сам.остатЗнач.Уст(val)
  124. return nil
  125. }
  126. // String -- возвращает строковое представление оставшихся сек
  127. func (сам *ВремОбрат) String() string {
  128. сам.блок.RLock()
  129. defer сам.блок.RUnlock()
  130. timeNow := time.Now().UTC().Unix()
  131. val := сам.порогЗнач.Получ() - int(timeNow)
  132. go сам.set_val(val)
  133. return сам.остатПарсер.String()
  134. }
  135. // КаналСиг -- возвращает канал чтения тиков
  136. func (сам *ВремОбрат) КаналСиг() <-chan int {
  137. return сам.канВызов
  138. }
  139. func (сам *ВремОбрат) закрыть() {
  140. <-сам.кнт.Done()
  141. сам.блок.Lock()
  142. defer сам.блок.Unlock()
  143. if !сам.еслиРаботает.Получ() {
  144. return
  145. }
  146. сам.еслиРаботает.Сброс()
  147. }