|
| 1 | +--------------------------------------------------------------------------------------- |
| 2 | +-- Module for date and time calculations |
| 3 | +-- |
| 4 | +-- Version 2.1.2 |
| 5 | + |
| 6 | +-- Copyright (C) 2013-2014, by Thijs Schreijer |
| 7 | +-- Licensed under MIT, http://opensource.org/licenses/MIT |
| 8 | +-- https://github.com/Tieske/date |
| 9 | +-- The MIT License (MIT) http://opensource.org/licenses/MIT |
| 10 | + |
| 11 | +-- Copyright (c) 2013-2017 Thijs Schreijer |
| 12 | +-- Copyright (c) 2018 Alexander Grist-Hucker, Igor Radovanovic |
| 13 | + |
| 14 | +-- lib based on Helix by Alexander Grist-Hucker, Igor Radovanovic 2018 |
| 15 | + |
| 16 | +-- due to UNIX time, normal os.time() cannnot be set before 1970, as 1st Jan 1970 is 0 |
| 17 | +-- the library fixes said issue. |
| 18 | + |
| 19 | + |
1 | 20 | nut.date = nut.date or {} |
2 | | -nut.date.cache = nut.date.cache or {} |
3 | | -nut.date.start = nut.date.start or os.time() |
| 21 | +nut.date.lib = nut.date.lib or include("thirdparty/sh_date.lua") |
| 22 | +nut.date.timeScale = nut.date.timeScale or nut.config.get("secondsPerMinute", 60) |
| 23 | +nut.date.dateObj = nut.date.dateObj or nut.date.lib() |
| 24 | +nut.date.start = nut.date.start or CurTime() |
4 | 25 |
|
5 | | -if (!nut.config) then |
| 26 | +if (not nut.config) then |
6 | 27 | include("nutscript/gamemode/core/sh_config.lua") |
7 | 28 | end |
8 | 29 |
|
9 | | -nut.config.add("year", 2015, "The starting year of the schema.", nil, { |
| 30 | +nut.config.add("year", 2021, "The starting year of the schema.", function(oldValue, newValue) |
| 31 | + if (SERVER and not nut.date.saving) then |
| 32 | + nut.date.update() |
| 33 | + nut.date.dateObj:setyear(newValue) |
| 34 | + nut.date.sync() |
| 35 | + end |
| 36 | +end, { |
10 | 37 | data = {min = 0, max = 4000}, |
11 | 38 | category = "date" |
12 | 39 | }) |
13 | 40 |
|
14 | | -nut.config.add("month", 1, "The starting month of the schema.", nil, { |
| 41 | +nut.config.add("month", 1, "The starting month of the schema.", function(oldValue, newValue) |
| 42 | + if (SERVER and not nut.date.saving) then |
| 43 | + nut.date.update() |
| 44 | + nut.date.dateObj:setmonth(newValue) |
| 45 | + nut.date.sync() |
| 46 | + end |
| 47 | +end, { |
15 | 48 | data = {min = 1, max = 12}, |
16 | 49 | category = "date" |
17 | 50 | }) |
18 | 51 |
|
19 | | -nut.config.add("day", 1, "The starting day of the schema.", nil, { |
| 52 | +nut.config.add("day", 1, "The starting day of the schema.", function(oldValue, newValue) |
| 53 | + if (SERVER and not nut.date.saving) then |
| 54 | + nut.date.update() |
| 55 | + nut.date.dateObj:setday(newValue) |
| 56 | + nut.date.sync() |
| 57 | + end |
| 58 | +end, { |
20 | 59 | data = {min = 1, max = 31}, |
21 | 60 | category = "date" |
22 | 61 | }) |
23 | 62 |
|
| 63 | +nut.config.add("secondsPerMinute", 60, "How many real life seconds it takes for a minute to pass in-game.", function(oldValue, newValue) |
| 64 | + if (SERVER and not nut.date.saving) then |
| 65 | + nut.date.updateTimescale(newValue) |
| 66 | + nut.date.sync() |
| 67 | + end |
| 68 | +end, { |
| 69 | + data = {min = 0.01, max = 120}, |
| 70 | + category = "date" |
| 71 | +}) |
| 72 | + |
| 73 | +nut.config.add("yearAppendix", "", "Add a custom appendix to your date, if you use a non-conventional calender", nil, { |
| 74 | + data = {form = "Generic"}, |
| 75 | + category = "date" |
| 76 | +}) |
| 77 | + |
24 | 78 | if (SERVER) then |
25 | | - function nut.date.get() |
26 | | - local unixTime = os.time() |
| 79 | + util.AddNetworkString("nutDateSync") |
27 | 80 |
|
28 | | - return (unixTime - (nut.date.start or unixTime)) + os.time({ |
29 | | - year = nut.config.get("year"), |
30 | | - month = nut.config.get("month"), |
31 | | - day = nut.config.get("day") |
32 | | - }) |
| 81 | + -- called upon server startup. Grabs the saved date data, or creates a new date instance, and sets it as the date object |
| 82 | + function nut.date.initialize() |
| 83 | + local currentDate = nut.data.get("date", nil, false, true) |
| 84 | + -- If we don't have date data already, use current defaults to create a new date data table |
| 85 | + if (not currentDate) then |
| 86 | + currentDate = { |
| 87 | + year = nut.config.get("year"), |
| 88 | + month = nut.config.get("month"), |
| 89 | + day = nut.config.get("day"), |
| 90 | + hour = tonumber(os.date("%H")) or 0, |
| 91 | + min = tonumber(os.date("%M")) or 0, |
| 92 | + sec = tonumber(os.date("%S")) or 0, |
| 93 | + } |
| 94 | + |
| 95 | + currentDate = nut.date.lib.serialize(nut.date.lib(currentDate)) |
| 96 | + |
| 97 | + nut.data.set("date", currentDate, false, true) -- save the new data |
| 98 | + end |
| 99 | + |
| 100 | + nut.date.timeScale = nut.config.get("secondsPerMinute", 60) |
| 101 | + nut.date.dateObj = nut.date.lib.construct(currentDate) -- update the date object with the initialized data |
33 | 102 | end |
34 | 103 |
|
35 | | - function nut.date.send(client) |
36 | | - netstream.Start(client, "dateSync", CurTime(), os.time() - nut.date.start) |
| 104 | + -- Called when date values have been manually changed, updating the date object. |
| 105 | + function nut.date.update() |
| 106 | + nut.date.dateObj = nut.date.get() |
| 107 | + nut.date.start = CurTime() |
37 | 108 | end |
38 | | -else |
39 | | - function nut.date.get() |
40 | | - local realTime = RealTime() |
41 | 109 |
|
42 | | - -- Add the starting time + offset + current time played. |
43 | | - return nut.date.start + os.time({ |
44 | | - year = nut.config.get("year"), |
45 | | - month = nut.config.get("month"), |
46 | | - day = nut.config.get("day") |
47 | | - }) + (realTime - (nut.joinTime or realTime)) |
| 110 | + -- This is an internal function that sets the amount of real life seconds in an ingame minute. While you can use this function, |
| 111 | + --you probably shouldn't, and rather use the ingame config. |
| 112 | + function nut.date.updateTimescale(secondsPerMinute) |
| 113 | + nut.date.update() |
| 114 | + nut.date.timeScale = secondsPerMinute |
48 | 115 | end |
49 | 116 |
|
50 | | - netstream.Hook("dateSync", function(curTime, offset) |
51 | | - offset = offset + (CurTime() - curTime) |
| 117 | + --Syncs the current date with the client/s. This allows the players to have proper date representation, such as in the F1menu. |
| 118 | + function nut.date.sync(client) |
| 119 | + net.Start("nutDateSync") |
| 120 | + net.WriteFloat(nut.date.timeScale) |
| 121 | + net.WriteTable(nut.date.dateObj) |
| 122 | + net.WriteFloat(nut.date.start) |
| 123 | + |
| 124 | + if (client) then |
| 125 | + net.Send(client) |
| 126 | + else |
| 127 | + net.Broadcast() |
| 128 | + end |
| 129 | + end |
| 130 | + |
| 131 | + -- saves the current in-game date data. |
| 132 | + function nut.date.save() |
| 133 | + nut.date.saving = true -- prevents from the function from being called before it finishes. |
| 134 | + |
| 135 | + nut.date.update() |
| 136 | + |
| 137 | + nut.data.set("date", nut.date.lib.serialize(nut.date.dateObj), false, true) -- saves the current data object |
| 138 | + |
| 139 | + -- update config to reflect current saved date |
| 140 | + nut.config.set("year", nut.date.dateObj:getyear()) |
| 141 | + nut.config.set("month", nut.date.dateObj:getmonth()) |
| 142 | + nut.config.set("day", nut.date.dateObj:getday()) |
| 143 | + |
| 144 | + nut.date.saving = nil -- allows the date to be saved again |
| 145 | + end |
| 146 | +else |
| 147 | + net.Receive("nutDateSync", function() -- set the clientside values to the updated serverside date values |
| 148 | + nut.date.timeScale = net.ReadFloat() |
| 149 | + nut.date.dateObj = nut.date.lib.construct(net.ReadTable()) |
| 150 | + nut.date.start = net.ReadFloat() |
| 151 | + print("synced") |
| 152 | + PrintTable(nut.date.dateObj) |
| 153 | + end) |
| 154 | +end |
| 155 | + |
| 156 | +--- Returns the currently set date. |
| 157 | +function nut.date.get() |
| 158 | + -- CurTime increases in value by 1 every second. By getting the difference in seconds between now and the date object initialization, |
| 159 | + local minutesSinceStart = (CurTime() - nut.date.start) / nut.date.timeScale --and divide it by the timescale, we get the minutes elapsed to add to the date start |
| 160 | + |
| 161 | + return nut.date.dateObj:copy():addminutes(minutesSinceStart) |
| 162 | +end |
| 163 | + |
| 164 | +--- Returns a string formatted version of a date. |
| 165 | +function nut.date.getFormatted(format, currentDate) |
| 166 | + return (currentDate or nut.date.get()):fmt(format) |
| 167 | +end |
| 168 | + |
| 169 | +if SERVER then |
| 170 | + hook.Add("InitializedSchema", "nutInitializeTime", function() |
| 171 | + nut.date.initialize() |
| 172 | + end) |
| 173 | + |
| 174 | + hook.Add("PlayerInitialSpawn", "nutDateSync", function(client) |
| 175 | + nut.date.sync(client) |
| 176 | + end) |
52 | 177 |
|
53 | | - nut.date.start = offset |
| 178 | + hook.Add("SaveData", "nutDateSave", function() |
| 179 | + nut.date.save() |
54 | 180 | end) |
55 | 181 | end |