| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // package down_time -- счётчик обратного времени в мсек
- package down_time
- import (
- "context"
- "fmt"
- // "log"
- "sync"
- "time"
- "wartank/pkg/alias"
- "wartank/pkg/components/parser_time"
- "wartank/pkg/components/safe_bool"
- "wartank/pkg/components/safe_int"
- "wartank/pkg/types"
- )
- const (
- спатьИнтервал = time.Millisecond * 1000 // Малый интервал сна в 100 мсек
- )
- // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.)
- type ВремОбрат struct {
- сцена types.ИСцена // Сцена, которой принадлежит отсчёт времени
- остатПарсер types.ИПарсерВремя // Парсер значения (мсек)
- текущ *safe_int.БезопЦелое // Фактическое значение счётчика в мсек
- лимит *safe_int.БезопЦелое // Целевое время срабатывания в мсек
- еслиРаботает *safe_bool.БезопБул // Признак работы
- канВызов chan int // Канал для отправки сигналов (для верхнего уровня)
- кнт context.Context // Контекст для счётчика времени
- фнОтмена func() // Функция отмены контекста для счётчика времени
- блок sync.RWMutex
- }
- // НовВремОбрат -- возвращает новый *CountTime
- func НовВремОбрат(сцена types.ИСцена, время alias.МилСек) *ВремОбрат {
- if сцена == nil {
- panic("НовВремОбрат(): ИСцена == nil")
- }
- кнт, фнОтмена := context.WithCancel(сцена.Контекст())
- сам := &ВремОбрат{
- сцена: сцена,
- текущ: safe_int.НовБезопЦелое(),
- канВызов: make(chan int, 2),
- еслиРаботает: safe_bool.НовБезопБул(),
- остатПарсер: parser_time.НовПарсерВремя(),
- лимит: safe_int.НовБезопЦелое(),
- кнт: кнт,
- фнОтмена: фнОтмена,
- }
- мСек := alias.МилСек(time.Now().UTC().UnixMilli()) + время
- сам.лимит.Уст(int(мСек))
- сам.еслиРаботает.Уст()
- go сам.пуск()
- go сам.закрыть()
- _ = types.ИВремяОстат(сам)
- return сам
- }
- // ПолучМилСек -- возвращает оставшееся хранимое время остатка
- func (сам *ВремОбрат) ПолучМилСек() alias.МилСек {
- return сам.остатПарсер.ПолучМилСек()
- }
- // Запускает тикер для интервалов сна (через каждые 1000 мСек)
- func (сам *ВремОбрат) пуск() {
- defer close(сам.канВызов)
- фнЖдать := func() {
- time.Sleep(спатьИнтервал)
- timeNow := time.Now().UTC().UnixMilli()
- цТекущ := сам.текущ.Получ()
- цТекущ -= 1000
- if цТекущ < 0 {
- цТекущ = 0
- }
- сам.текущ.Уст(цТекущ)
- if сам.лимит.Получ() > int(timeNow) || цТекущ > 0 {
- return
- }
- сам.канВызов <- 1
- сам.лимит.Уст(int(timeNow) + int(сам.остатПарсер.ПолучМилСек()))
- }
- for {
- select {
- case <-сам.кнт.Done(): // Отмена контекста тикера (а может и сцены, может и бота)
- return
- default:
- фнЖдать()
- }
- }
- }
- // Сброс -- сбрасывает оставшееся время в ноль
- func (сам *ВремОбрат) Сброс() {
- сам.остатПарсер.Сброс()
- сам.текущ.Сброс()
- сам.лимит.Сброс()
- }
- // Стоп -- останавливает работу cчётчика
- func (сам *ВремОбрат) Стоп() {
- сам.фнОтмена()
- }
- // Уст -- устанавливает число оставшихся сек
- func (сам *ВремОбрат) Уст(время alias.Время) error {
- сам.блок.Lock()
- defer сам.блок.Unlock()
- if ош := сам.остатПарсер.Уст(время); ош != nil {
- return fmt.Errorf("ВремОбрат(): ошибка при установке времени, ош=\n\t%w", ош)
- }
- _val := сам.остатПарсер.ПолучМилСек()
- сам.текущ.Уст(int(_val))
- val := int(time.Now().UTC().UnixMilli()) + сам.текущ.Получ()
- сам.лимит.Уст(val)
- return nil
- }
- // String -- возвращает строковое представление оставшихся сек
- func (сам *ВремОбрат) String() string {
- сам.блок.RLock()
- defer сам.блок.RUnlock()
- цОстат := сам.текущ.Получ()
- остат := time.Millisecond * time.Duration(цОстат)
- стрВрем := остат.String()
- return стрВрем
- }
- // КаналСиг -- возвращает канал чтения тиков
- func (сам *ВремОбрат) КаналСиг() <-chan int {
- return сам.канВызов
- }
- func (сам *ВремОбрат) закрыть() {
- <-сам.кнт.Done()
- сам.блок.Lock()
- defer сам.блок.Unlock()
- if !сам.еслиРаботает.Получ() {
- return
- }
- сам.еслиРаботает.Сброс()
- }
|