fully implement custom timer logic

Addresses https://gitlab.com/4w/mtimer/-/issues/10
This commit is contained in:
Dirk Sohler 2021-02-12 20:23:40 +01:00
parent 8b1664111f
commit 55dd5b1d9a
No known key found for this signature in database
GPG Key ID: B9751241BD7D4E1A
9 changed files with 195 additions and 22 deletions

@ -46,6 +46,26 @@ Where needed, apply buttons are placed. So instead of having to leave the formsp
In the timer format configuration formspec shown above a text area is used where pressing enter goes to a new line so a button for applying the changes is shown. In the timer format configuration formspec shown above a text area is used where pressing enter goes to a new line so a button for applying the changes is shown.
## Custom Timer
mTimer comes with a freely configurable custom timer that can run in three different modes and shows its current state and time as applicable. The formats for the three states can be configured individually.
The four time unput fields on the middle left are used for days, hours, minutes, and seconds and the table with the variables shows the set value and not the current value because the current value changes depending on the timer state and mode.
![Custom Timer](screenshots/custom_timer.png)
In *Countdown* mode the custom timer counts down from the time that was set using the time input fields. In *Timer* mode the custom timer counts up to the value that was set. And in *Continuous Run* mode the custom timer counts up from when it was started, adding the time that was set to the starting point calculation.
After the custom timer was set up it needs to be started. Starting (as well as stopping or restarting) it is done using the defined chat commands.
```
/mtimer ctstart -- Start the timer if not running
/mtimer ctstop -- Stop the timer if running
/mtimer ctrestart -- Restart the timer if running
```
If the custom timer finished the format changes to the “finished” value. Please note that the timer is still running if this message is shown. To stop the timer you need to invoke the corresponding chat command. Only if the “stopped” format is used the timer is actually stopped (i.e. most of its logic isnt running).
## Default configuration (interesting for server admins) ## Default configuration (interesting for server admins)
The default configuration is loaded on server start and applied when a new player joins or an already existing player sets any (or all) of the mTimer values to the default value via the main menu or one of the specific dialog formspecs. The default configuration is loaded on server start and applied when a new player joins or an already existing player sets any (or all) of the mTimer values to the default value via the main menu or one of the specific dialog formspecs.

@ -74,12 +74,22 @@ Host Time=Hostzeit
Running=Timer läuft Running=Timer läuft
Stopped=Angehalten Stopped=Angehalten
Finished=Abgeschlossen Finished=Abgeschlossen
Countdown=Herunterzählen Countdown=Countdown
Timer Mode=Heraufzählen Timer Mode=Zielzeit
Continuous Run=Dauerlauf Continuous Run=Dauerlauf
Used Value=Benutzter Wert
As configured=Wie konfiguriert
The timer is stopped=Der Timer ist angehalten The timer is stopped=Der Timer ist angehalten
The timer has finished=Der Timer hat sein Ziel erreicht The timer has finished=Der Timer hat sein Ziel erreicht
Used Value=Genutzter Wert Configure the custom timer=Individuellen Timer konfigurieren
Start the custom timer=Individuellen Timer starten
Stop stop custom timer=Individuellen Timer anhalten
Restart the custom timer=Individuellen Timer neu starten
The custom timer is already running=Der individuelle Timer läuft bereits
The custom timer is not running=Der individuelle Timer läuft nicht
The custom timer was started=Der individuelle Timer wurde gestartet
The custom timer was stopped=Der individuelle Timer wurde angehalten
The custom timer was restarted=Der individuelle timer wurde neu gestartet
# Default Timer Format # Default Timer Format
Current Date: @1=Aktuelles Datum: @1 Current Date: @1=Aktuelles Datum: @1

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 57 KiB

@ -2,6 +2,7 @@ local m = mtimer
local S = m.translator local S = m.translator
local d = m.dialog local d = m.dialog
local cs = minetest.chat_send_player local cs = minetest.chat_send_player
local ds = minetest.deserialize
-- Colorize a command sequence -- Colorize a command sequence
@ -16,6 +17,46 @@ local command = function (command)
end end
local custom_timer_handling = function (name, action)
local player = minetest.get_player_by_name(name)
local player_meta = player:get_meta()
local current_timestamp = os.time(os.date('!*t'))
local ctv_key = m.meta.custom_timer_settings.key
local ctv = ds(player_meta:get_string(ctv_key))
if action == 'start' then
if ctv.running ~= true then
ctv.running = true
ctv.start_timestamp = current_timestamp
cs(name, S('The custom timer was started'))
else
cs(name, S('The custom timer is already running'))
end
end
if action == 'stop' then
if ctv.running ~= false then
ctv.running = false
ctv.start_timestamp = 0
cs(name, S('The custom timer was stopped'))
else
cs(name, S('The custom timer is not running'))
end
end
if action == 'restart' then
if ctv.running == true then
ctv.start_timestamp = current_timestamp
cs(name, S('The custom timer was restarted'))
else
cs(name, S('The custom timer is not running'))
end
end
player_meta:set_string(ctv_key, minetest.serialize(ctv))
end
-- Chat command -- Chat command
-- --
-- The `/mtimer` chat command opens the main menu and allows to directly open -- The `/mtimer` chat command opens the main menu and allows to directly open
@ -44,7 +85,7 @@ end
-- Providing unknown parameters has no effect. -- Providing unknown parameters has no effect.
minetest.register_chatcommand('mtimer', { minetest.register_chatcommand('mtimer', {
description = S('Configure timer display'), description = S('Configure timer display'),
params = '<vi/po/co/tz/in/re/st/sd/tt/help>', params = '<vi/po/co/tz/in/re/ht/st/sd/hs/os/tf/ct/help>',
func = function(name, parameters) func = function(name, parameters)
local action = parameters:match('%a+') local action = parameters:match('%a+')
@ -65,8 +106,13 @@ minetest.register_chatcommand('mtimer', {
if action == 'tf' then d.timer_format(name) end if action == 'tf' then d.timer_format(name) end
if action == 'ct' then d.custom_timer(name) end if action == 'ct' then d.custom_timer(name) end
if action == 'ctstart' then custom_timer_handling(name,'start') end
if action == 'ctstop' then custom_timer_handling(name,'stop') end
if action == 'ctrestart' then custom_timer_handling(name,'restart') end
if action == 'help' then if action == 'help' then
local message = { local message = {
command(' ')..S('Open Main Menu'),
command('vi')..S('Visibility'), command('vi')..S('Visibility'),
command('po')..S('Position'), command('po')..S('Position'),
command('co')..S('Color'), command('co')..S('Color'),
@ -79,8 +125,11 @@ minetest.register_chatcommand('mtimer', {
command('hs')..S('HUD Element Size'), command('hs')..S('HUD Element Size'),
command('os')..S('HUD Element Offset'), command('os')..S('HUD Element Offset'),
command('tf')..S('Timer Format'), command('tf')..S('Timer Format'),
command('ct')..S('Custom Timer'), '',
command(' ')..S('Open Main Menu') command('ct ')..S('Configure the custom timer'),
command('ctstart ')..S('Start the custom timer'),
command('ctstop ')..S('Stop stop custom timer'),
command('ctrestart')..S('Restart the custom timer')
} }
cs(name, table.concat(message, '\n')) cs(name, table.concat(message, '\n'))
end end

@ -305,7 +305,7 @@ mtimer.dialog.timer_format = function (player_name)
mtimer.show_formspec('mtimer:timer_format', { mtimer.show_formspec('mtimer:timer_format', {
title = S('Timer Format'), title = S('Timer Format'),
show_to = player_name, show_to = player_name,
height = 5.75, height = 6,
width = 8.5, width = 8.5,
formspec = { formspec = {
'textarea[0,0;6,2.5;format;;'..fe(timer_data.format)..']', 'textarea[0,0;6,2.5;format;;'..fe(timer_data.format)..']',
@ -318,6 +318,7 @@ mtimer.dialog.timer_format = function (player_name)
'label[0,1.3;'..S('Session Start Time')..'] label[2.5,1.3;{st}] label[4,1.3;'..fe(timer_data.session_start_time)..']', 'label[0,1.3;'..S('Session Start Time')..'] label[2.5,1.3;{st}] label[4,1.3;'..fe(timer_data.session_start_time)..']',
'label[0,1.7;'..S('Session Duration')..'] label[2.5,1.7;{sd}] label[4,1.7;'..fe(timer_data.session_duration)..']', 'label[0,1.7;'..S('Session Duration')..'] label[2.5,1.7;{sd}] label[4,1.7;'..fe(timer_data.session_duration)..']',
'label[0,2.1;'..S('Host Time')..'] label[2.5,2.1;{ht}] label[4,2.1;'..fe(timer_data.host_time)..']', 'label[0,2.1;'..S('Host Time')..'] label[2.5,2.1;{ht}] label[4,2.1;'..fe(timer_data.host_time)..']',
'label[0,2.5;'..S('Custom Timer')..'] label[2.5,2.5;{ct}] label[4,2.5;'..fe(S('As configured'))..']',
'container_end[]', 'container_end[]',
'container[6.25,0]', 'container[6.25,0]',
'button[0,0;2,0.5;apply;'..S('Apply')..']', 'button[0,0;2,0.5;apply;'..S('Apply')..']',
@ -340,6 +341,10 @@ mtimer.dialog.custom_timer = function (player_name)
local a_timer = ctv.timer_mode == 'timer' and 'x' or '' local a_timer = ctv.timer_mode == 'timer' and 'x' or ''
local a_continuous = ctv.timer_mode == 'continuous' and 'x' or '' local a_continuous = ctv.timer_mode == 'continuous' and 'x' or ''
local format_running = ctv.format.running or ''
local format_stopped = ctv.format.stopped or ''
local format_finished = ctv.format.finished or ''
mtimer.show_formspec('mtimer:custom_timer', { mtimer.show_formspec('mtimer:custom_timer', {
title = S('Custom Timer'), title = S('Custom Timer'),
show_to = player_name, show_to = player_name,
@ -354,9 +359,9 @@ mtimer.dialog.custom_timer = function (player_name)
'field_close_on_enter[v_minutes;false]', 'field_close_on_enter[v_minutes;false]',
'field_close_on_enter[v_seconds;false]', 'field_close_on_enter[v_seconds;false]',
'container[0,0]', 'container[0,0]',
' label[0,0.25;'..S('Running')..'] field[2.25,0;6.5,0.5;v_format_running;;'..fe(ctv.format.running)..']', ' label[0,0.25;'..S('Running')..'] field[2.25,0;6.5,0.5;v_format_running;;'..fe(format_running)..']',
' label[0,0.85;'..S('Stopped')..'] field[2.25,0.6;6.5,0.5;v_format_stopped;;'..fe(ctv.format.stopped)..']', ' label[0,0.85;'..S('Stopped')..'] field[2.25,0.6;6.5,0.5;v_format_stopped;;'..fe(format_stopped)..']',
' label[0,1.45;'..S('Finished')..'] field[2.25,1.2;6.5,0.5;v_format_finished;;'..fe(ctv.format.finished)..']', ' label[0,1.45;'..S('Finished')..'] field[2.25,1.2;6.5,0.5;v_format_finished;;'..fe(format_finished)..']',
' box[0,2;+linewidth,0.04;#ffffff]', ' box[0,2;+linewidth,0.04;#ffffff]',
'container_end[]', 'container_end[]',
'container[3.75,2.4]', 'container[3.75,2.4]',

@ -244,16 +244,92 @@ local get_session_duration = function (player_name)
end end
-- TODO: Implemet custom timer logic -- Custom timer
--
-- The timer values are loaded from the meta entry and are calculated on
-- runtime. The timer knows thre modes.
--
-- 1. continuous run
--
-- In this mode the time just runs in a continuous way and all that is
-- calculated is the difference between the current timestamp and the
-- timestamp from when the timer was started.
--
-- 2. countdown
--
-- For the countdown the given input values and the start timestamp are
-- added together, then the current timestamp is substracted from this.
-- This results in the difference getting smaller and eventually being
-- equal or less than 0. In this case the format is changed to the “timer
-- finished” format.
--
-- 3. timer
--
-- The timer mode calculated the difference like the continuous run mode
-- but also calculates a target from the starting timestamp and the input
-- values. When the difference is equal or larger than the target the format
-- is changed to the “timer finished” format.
--
-- The output is parsed outside the mode-specific calculations. Also every
-- format is parsed with the output time parts. This automatically allows all
-- time parts in all formats wich makes no sense because the difference
-- calculation is messed up outside the specific boundaries.
local get_custom_timer = function (player_name) local get_custom_timer = function (player_name)
local player = minetest.get_player_by_name(player_name) local player = minetest.get_player_by_name(player_name)
local player_meta = player:get_meta() local player_meta = player:get_meta()
local ctv = ds(player_meta:get_string(m.meta.custom_timer_settings.key)) local ctv = ds(player_meta:get_string(m.meta.custom_timer_settings.key))
local current_timestamp = os.time(os.date('!*t'))
local values = {} local running = ctv.running
values['formatted'] = 'PLACEHOLDER' local difference = 0
local format = ''
local finished = false
return values if running == false then format = ctv.format.stopped end
if running == true and finished == true then format=ctv.format.finished end
if running == true and finished == false then format=ctv.format.running end
-- Calculate seconds from the given input values
local value_seconds = 0
value_seconds = value_seconds + ctv.values.seconds
value_seconds = value_seconds + (ctv.values.minutes * 60)
value_seconds = value_seconds + (ctv.values.hours * 3600)
value_seconds = value_seconds + (ctv.values.days * 86400)
-- Continuous run
if running == true and ctv.timer_mode == 'continuous' then
difference = current_timestamp - (ctv.start_timestamp - value_seconds)
end
-- Countdown
if running == true and ctv.timer_mode == 'countdown' then
difference = (ctv.start_timestamp + value_seconds) - current_timestamp
if difference <= 0 then format = ctv.format.finished end
end
-- Timer
if running == true and ctv.timer_mode == 'timer' then
difference = current_timestamp - ctv.start_timestamp
local target = ctv.start_timestamp + value_seconds
if current_timestamp >= target then format = ctv.format.finished end
end
-- Parse values into time parts
local result_values = {
days = string.format('%02d', math.floor(difference/86400)),
hours = string.format('%02d', math.floor((difference % 86400)/3600)),
minutes = string.format('%02d', math.floor((difference % 3600)/60)),
seconds = string.format('%02d', math.floor((difference % 60)))
}
return {
formatted = format:gsub('{[a-z0-9]+}', {
['{days}'] = result_values.days or 0,
['{hours}'] = result_values.hours or 0,
['{minutes}'] = result_values.minutes or 0,
['{seconds}'] = result_values.seconds or 0
})
}
end end

@ -98,6 +98,5 @@ set('timer_format', table.concat({
S('Current Date: @1', '{rd}'), S('Current Date: @1', '{rd}'),
S('Ingame Time: @1', '{it}'), S('Ingame Time: @1', '{it}'),
S('Session Start: @1', '{st}'), S('Session Start: @1', '{st}'),
S('Session Duration: @1', '{sd}'), S('Session Duration: @1', '{sd}')
S('Custom Timer: @1', '{ct}')
}, '\n'), false) }, '\n'), false)

@ -218,18 +218,32 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
if hours < 0 then hours = 23 days = days - 1 end if hours < 0 then hours = 23 days = days - 1 end
if days < 0 then days = 0 end if days < 0 then days = 0 end
-- set formats -- set relevant values
ctv.format = { ctv.format = {
running = fields.v_format_running, running = fields.v_format_running,
stopped = fields.v_format_stopped, stopped = fields.v_format_stopped,
finished = fields.v_format_finished finished = fields.v_format_finished
} }
ctv.values = {
days = days,
hours = hours,
minutes = minutes,
seconds = seconds
}
-- (re)set (and quit) -- Set default values if requested
ctv.values = {days=days,hours=hours,minutes=minutes,seconds=seconds} if fields.default then
meta:set_string(attr.key, minetest.serialize(table.copy(ctv))) meta:set_string(attr.key, attr.default)
if fields.default then meta:set_string(attr.key, attr.default) end d.custom_timer(name)
if not fields.quit then d.custom_timer(name) end m.update_timer(name)
return
end
-- Set values if not quitting
if not fields.quit then
meta:set_string(attr.key, minetest.serialize(ctv))
d.custom_timer(name)
end
end end