Add consistent monotonic day counter - get_day_count()
authorAuke Kok <sofar@foo-projects.org>
Sun, 6 Mar 2016 20:02:21 +0000 (12:02 -0800)
committerest31 <MTest31@outlook.com>
Wed, 9 Mar 2016 00:45:23 +0000 (01:45 +0100)
I've written several experimental bits of code that revolve around the
need for a consistent calendar, but implementing one is extremely hard
in mods due to time changes and mods overriding core.get_timeofday(),
which will conflict.

The second part of the problem is that doing this from a mod requires
constant maintenance of a settings file.

An implementation in core is trivial, however, and solves all of
these problems at virtually no cost: No extra branches in server
steps, and a single branch when minetest.set_time_of_day(), which is
entirely reasonable.

We store the day_count value in env_meta.txt.

The use case is obvious: This change allows mods to create an actual
virtual calendar, or properly account for seasonal changes, etc..

We add a "/days" chatcommand that displays the current day count. No
permissions are needed. It can only retrieve the day count, not
modify it.

builtin/game/chatcommands.lua
doc/lua_api.txt
src/environment.cpp
src/environment.h
src/script/lua_api/l_env.cpp
src/script/lua_api/l_env.h

index 2c7a0b532837a63881014c6827edeaae4c23b9ba..0b197664081bae00a22916eb40c88be1f3c81482 100644 (file)
@@ -785,6 +785,13 @@ core.register_chatcommand("time", {
        end,
 })
 
+core.register_chatcommand("days", {
+       description = "Display day count",
+       func = function(name, param)
+               return true, "Current day is " .. core.get_day_count()
+       end
+})
+
 core.register_chatcommand("shutdown", {
        description = "shutdown server",
        privs = {server=true},
index 65af515788b3faf42cbf66959c1aeb7180439dbe..e2ab65ddcb4af77a4d310b9fb20b220ff3b82f59 100644 (file)
@@ -1998,6 +1998,8 @@ and `minetest.auth_reload` call the authetification handler.
     * `val` is between `0` and `1`; `0` for midnight, `0.5` for midday
 * `minetest.get_timeofday()`
 * `minetest.get_gametime()`: returns the time, in seconds, since the world was created
+* `minetest.get_day_count()`: returns number days elapsed since world was created,
+    * accounting for time changes.
 * `minetest.find_node_near(pos, radius, nodenames)`: returns pos or `nil`
     * `radius`: using a maximum metric
     * `nodenames`: e.g. `{"ignore", "group:tree"}` or `"default:dirt"`
index 6da376a15eeb673f54b67454df85f71a04faa7b1..0d00ed17043c8e4181460a84faea833be368e8df 100644 (file)
@@ -208,6 +208,8 @@ void Environment::setDayNightRatioOverride(bool enable, u32 value)
 void Environment::setTimeOfDay(u32 time)
 {
        MutexAutoLock lock(this->m_time_lock);
+       if (m_time_of_day > time)
+               m_day_count++;
        m_time_of_day = time;
        m_time_of_day_f = (float)time / 24000.0;
 }
@@ -238,8 +240,10 @@ void Environment::stepTimeOfDay(float dtime)
        bool sync_f = false;
        if (units > 0) {
                // Sync at overflow
-               if (m_time_of_day + units >= 24000)
+               if (m_time_of_day + units >= 24000) {
                        sync_f = true;
+                       m_day_count++;
+               }
                m_time_of_day = (m_time_of_day + units) % 24000;
                if (sync_f)
                        m_time_of_day_f = (float)m_time_of_day / 24000.0;
@@ -256,6 +260,13 @@ void Environment::stepTimeOfDay(float dtime)
        }
 }
 
+u32 Environment::getDayCount()
+{
+       // Atomic<u32> counter
+       return m_day_count;
+}
+
+
 /*
        ABMWithState
 */
@@ -727,6 +738,7 @@ void ServerEnvironment::saveMeta()
        args.setU64("lbm_introduction_times_version", 1);
        args.set("lbm_introduction_times",
                m_lbm_mgr.createIntroductionTimesString());
+       args.setU64("day_count", m_day_count);
        args.writeLines(ss);
        ss<<"EnvArgsEnd\n";
 
@@ -792,6 +804,12 @@ void ServerEnvironment::loadMeta()
        }
        m_lbm_mgr.loadIntroductionTimes(lbm_introduction_times, m_gamedef, m_game_time);
 
+       try {
+               m_day_count = args.getU64("day_count");
+       } catch (SettingNotFoundException &e) {
+               // If missing, start the day counter
+               m_day_count = 0;
+       }
 }
 
 void ServerEnvironment::loadDefaultMeta()
index 448ed70e658410c1cfc5503422eec88de154f76f..9b91a09289cd146a03d8d9aa1d82385ba5b212b8 100644 (file)
@@ -95,6 +95,8 @@ public:
 
        void setDayNightRatioOverride(bool enable, u32 value);
 
+       u32 getDayCount();
+
        // counter used internally when triggering ABMs
        u32 m_added_objects;
 
@@ -117,6 +119,9 @@ protected:
        // Overriding the day-night ratio is useful for custom sky visuals
        bool m_enable_day_night_ratio_override;
        u32 m_day_night_ratio_override;
+       // Days from the server start, accounts for time shift
+       // in game (e.g. /time or bed usage)
+       Atomic<u32> m_day_count;
        /*
         * Above: values managed by m_time_lock
        */
index f4ddc2afc3c1b9891c26b676934881d260e985b5..af89da9a1b841c0fafd50e5d0f1f1ac865f865c2 100644 (file)
@@ -561,6 +561,15 @@ int ModApiEnvMod::l_get_timeofday(lua_State *L)
        return 1;
 }
 
+// get_day_count() -> int
+int ModApiEnvMod::l_get_day_count(lua_State *L)
+{
+       GET_ENV_PTR;
+
+       lua_pushnumber(L, env->getDayCount());
+       return 1;
+}
+
 // get_gametime()
 int ModApiEnvMod::l_get_gametime(lua_State *L)
 {
@@ -1055,6 +1064,7 @@ void ModApiEnvMod::Initialize(lua_State *L, int top)
        API_FCT(set_timeofday);
        API_FCT(get_timeofday);
        API_FCT(get_gametime);
+       API_FCT(get_day_count);
        API_FCT(find_node_near);
        API_FCT(find_nodes_in_area);
        API_FCT(find_nodes_in_area_under_air);
index 0e385ffefd06c713505a68c27abc09fff4c55513..89dd7978f910f914ceef3b04bf093b2d099f3f81 100644 (file)
@@ -113,6 +113,9 @@ private:
        // get_gametime()
        static int l_get_gametime(lua_State *L);
 
+       // get_day_count() -> int
+       static int l_get_day_count(lua_State *L);
+
        // find_node_near(pos, radius, nodenames) -> pos or nil
        // nodenames: eg. {"ignore", "group:tree"} or "default:dirt"
        static int l_find_node_near(lua_State *L);