1
0
mirror of https://github.com/metrostroi-repo/MetrostroiAddon.git synced 2026-05-02 00:42:29 +00:00
Files
MetrostroiAddon/lua/metrostroi/sh_time.lua
g_brzhezinskiy 1d05caf866 init
2021-01-02 12:51:45 +03:00

433 lines
17 KiB
Lua

if CLIENT then
local function getTime()
return os.time()+GetGlobalFloat("MetrostroiTimeOffset",0)
end
function Metrostroi.GetTimedT(notsync)
local T0 = GetGlobalFloat("MetrostroiT0",os.time())+GetGlobalFloat("MetrostroiTY")
local T1 = GetGlobalFloat("MetrostroiT1",CurTime())
local dT
if notsync then
dT = (os.time()-T0) - (CurTime()-T1)
else
dT = (os.time()-T0 + (CurTime() % 1.0)) - (CurTime()-T1)
end
return dT
end
function Metrostroi.GetSyncTime(notsync)
return getTime()-Metrostroi.GetTimedT(notsync)
end
timer.Simple(0,function()
net.Start("MetrostroiUpdateTimeSync")
net.SendToServer()
end)
return
end
local C_TimeOffset = CreateConVar("metrostroi_time_offset",0,FCVAR_ARCHIVE,"Server time offset in seconds")
local C_TimeOld = CreateConVar("metrostroi_time_old",0,FCVAR_ARCHIVE,"Enables old time system without codepoints")
local function getTime()
return os.time()+C_TimeOffset:GetFloat()
end
local function UpdateTimeSync()
--if GetGlobalFloat("MetrostroiT0",0) == 0 then
local year = os.time{hour=3,day=1,month=1,year=1971}
SetGlobalFloat("MetrostroiTY",year*math.ceil((os.time())/year))
SetGlobalFloat("MetrostroiT0",os.time()-GetGlobalFloat("MetrostroiTY"))
SetGlobalFloat("MetrostroiT1",CurTime())
SetGlobalFloat("MetrostroiTimeOffset",C_TimeOffset:GetFloat())
--[[else
print"GETSECOND"
SetGlobalFloat("MetrostroiT0",GetGlobalFloat("MetrostroiT0"))
SetGlobalFloat("MetrostroiT1",GetGlobalFloat("MetrostroiT1"))
end]]
end
timer.Create("metrostroi_time_update",60,0,UpdateTimeSync)
util.AddNetworkString("MetrostroiUpdateTimeSync")
net.Receive("MetrostroiUpdateTimeSync",UpdateTimeSync)
cvars.AddChangeCallback("metrostroi_time_offset",UpdateTimeSync,"MetrostroiUpdateTimeSync")
hook.Add("PlayerInitialSpawn","metrostroi_time_sync",UpdateTimeSync)
UpdateTimeSync()
local function tonumberVar(...)
local out = {}
for i,num in ipairs{...} do out[i] = tonumber(num) or num ~= "" and num end
return unpack(out)
end
local message =[[
metrostroi_time_set commang usage:
Date or time in DD.MM.YYYY HH:MM:SS format
Time offset in +24 or -24 format
Seconds and 12-hours(AM/PM) are optional
Examples:
metrostroi_time_set 12:00
metrostroi_time_set 01.01.2019
metrostroi_time_set 27.03.2019 10:14:30
metrostroi_time_set 26.10.1985 9:00AM
metrostroi_time_set +3
metrostroi_time_set 0 to reset]]
concommand.Add("metrostroi_time_set",function(ply,_,_,fargs)
if IsValid(ply) then return end
local tMinArr = os.date("*t")
local tArr = os.date("!*t")
local GMTMin = os.time(tMinArr)-os.time(tArr)
local timeAdd = tonumber(string.match(fargs,"^+?-?[012]?%d$"))
if timeAdd then
if -24 <= timeAdd and timeAdd <= 24 then
RunConsoleCommand("metrostroi_time_offset",timeAdd*3600)
RunConsoleCommand("metrostroi_time")
else
print(message)
end
else
local H,M,S,twH = tonumberVar(string.match(fargs,"(%d?%d):(%d%d):?(%d?%d?)%s?(P?A?M?)"))
if H and M then
if twH and twH ~= "AM" and twH ~= "PM" then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\n12-hours parameter must be AM or PM\n") return end
if H < 0 or (not twH and H > 24 or twH and H > 12) then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nHours must be in 0.."..(twH and 12 or 23).." range\n") return end
if M < 0 or M > 59 then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nMinutes must be in 0..59 range\n") return end
if S and (S < 0 or S > 59) then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nSeconds must be in 0..59 range\n") return end
local isPM = twH and twH == "PM"
if twH and H == 12 then H = 0 isPM = not isPM end
tArr.changed = true
tArr.hour = (isPM and H+12 or H)
tArr.min = M
tArr.sec = S or tArr.sec
end
local d,m,y = tonumberVar(string.match(fargs,"(%d%d)%.(%d%d?)%.(%d%d%d?%d?)"))
if d and m and y then
local maxDays = m == 2 and (y%4 == 0 and 29 or 28) or (m < 8 and m%2==1 or m >= 8 and m%2==0) and 31 or 30
if y < 1970 or y > 2999 then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nYear must be in 1970..2999 range\n") return end
if d < 1 or d > maxDays then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nDay must be in 1.."..maxDays.." range\n") return end
if m < 1 or m > 12 then MsgC(Color(255,0,0),"Bad metrostroi_time_set commang usage.\nMonth must be in 1..12 range\n") return end
tArr.changed = true
tArr.day = d
tArr.month = m
tArr.year = y
end
if not tArr.changed then print(message) return end
RunConsoleCommand("metrostroi_time_offset",-os.time()+os.time(tArr)+GMTMin)
RunConsoleCommand("metrostroi_time")
end
end,nil,"Sets current server date and time. metrostroi_time to more info.")
function Metrostroi.GetTimedT(notsync)
local T0 = GetGlobalFloat("MetrostroiT0",os.time())+GetGlobalFloat("MetrostroiTY")
local T1 = GetGlobalFloat("MetrostroiT1",CurTime())
local dT
if notsync then
dT = (os.time()-T0) - (CurTime()-T1)
else
dT = (os.time()-T0 + (CurTime() % 1.0)) - (CurTime()-T1)
end
return dT
end
function Metrostroi.GetSyncTime(notsync)
return getTime()-Metrostroi.GetTimedT(notsync)
end
local CV_PassScale = CreateConVar("metrostroi_passengers_scale",50,FCVAR_ARCHIVE,"Global passengers scale")
concommand.Add("metrostroi_time", function(ply, _, args)
local time = Metrostroi.GetSyncTime()
if IsValid(ply) then
ply:PrintMessage(HUD_PRINTCONSOLE, os.date("!Server date: %d.%m.%Y Server time: %H:%M:%S ",time)..Format("Current scale %.1f (%d%%)",Metrostroi.PassengersScale,Metrostroi.PassengersScale/CV_PassScale:GetFloat()*100))
--[=[local t = (time/60)%(60*24)
local printed = false
local train = ply:GetTrain()
if IsValid(train) and train.Schedule then
for k,v in ipairs(train.Schedule) do
local prefix = ""
if (not printed) and (t < v[3]) then
prefix = ">>>>"
printed = true
end
ply:PrintMessage(HUD_PRINTCONSOLE,
Format(prefix.."\t[%03d][%s] %02d:%02d:%02d",v[1],
Metrostroi.StationNames[v[1]] or "N/A",
math.floor(v[3]/60)%24,
math.floor(v[3])%60,
math.floor(v[3]*60)%60))
end
end]=]
else
print(os.date("!Server date: %d.%m.%Y Server time: %H:%M:%S ",time)..Format("Current scale %.1f (%d%%)",Metrostroi.PassengersScale,Metrostroi.PassengersScale/CV_PassScale:GetFloat()*100))
end
end,nil,"Prints the current server time.")
Metrostroi.CodePoints = Metrostroi.CodePoints or {
{23,1,0.2},
{5.5,8.5,3},
{10,13,1},
{16,18,3},
{20.5,22.5,0.5},
}
function Metrostroi.GetPassengersScale(time)
for i,tA in ipairs(Metrostroi.CodePoints) do
local pA,nA = (Metrostroi.CodePoints[i-1] or Metrostroi.CodePoints[#Metrostroi.CodePoints]),Metrostroi.CodePoints[i]
local pTime,nTime = pA[2],nA[2]
if pTime > nTime and (time < pTime and time > nTime) or pTime < nTime and (time < pTime or time > nTime) then continue end
local time1,time2 = tA[1],tA[2]
local pPoint, nPoint = pA[3],nA[3]
if not pPoint then pPoint = Metrostroi.CodePoints[#Metrostroi.CodePoints] end
if not nPoint then nPoint = Metrostroi.CodePoints[1] end
if time1 > time2 and time >= time1 then time2 = time1+24+(time2-time1) end
if time1 > time2 and time <= time2 then time1 = time2-24-(time2-time1) end
if pPoint > nPoint then
return math.Clamp(pPoint + (time-time1)/(time2-time1)*(nPoint-pPoint),nPoint,pPoint)*CV_PassScale:GetFloat()
else
return math.Clamp(pPoint + (time-time1)/(time2-time1)*(nPoint-pPoint),pPoint,nPoint)*CV_PassScale:GetFloat()
end
end
return CV_PassScale:GetFloat()
end
local function timeStr(time)
return Format("%02d:%02d",math.floor(time),math.floor(time*60)%60)
end
local function appendRight(str,sz)
return str..string.rep(" ",sz-#str)
end
local function printArray(id)
if #Metrostroi.CodePoints == 0 then
print("No codepoints array. Current scale:"..Metrostroi.GetPassengersScale(0))
return
end
for i,point in pairs(Metrostroi.CodePoints) do
print(Format("[% 2d] %s-%s scale from %.2f to %.2f%s",i,timeStr(point[1]),timeStr(point[2]),(Metrostroi.CodePoints[i-1] or Metrostroi.CodePoints[#Metrostroi.CodePoints])[3],point[3],i == id and " <" or ""))
end
print("Time scale array:")
for x=0,4 do
for i=x*5,math.min(23.5,x*5+4) do Msg(appendRight(Format("%d",i),10)) end
Msg("\n")
for i=x*5,math.min(23.5,x*5+4.5),0.5 do Msg(appendRight(Format("%.2f",Metrostroi.GetPassengersScale(i)/CV_PassScale:GetFloat()),5)) end
Msg("\n")
end
end
concommand.Add("metrostroi_time_codepoints",function(ply,_,_,fargs)
if IsValid(ply) then return end
printArray()
end,nil,"Print current codepoints array")
local function getTime(str,st)
local _,en,H,M,twH = tonumberVar(string.find(str,"(%d?%d):?(%d?%d?)%s?(P?A?M?)",st))
if H then
if twH and twH ~= "AM" and twH ~= "PM" then MsgC(Color(255,0,0),"Bad metrostroi_time_codepoint_add commang usage.\n12-hours parameter must be AM or PM\n") return end
if H < 0 or (not twH and H > 24 or twH and H > 12) then MsgC(Color(255,0,0),"Bad metrostroi_time_codepoint_add commang usage.\nHours must be in 0.."..(twH and 12 or 23).." range\n") return end
if M and (M < 0 or M > 59) then MsgC(Color(255,0,0),"Bad metrostroi_time_codepoint_add commang usage.\nMinutes must be in 0..59 range\n") return end
local isPM = twH and twH == "PM"
if twH and H == 12 then H = 0 isPM = not isPM end
if isPM then H = H+12 end
return M and H+M/60 or H,en
end
end
local function checkTime(v1,v2,afterMid,id,arr)
for i,point in ipairs(arr or Metrostroi.CodePoints) do
if i == id then continue end
if not afterMid and point[1] == v1 and point[2] == v2 or afterMid and point[1] == v2 and point[2] == v1
or point[1] < point[2] and (
point[1] < v1 and v1 < point[2] or
point[1] < v2 and v2 < point[2] or
not afterMid and point[1] > v1 and point[2] < v2 or
afterMid and point[1] < v1 and point[2] > v2
) or point[1] > point[2] and (point[1] < v2 or point[2] > v1) then
print("Time overlap!")
print(Format("[% 2d] %s-%s our %s-%s",i,timeStr(point[1]),timeStr(point[2]),timeStr(v1),timeStr(v2)))
return false
end
end
return not id or (arr or Metrostroi.CodePoints)[id]
end
concommand.Add("metrostroi_time_add",function(ply,_,_,fargs)
if IsValid(ply) then return end
local v1, e1 = getTime(fargs)
local v2, e2 = getTime(fargs,e1)
local val = e2 and tonumber(string.sub(fargs,e2,-1))
if not v1 or not v2 or not val then
print("metrostroi_time_add usage:\nEnter scale change start and end time and then target scale at end of this time\nExamples:\nmetrostroi_time_add 10:00 11:00 2\nmetrostroi_time_add 12 13 1")
return
end
local afterMid = v1 > v2
if afterMid then
local v = v1
v1 = v2
v2 = v
end
if not checkTime(v1,v2,afterMid) then return end
if afterMid then
table.insert(Metrostroi.CodePoints,{v2,v1,val})
else
table.insert(Metrostroi.CodePoints,{v1,v2,val})
end
table.sort(Metrostroi.CodePoints,function(a,b) return a[2] < b[2] end)
for i,v in ipairs(Metrostroi.CodePoints) do
if afterMid and v1 == v[2] and v2 == v[1] or not afterMid and v1 == v[1] and v2 == v[2] then
printArray(i)
print("Added at id "..i)
return
end
end
end,nil,"Adds a new codepoint. metrostroi_time_add to more info.")
concommand.Add("metrostroi_time_edit",function(ply,_,_,fargs)
if IsValid(ply) then return end
local _,e1,id = tonumberVar(string.find(fargs,"([^%s]+)"))
local v1, e2 = getTime(fargs,e1 and e1+1)
local v2, e3 = getTime(fargs,e2)
local val = e3 and tonumber(string.sub(fargs,e3,-1))
if not id or not v1 or not v2 or not val then
print("metrostroi_time_edit usage:\nEnter scale change start and end time and then target scale at end of this time\nExamples:\nmetrostroi_time_edit 10:00 11:00 2\nmetrostroi_time_edit 12 13 1")
return
end
if not Metrostroi.CodePoints[id] then
print("Codepoint at "..id.." id is not found!")
return
end
local afterMid = v1 > v2
if afterMid then
local v = v1
v1 = v2
v2 = v
end
local points = checkTime(v1,v2,afterMid,id)
if not points then return end
if afterMid then
points[1] = v2
points[2] = v1
points[3] = val
else
points[1] = v1
points[2] = v2
points[3] = val
end
table.sort(Metrostroi.CodePoints,function(a,b) return a[2] < b[2] end)
printArray(id)
print("Edited id "..id)
end,nil,"Edits an exist codepoint. metrostroi_time_edit to more info.")
concommand.Add("metrostroi_time_remove",function(ply,_,_,fargs)
if IsValid(ply) then return end
local id = tonumber(fargs)
if not id then
print("metrostroi_time_remove usage:\nEnter codepoint id to remove it\nExamples:\nmetrostroi_time_remove 2")
return
end
if not Metrostroi.CodePoints[id] then
print("Codepoint at "..id.." id is not found!")
return
end
table.remove(Metrostroi.CodePoints,id)
table.sort(Metrostroi.CodePoints,function(a,b) return a[2] < b[2] end)
printArray(id-1)
print("Removed id "..id)
end,nil,"Removes an exist codepoint. metrostroi_time_remove to more info.")
concommand.Add("metrostroi_time_clear",function(ply,_,_,fargs)
if IsValid(ply) then return end
Metrostroi.CodePoints = {}
printArray()
print("Codepoints array cleared ")
end,nil,"Fully clears codepoint array.")
concommand.Add("metrostroi_time_reset",function(ply,_,_,fargs)
if IsValid(ply) then return end
Metrostroi.CodePoints = {
{23,1,0.2},
{5.5,8.5,3},
{10,13,1},
{16,18,3},
{20.5,22.5,0.5},
}
printArray()
print("Codepoints array cleared ")
end,nil,"Resets codepoint array to default.")
function Metrostroi.LoadCodepoints()
local arrayData = file.Read("metrostroi_data/time_codepoints.txt")
local arr
if arrayData then
arr = {}
for v1,v2,val in string.gmatch(arrayData,"([^%s]+)%s*([^%s]+)%s*([^\n\r]+)") do
v1,v2,val = tonumberVar(v1,v2,val)
if not v1 or not v2 or not val then
arr = false
break
end
table.insert(arr,{v1,v2,val})
end
end
if arr then
--table.sort(arr,function(a,b) return a[2] < b[2] end)
local good = true
for i,point in ipairs(arr) do
if point[1] > point[2] and not checkTime(point[2],point[1],true,i,arr) or point[1] < point[2] and not checkTime(point[1],point[2],false,i,arr) then
good = false
break
end
end
if good then
Metrostroi.CodePoints = arr
print("Metrostroi: Loaded time codepoints")
return
end
end
Metrostroi.CodePoints = {
{23,1,0.2},
{5.5,8.5,3},
{10,13,1},
{16,18,3},
{20.5,22.5,0.5},
}
print("Metrostroi: Loaded default time codepoints")
end
function Metrostroi.SaveCodepoints()
if not file.Exists("metrostroi_data","DATA") then
file.CreateDir("metrostroi_data")
end
local arrayData = ""
for i,v in ipairs(Metrostroi.CodePoints) do
arrayData = arrayData..Format("%f %f %f\n",v[1],v[2],v[3])
end
file.Write("metrostroi_data/time_codepoints.txt",arrayData)
print("Metrostroi: Saved time codepoints")
end
Metrostroi.LoadCodepoints()
concommand.Add("metrostroi_time_save", function(ply, _, args)
if IsValid(ply) then return end
Metrostroi.SaveCodepoints()
end,nil,"Save current codepoint array.")
concommand.Add("metrostroi_time_load", function(ply, _, args)
if IsValid(ply) then return end
Metrostroi.LoadCodepoints()
end,nil,"Load current codepoint array.")
local function getScale()
if C_TimeOld:GetBool() then
Metrostroi.PassengersScale = CV_PassScale:GetFloat()
else
Metrostroi.PassengersScale = Metrostroi.GetPassengersScale(Metrostroi.GetSyncTime()%86400/3600)
end
end
timer.Create("PassScaleChecker", 10, 0, getScale)
getScale()
cvars.AddChangeCallback("metrostroi_time_old",getScale,"PassScaleChecker")