// package down_time -- счётчик обратного времени в мсек package down_time import ( "context" "fmt" // "log" "sync" "time" "wartank/pkg/components/parsetime" "wartank/pkg/components/safebool" "wartank/pkg/components/safeint" "wartank/pkg/types" ) const ( спатьИнтервал = time.Millisecond * 100 // Малый интервал сна в 100 мсек ) // ВремОбрат -- счётчик обратного времени для игровой зоны (анга, база, битва и т.п.) type ВремОбрат struct { сцена types.ИСцена парсер *parsetime.ParseTime // Парсер значения времяЗнач *safeint.SafeInt // Фактическое значение счётчика времПорог *safeint.SafeInt // Целевое время срабатывания еслиРаботает *safebool.SafeBool // Признак работы канТик chan int // Канал секундных интервалов сна (для отображения) канВызов chan int // Канал для отправки сигналов (для верхнего уровня) кнт context.Context // Контекст для счётчика времени фнОтмена func() // Функция отмены контекста для счётчика времени блок sync.RWMutex } // НовВремОбрат -- возвращает новый *CountTime func НовВремОбрат(сцена types.ИСцена, знач int) *ВремОбрат { if сцена == nil { panic("НовВремОбрат(): ИСцена == nil") } кнт, фнОтмена := context.WithCancel(сцена.Кнт()) сам := &ВремОбрат{ сцена: сцена, времяЗнач: safeint.NewSafeInt(), канТик: make(chan int, 5), канВызов: make(chan int, 2), еслиРаботает: safebool.NewSafeBool(), парсер: parsetime.NewParseTime(), времПорог: safeint.NewSafeInt(), кнт: кнт, фнОтмена: фнОтмена, } знач = int(time.Now().UTC().Unix()) + знач сам.времПорог.Уст(знач) сам.еслиРаботает.Уст() go сам.сделатьТик() go сам.пуск() go сам.закрыть() return сам } // Запускает тикер для секундных интервалов func (сам *ВремОбрат) сделатьТик() { спатьСчёт := 0 // Счётчик сна (10 периодов -- 1 секунда) for { select { case <-сам.кнт.Done(): // Отмена контекста бота // log._rintf("CountTime.makeTick(): отмена контекста бота\n") return default: if !сам.еслиРаботает.Получ() { сам.фнОтмена() return } if спатьСчёт >= 10 { сам.канТик <- 1 спатьСчёт = 0 } time.Sleep(спатьИнтервал) спатьСчёт++ } } } // Главный цикл обратного отсчёта func (sf *ВремОбрат) пуск() { for range sf.канТик { time.Sleep(time.Millisecond * 100) timeNow := time.Now().UTC().Unix() if sf.времПорог.Get() > int(timeNow) { continue } close(sf.канВызов) sf.фнОтмена() return } } // Стоп -- останавливает работу cчётчика func (sf *ВремОбрат) Стоп() { sf.еслиРаботает.Сброс() } // Получ -- возвращает число оставшихся сек func (sf *ВремОбрат) Получ() int { return sf.времяЗнач.Get() } // устанавливает число оставшихся сек func (sf *ВремОбрат) parse(val string) error { sf.блок.Lock() defer sf.блок.Unlock() if val == "" { return fmt.Errorf("CountTime.parse(): val is empty") } sf.парсер.Parse(val) _val := sf.парсер.Hour().Get()*3600 + sf.парсер.Min().Get()*60 + sf.парсер.Min().Get() sf.времяЗнач.Уст(_val) _val = int(time.Now().UTC().Unix()) + sf.времяЗнач.Get() sf.времПорог.Уст(_val) return nil } // устанавливает число оставшихся сек func (sf *ВремОбрат) set_val(val int) error { sf.блок.Lock() defer sf.блок.Unlock() if val < 0 { return fmt.Errorf("CountTime.set_val(): val(%v)<0", val) } sf.времяЗнач.Уст(val) { // Обновить локальные счётчики if val < 60 { sf.парсер.Hour().Reset() sf.парсер.Min().Reset() sf.парсер.Sec().Set(val) return nil } if 60 < val && val < 3600 { sf.парсер.Hour().Reset() iMin := val / 60 sf.парсер.Min().Set(iMin) val -= iMin * 60 sf.парсер.Sec().Set(val) return nil } sf.парсер.Hour().Set(val / 3600) val -= sf.парсер.Hour().Get() * 3600 sf.парсер.Min().Set(val / 60) val -= sf.парсер.Min().Get() * 60 sf.парсер.Sec().Set(val) // val = int(time.Now().UTC().Unix()) + sf.val.Get() } return nil } // Стр -- возвращает строковое представление оставшихся сек func (sf *ВремОбрат) Стр() string { sf.блок.RLock() defer sf.блок.RUnlock() timeNow := time.Now().UTC().Unix() val := sf.времПорог.Get() - int(timeNow) go sf.set_val(val) return sf.парсер.String() } // КаналСиг -- возвращает канал чтения тиков func (sf *ВремОбрат) КаналСиг() <-chan int { return sf.канВызов } func (сам *ВремОбрат) закрыть() { <-сам.кнт.Done() сам.блок.Lock() defer сам.блок.Unlock() if !сам.еслиРаботает.Получ() { return } сам.еслиРаботает.Сброс() close(сам.канТик) }