From 7bca867dcb859af0e16818603293eeb2b67d7f44 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 12:02:32 +0100 Subject: [PATCH 01/29] Add persistence plugin --- plugins/persistence.lua | 170 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) create mode 100644 plugins/persistence.lua diff --git a/plugins/persistence.lua b/plugins/persistence.lua new file mode 100644 index 00000000..651c2590 --- /dev/null +++ b/plugins/persistence.lua @@ -0,0 +1,170 @@ +local PLUGIN = PLUGIN + +PLUGIN.name = "Persistence" +PLUGIN.desc = "Saves persisted entities through restarts." +PLUGIN.author = "Zoephix" +PLUGIN.entities = PLUGIN.entities or {} +PLUGIN.blacklist = PLUGIN.blacklist or {} + +-- Copy blacklist from the restrictions plugin +local restrictions = nut.plugin.list["restrictions"] +if (restrictions and restrictions.blacklist) then + PLUGIN.blacklist = restrictions.blacklist +end + +properties.Add( "persist", { + MenuLabel = "#makepersistent", + Order = 400, + MenuIcon = "icon16/link.png", + + Filter = function( self, ent, ply ) + + if ( ent:IsPlayer() ) then return false end + if ( PLUGIN.blacklist[ent:GetClass()] ) then return false end + if ( !gamemode.Call( "CanProperty", ply, "persist", ent ) ) then return false end + + return !ent:getNetVar( "persistent", false ) + + end, + + Action = function( self, ent ) + + self:MsgStart() + net.WriteEntity( ent ) + self:MsgEnd() + + end, + + Receive = function( self, length, ply ) + + local ent = net.ReadEntity() + if ( !IsValid( ent ) ) then return end + if ( !self:Filter( ent, ply ) ) then return end + + ent:setNetVar( "persistent", true ) + + -- Register the entity + PLUGIN.entities[#PLUGIN.entities + 1] = ent + + -- Add new log + nut.log.add(ply, "persistedEntity", ent ) + end + +} ) + +properties.Add( "persist_end", { + MenuLabel = "#stoppersisting", + Order = 400, + MenuIcon = "icon16/link_break.png", + + Filter = function( self, ent, ply ) + + if ( ent:IsPlayer() ) then return false end + if ( !gamemode.Call( "CanProperty", ply, "persist", ent ) ) then return false end + + return ent:getNetVar( "persistent", false ) + + end, + + Action = function( self, ent ) + + self:MsgStart() + net.WriteEntity( ent ) + self:MsgEnd() + + end, + + Receive = function( self, length, ply ) + + local ent = net.ReadEntity() + if ( !IsValid( ent ) ) then return end + if ( !properties.CanBeTargeted( ent, ply ) ) then return end + if ( !self:Filter( ent, ply ) ) then return end + + -- Remove entity from registration + for k, v in ipairs(PLUGIN.entities) do + if (v == entity) then + PLUGIN.entities[k] = nil + + break + end + end + + -- Remove persistent state + ent:setNetVar( "persistent", false ) + + -- Add new log + nut.log.add(ply, "unpersistedEntity", ent ) + end + +} ) + +if (SERVER) then + nut.log.addType("persistedEntity", function(client, entity) + return string.format("%s has persisted '%s'.", client:Name(), entity) + end) + + nut.log.addType("unpersistedEntity", function(client, entity) + return string.format("%s has removed persistence from '%s'.", client:Name(), entity) + end) + + -- Prevent from picking up persisted entities + function PLUGIN:PhysgunPickup(client, entity) + if (entity:getNetVar("persistent", false)) then + return false + end + end + + function PLUGIN:SaveData() + local data = {} + + for k, v in ipairs(self.entities) do + if (IsValid(v)) then + local entData = {} + entData.class = v:GetClass() + entData.pos = v:GetPos() + entData.angles = v:GetAngles() + entData.model = v:GetModel() + entData.skin = v:GetSkin() + entData.color = v:GetColor() + entData.material = v:GetMaterial() + entData.bodygroups = v:GetBodyGroups() + + local physicsObject = v:GetPhysicsObject() + if (IsValid(physicsObject)) then + entData.moveable = physicsObject:IsMoveable() + end + + data[#data +1] = entData + end + end + self:setData(data) + end + + function PLUGIN:LoadData() + for k, v in pairs(self:getData() or {}) do + local ent = ents.Create(v.class) + ent:SetPos(v.pos) + ent:SetAngles(v.angles) + ent:SetModel(v.model) + ent:SetSkin(v.skin) + ent:SetColor(v.color) + ent:SetMaterial(v.material) + ent:Spawn() + ent:Activate() + + for _, data in pairs(v.bodygroups) do + ent:SetBodygroup(data.id, data.num) + end + + local physicsObject = ent:GetPhysicsObject() + if (IsValid(physicsObject)) then + physicsObject:EnableMotion(ent.moveable or false) + end + + ent:setNetVar("persistent", true) + + self.entities[#self.entities + 1] = ent + end + end +end \ No newline at end of file From 2d1c2813ad8fcfc01b2c1e0e7b6bfcdb334b6510 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Sun, 14 Feb 2021 13:14:09 +0200 Subject: [PATCH 02/29] lua consistency newlines and || vs or --- gamemode/core/libs/sv_database.lua | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index 6a1789a1..d38d4a7d 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -76,10 +76,8 @@ modules.tmysql4 = { local queryStatus, queryError, affected, lastID, time, data = result.status, result.error, result.affected, result.lastid, result.time, result.data - if (queryStatus and queryStatus == true) then - if (callback) then - callback(data, lastID) - end + if (queryStatus and queryStatus == true) and callback then + callback(data, lastID) end else file.Write("nut_queryerror.txt", query) @@ -189,7 +187,7 @@ modules.mysqloo = { for k, db in pairs(nut.db.pool) do local queueSize = db:queueSize() - if (!lowest || queueSize < lowestCount) then + if (!lowest or queueSize < lowestCount) then lowest = db lowestCount = queueSize lowestIndex = k @@ -207,7 +205,7 @@ modules.mysqloo = { return setNetVar("dbError", system.IsWindows() and "Server is missing VC++ redistributables!" or "Server is missing binaries for mysqloo!") end - if (mysqloo.VERSION != "9" || !mysqloo.MINOR_VERSION || tonumber(mysqloo.MINOR_VERSION) < 1) then + if (mysqloo.VERSION != "9" or !mysqloo.MINOR_VERSION or tonumber(mysqloo.MINOR_VERSION) < 1) then MsgC(Color(255, 0, 0), "You are using an outdated mysqloo version\n") MsgC(Color(255, 0, 0), "Download the latest mysqloo9 from here\n") MsgC(Color(86, 156, 214), "https://github.com/syl0r/MySQLOO/releases") @@ -251,7 +249,7 @@ modules.mysqloo = { if (callback) then callback() end - + hook.Run("OnMySQLOOConnected") end end From 10a1cafaa9769bd782cdcf398957408d1e3ec8f9 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 12:20:01 +0100 Subject: [PATCH 03/29] Removed restrictions plugin alliances --- plugins/persistence.lua | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/plugins/persistence.lua b/plugins/persistence.lua index 651c2590..cf46adb0 100644 --- a/plugins/persistence.lua +++ b/plugins/persistence.lua @@ -3,14 +3,23 @@ local PLUGIN = PLUGIN PLUGIN.name = "Persistence" PLUGIN.desc = "Saves persisted entities through restarts." PLUGIN.author = "Zoephix" + +-- Storage for persisted map entities PLUGIN.entities = PLUGIN.entities or {} -PLUGIN.blacklist = PLUGIN.blacklist or {} --- Copy blacklist from the restrictions plugin -local restrictions = nut.plugin.list["restrictions"] -if (restrictions and restrictions.blacklist) then - PLUGIN.blacklist = restrictions.blacklist -end +-- Entities which are blocked from interaction +PLUGIN.blacklist = PLUGIN.blacklist or { + [ "func_button" ] = true, + [ "class C_BaseEntity" ] = true, + [ "func_brush" ] = true, + [ "func_tracktrain" ] = true, + [ "func_door" ] = true, + [ "func_door_rotating" ] = true, + [ "prop_door_rotating" ] = true, + [ "prop_static" ] = true, + [ "prop_dynamic" ] = true, + [ "prop_physics_override" ] = true, +} properties.Add( "persist", { MenuLabel = "#makepersistent", @@ -40,12 +49,12 @@ properties.Add( "persist", { local ent = net.ReadEntity() if ( !IsValid( ent ) ) then return end if ( !self:Filter( ent, ply ) ) then return end - - ent:setNetVar( "persistent", true ) - -- Register the entity + ent:setNetVar( "persistent", true ) + + -- Register the entity PLUGIN.entities[#PLUGIN.entities + 1] = ent - + -- Add new log nut.log.add(ply, "persistedEntity", ent ) end @@ -80,6 +89,8 @@ properties.Add( "persist_end", { if ( !IsValid( ent ) ) then return end if ( !properties.CanBeTargeted( ent, ply ) ) then return end if ( !self:Filter( ent, ply ) ) then return end + + ent:setNetVar( "persistent", false ) -- Remove entity from registration for k, v in ipairs(PLUGIN.entities) do @@ -90,9 +101,6 @@ properties.Add( "persist_end", { end end - -- Remove persistent state - ent:setNetVar( "persistent", false ) - -- Add new log nut.log.add(ply, "unpersistedEntity", ent ) end @@ -167,4 +175,4 @@ if (SERVER) then self.entities[#self.entities + 1] = ent end end -end \ No newline at end of file +end From b85c570a0275b3e16ea8006e02e2cc0e1cdbef9c Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 12:23:22 +0100 Subject: [PATCH 04/29] Update sv_database.lua --- gamemode/core/libs/sv_database.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index d38d4a7d..d6d3913b 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -76,7 +76,7 @@ modules.tmysql4 = { local queryStatus, queryError, affected, lastID, time, data = result.status, result.error, result.affected, result.lastid, result.time, result.data - if (queryStatus and queryStatus == true) and callback then + if (queryStatus and queryStatus == true and callback) then callback(data, lastID) end else From cbf0c42e85ccf9233e4ae70625c8a0ef2fb042a1 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Sun, 14 Feb 2021 14:19:22 +0200 Subject: [PATCH 05/29] whitespace --- gamemode/core/libs/sh_character.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gamemode/core/libs/sh_character.lua b/gamemode/core/libs/sh_character.lua index d8c78b02..a6694a1a 100644 --- a/gamemode/core/libs/sh_character.lua +++ b/gamemode/core/libs/sh_character.lua @@ -11,7 +11,7 @@ if (SERVER) then if (not nut.db) then include("sv_database.lua") end - + -- Fetches all the character names and stores -- them into a table so they only have to be fetched once if (#nut.char.names < 1) then From 57cc63e3b3d687afc9fae6b88f9cdb732b2b3ed6 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 14:42:17 +0100 Subject: [PATCH 06/29] Update sh_plugin.lua --- plugins/f1menu/sh_plugin.lua | 217 +++++++++++++++++++++++++---------- 1 file changed, 159 insertions(+), 58 deletions(-) diff --git a/plugins/f1menu/sh_plugin.lua b/plugins/f1menu/sh_plugin.lua index 7a401829..e29ca77f 100644 --- a/plugins/f1menu/sh_plugin.lua +++ b/plugins/f1menu/sh_plugin.lua @@ -1,68 +1,169 @@ -PLUGIN.name = "F1 Menu" -PLUGIN.author = "Cheesenut" -PLUGIN.desc = "A menu that shows up upon pressing F1." +local PANEL = {} + function PANEL:Init() + if (IsValid(nut.gui.info)) then + nut.gui.info:Remove() + end + + nut.gui.info = self + + self:SetSize(ScrW() * 0.6, ScrH() * 0.7) + self:Center() + + local suppress = hook.Run("CanCreateCharInfo", self) + + if (!suppress or (suppress and !suppress.all)) then + if (!suppress or !suppress.model) then + self.model = self:Add("nutModelPanel") + self.model:SetWide(ScrW() * 0.25) + self.model:Dock(LEFT) + self.model:SetFOV(50) + self.model.enableHook = true + self.model.copyLocalSequence = true + end + + if (!suppress or !suppress.info) then + self.info = self:Add("DPanel") + self.info:SetWide(ScrW() * 0.4) + self.info:Dock(RIGHT) + self.info:SetPaintBackground(false) + self.info:DockMargin(150, ScrH() * 0.2, 0, 0) + end + + if (!suppress or !suppress.name) then + self.name = self.info:Add("DLabel") + self.name:SetFont("nutHugeFont") + self.name:SetTall(60) + self.name:Dock(TOP) + self.name:SetTextColor(color_white) + self.name:SetExpensiveShadow(1, Color(0, 0, 0, 150)) + end -function PLUGIN:ShowHelp() return false end + if (!suppress or !suppress.desc) then + self.desc = self.info:Add("DTextEntry") + self.desc:Dock(TOP) + self.desc:SetFont("nutMediumLightFont") + self.desc:SetTall(28) + end -if (SERVER) then return end + if (!suppress or !suppress.time) then + self.time = self.info:Add("DLabel") + self.time:SetFont("nutMediumFont") + self.time:SetTall(28) + self.time:Dock(TOP) + self.time:SetTextColor(color_white) + self.time:SetExpensiveShadow(1, Color(0, 0, 0, 150)) + end -function PLUGIN:PlayerBindPress(client, bind, pressed) - if (bind:lower():find("gm_showhelp") and pressed) then - if (IsValid(nut.gui.menu)) then - nut.gui.menu:remove() - elseif (LocalPlayer():getChar()) then - vgui.Create("nutMenu") + if (!suppress or !suppress.money) then + self.money = self.info:Add("DLabel") + self.money:Dock(TOP) + self.money:SetFont("nutMediumFont") + self.money:SetTextColor(color_white) + self.money:SetExpensiveShadow(1, Color(0, 0, 0, 150)) + self.money:DockMargin(0, 10, 0, 0) + end + + if (!suppress or !suppress.faction) then + self.faction = self.info:Add("DLabel") + self.faction:Dock(TOP) + self.faction:SetFont("nutMediumFont") + self.faction:SetTextColor(color_white) + self.faction:SetExpensiveShadow(1, Color(0, 0, 0, 150)) + self.faction:DockMargin(0, 10, 0, 0) + end + + if (!suppress or !suppress.class) then + local class = nut.class.list[LocalPlayer():getChar():getClass()] + + if (class) then + self.class = self.info:Add("DLabel") + self.class:Dock(TOP) + self.class:SetFont("nutMediumFont") + self.class:SetTextColor(color_white) + self.class:SetExpensiveShadow(1, Color(0, 0, 0, 150)) + self.class:DockMargin(0, 10, 0, 0) + end + end + + hook.Run("CreateCharInfoText", self, suppress) end - return true + hook.Run("CreateCharInfo", self) end -end - -function PLUGIN:OnCharInfoSetup(infoPanel) - -- Get the model entity from the F1 menu. - if (not IsValid(infoPanel.model)) then return end - local mdl = infoPanel.model - local ent = mdl.Entity - local client = LocalPlayer() - - -- If the player is alive with a weapon, add a weapon model to the - -- character model in the F1 menu. - if (not IsValid(client) or not client:Alive()) then return end - local weapon = client:GetActiveWeapon() - if (not IsValid(weapon)) then return end - - local weapModel = ClientsideModel(weapon:GetModel(), RENDERGROUP_BOTH) - if (not IsValid(weapModel)) then return end - - weapModel:SetParent(ent) - weapModel:AddEffects(EF_BONEMERGE) - weapModel:SetSkin(weapon:GetSkin()) - weapModel:SetColor(weapon:GetColor()) - weapModel:SetNoDraw(true) - ent.weapon = weapModel - - -- Then, change the animation so the character model holds the weapon. - local act = ACT_MP_STAND_IDLE - local model = ent:GetModel():lower() - local class = nut.anim.getModelClass(model) - local tree = nut.anim[class] - - if (not tree) then return end - - local subClass = weapon.HoldType or weapon:GetHoldType() - subClass = HOLDTYPE_TRANSLATOR[subClass] or subClass - - if (tree[subClass] and tree[subClass][act]) then - local branch = tree[subClass][act] - local act2 = istable(branch) and branch[1] or branch - - if (isstring(act2)) then - act2 = ent:LookupSequence(act2) - else - act2 = ent:SelectWeightedSequence(act2) + + function PANEL:setup() + local char = LocalPlayer():getChar() + if (self.desc) then + self.desc:SetText(char:getDesc():gsub("#", "\226\128\139#")) + self.desc.OnEnter = function(this, w, h) + nut.command.send( + "chardesc", + (this:GetText():gsub("\226\128\139#", "#")) + ) + end + end + + if (self.name) then + self.name:SetText(LocalPlayer():Name():gsub("#", "\226\128\139#")) + hook.Add( + "OnCharVarChanged", + self, + function(panel, character, key, oldValue, value) + if (char ~= character) then return end + if (key ~= "name") then return end + + self.name:SetText(value:gsub("#", "\226\128\139#")) + end + ) end - ent:ResetSequence(act2) + if (self.money) then + self.money:SetText(L("charMoney", nut.currency.get(char:getMoney()))) + end + + if (self.faction) then + self.faction:SetText(L("charFaction", L(team.GetName(LocalPlayer():Team())))) + end + + if (self.time) then + local format = "%A, %d %B "..nut.config.get("year").." %T" + + self.time:SetText(L("curTime", nut.date.getFormatted(format))) + self.time.Think = function(this) + if ((this.nextTime or 0) < CurTime()) then + this:SetText(L("curTime", nut.date.getFormatted(format))) + this.nextTime = CurTime() + 0.5 + end + end + end + + if (self.class) then + local class = nut.class.list[char:getClass()] + if (class) then + self.class:SetText(L("charClass", L(class.name))) + end + end + + if (self.model) then + self.model:SetModel(LocalPlayer():GetModel()) + self.model.Entity:SetSkin(LocalPlayer():GetSkin()) + + for k, v in ipairs(LocalPlayer():GetBodyGroups()) do + self.model.Entity:SetBodygroup(v.id, LocalPlayer():GetBodygroup(v.id)) + end + + local ent = self.model.Entity + if (ent and IsValid(ent)) then + local mats = LocalPlayer():GetMaterials() + for k, v in pairs(mats) do + ent:SetSubMaterial(k - 1, LocalPlayer():GetSubMaterial(k - 1)) + end + end + end + + hook.Run("OnCharInfoSetup", self) end -end + function PANEL:Paint(w, h) + end +vgui.Register("nutCharInfo", PANEL, "EditablePanel") From c89281d9e284ecf6bf816a8f0df78f78e0d2f60d Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 14:44:53 +0100 Subject: [PATCH 07/29] Revert "Merge pull request #10 from Zoephix/1.2-wip" This reverts commit 944229f4eb9fc74af5dba8259c689ba28881e243. --- plugins/f1menu/sh_plugin.lua | 217 ++++++++++------------------------- 1 file changed, 58 insertions(+), 159 deletions(-) diff --git a/plugins/f1menu/sh_plugin.lua b/plugins/f1menu/sh_plugin.lua index e29ca77f..7a401829 100644 --- a/plugins/f1menu/sh_plugin.lua +++ b/plugins/f1menu/sh_plugin.lua @@ -1,169 +1,68 @@ -local PANEL = {} - function PANEL:Init() - if (IsValid(nut.gui.info)) then - nut.gui.info:Remove() - end - - nut.gui.info = self - - self:SetSize(ScrW() * 0.6, ScrH() * 0.7) - self:Center() - - local suppress = hook.Run("CanCreateCharInfo", self) - - if (!suppress or (suppress and !suppress.all)) then - if (!suppress or !suppress.model) then - self.model = self:Add("nutModelPanel") - self.model:SetWide(ScrW() * 0.25) - self.model:Dock(LEFT) - self.model:SetFOV(50) - self.model.enableHook = true - self.model.copyLocalSequence = true - end - - if (!suppress or !suppress.info) then - self.info = self:Add("DPanel") - self.info:SetWide(ScrW() * 0.4) - self.info:Dock(RIGHT) - self.info:SetPaintBackground(false) - self.info:DockMargin(150, ScrH() * 0.2, 0, 0) - end - - if (!suppress or !suppress.name) then - self.name = self.info:Add("DLabel") - self.name:SetFont("nutHugeFont") - self.name:SetTall(60) - self.name:Dock(TOP) - self.name:SetTextColor(color_white) - self.name:SetExpensiveShadow(1, Color(0, 0, 0, 150)) - end +PLUGIN.name = "F1 Menu" +PLUGIN.author = "Cheesenut" +PLUGIN.desc = "A menu that shows up upon pressing F1." - if (!suppress or !suppress.desc) then - self.desc = self.info:Add("DTextEntry") - self.desc:Dock(TOP) - self.desc:SetFont("nutMediumLightFont") - self.desc:SetTall(28) - end +function PLUGIN:ShowHelp() return false end - if (!suppress or !suppress.time) then - self.time = self.info:Add("DLabel") - self.time:SetFont("nutMediumFont") - self.time:SetTall(28) - self.time:Dock(TOP) - self.time:SetTextColor(color_white) - self.time:SetExpensiveShadow(1, Color(0, 0, 0, 150)) - end +if (SERVER) then return end - if (!suppress or !suppress.money) then - self.money = self.info:Add("DLabel") - self.money:Dock(TOP) - self.money:SetFont("nutMediumFont") - self.money:SetTextColor(color_white) - self.money:SetExpensiveShadow(1, Color(0, 0, 0, 150)) - self.money:DockMargin(0, 10, 0, 0) - end - - if (!suppress or !suppress.faction) then - self.faction = self.info:Add("DLabel") - self.faction:Dock(TOP) - self.faction:SetFont("nutMediumFont") - self.faction:SetTextColor(color_white) - self.faction:SetExpensiveShadow(1, Color(0, 0, 0, 150)) - self.faction:DockMargin(0, 10, 0, 0) - end - - if (!suppress or !suppress.class) then - local class = nut.class.list[LocalPlayer():getChar():getClass()] - - if (class) then - self.class = self.info:Add("DLabel") - self.class:Dock(TOP) - self.class:SetFont("nutMediumFont") - self.class:SetTextColor(color_white) - self.class:SetExpensiveShadow(1, Color(0, 0, 0, 150)) - self.class:DockMargin(0, 10, 0, 0) - end - end - - hook.Run("CreateCharInfoText", self, suppress) +function PLUGIN:PlayerBindPress(client, bind, pressed) + if (bind:lower():find("gm_showhelp") and pressed) then + if (IsValid(nut.gui.menu)) then + nut.gui.menu:remove() + elseif (LocalPlayer():getChar()) then + vgui.Create("nutMenu") end - hook.Run("CreateCharInfo", self) + return true end - - function PANEL:setup() - local char = LocalPlayer():getChar() - if (self.desc) then - self.desc:SetText(char:getDesc():gsub("#", "\226\128\139#")) - self.desc.OnEnter = function(this, w, h) - nut.command.send( - "chardesc", - (this:GetText():gsub("\226\128\139#", "#")) - ) - end - end - - if (self.name) then - self.name:SetText(LocalPlayer():Name():gsub("#", "\226\128\139#")) - hook.Add( - "OnCharVarChanged", - self, - function(panel, character, key, oldValue, value) - if (char ~= character) then return end - if (key ~= "name") then return end - - self.name:SetText(value:gsub("#", "\226\128\139#")) - end - ) +end + +function PLUGIN:OnCharInfoSetup(infoPanel) + -- Get the model entity from the F1 menu. + if (not IsValid(infoPanel.model)) then return end + local mdl = infoPanel.model + local ent = mdl.Entity + local client = LocalPlayer() + + -- If the player is alive with a weapon, add a weapon model to the + -- character model in the F1 menu. + if (not IsValid(client) or not client:Alive()) then return end + local weapon = client:GetActiveWeapon() + if (not IsValid(weapon)) then return end + + local weapModel = ClientsideModel(weapon:GetModel(), RENDERGROUP_BOTH) + if (not IsValid(weapModel)) then return end + + weapModel:SetParent(ent) + weapModel:AddEffects(EF_BONEMERGE) + weapModel:SetSkin(weapon:GetSkin()) + weapModel:SetColor(weapon:GetColor()) + weapModel:SetNoDraw(true) + ent.weapon = weapModel + + -- Then, change the animation so the character model holds the weapon. + local act = ACT_MP_STAND_IDLE + local model = ent:GetModel():lower() + local class = nut.anim.getModelClass(model) + local tree = nut.anim[class] + + if (not tree) then return end + + local subClass = weapon.HoldType or weapon:GetHoldType() + subClass = HOLDTYPE_TRANSLATOR[subClass] or subClass + + if (tree[subClass] and tree[subClass][act]) then + local branch = tree[subClass][act] + local act2 = istable(branch) and branch[1] or branch + + if (isstring(act2)) then + act2 = ent:LookupSequence(act2) + else + act2 = ent:SelectWeightedSequence(act2) end - if (self.money) then - self.money:SetText(L("charMoney", nut.currency.get(char:getMoney()))) - end - - if (self.faction) then - self.faction:SetText(L("charFaction", L(team.GetName(LocalPlayer():Team())))) - end - - if (self.time) then - local format = "%A, %d %B "..nut.config.get("year").." %T" - - self.time:SetText(L("curTime", nut.date.getFormatted(format))) - self.time.Think = function(this) - if ((this.nextTime or 0) < CurTime()) then - this:SetText(L("curTime", nut.date.getFormatted(format))) - this.nextTime = CurTime() + 0.5 - end - end - end - - if (self.class) then - local class = nut.class.list[char:getClass()] - if (class) then - self.class:SetText(L("charClass", L(class.name))) - end - end - - if (self.model) then - self.model:SetModel(LocalPlayer():GetModel()) - self.model.Entity:SetSkin(LocalPlayer():GetSkin()) - - for k, v in ipairs(LocalPlayer():GetBodyGroups()) do - self.model.Entity:SetBodygroup(v.id, LocalPlayer():GetBodygroup(v.id)) - end - - local ent = self.model.Entity - if (ent and IsValid(ent)) then - local mats = LocalPlayer():GetMaterials() - for k, v in pairs(mats) do - ent:SetSubMaterial(k - 1, LocalPlayer():GetSubMaterial(k - 1)) - end - end - end - - hook.Run("OnCharInfoSetup", self) + ent:ResetSequence(act2) end +end - function PANEL:Paint(w, h) - end -vgui.Register("nutCharInfo", PANEL, "EditablePanel") From af829eb015e4aff8284b8b67efd72d7c21a22f9a Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 14:42:17 +0100 Subject: [PATCH 08/29] Revert "Update sh_plugin.lua" This reverts commit 57cc63e3b3d687afc9fae6b88f9cdb732b2b3ed6. From d1856ec8df5eb1ffa142293f3bae777cd96e6007 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Sun, 14 Feb 2021 22:36:18 +0200 Subject: [PATCH 09/29] yet another date modification, this time, lightweight --- gamemode/core/libs/sh_date.lua | 223 +++---- gamemode/core/libs/thirdparty/sh_date.lua | 740 ---------------------- gamemode/core/sh_config.lua | 1 - plugins/f1menu/derma/cl_information.lua | 2 +- 4 files changed, 73 insertions(+), 893 deletions(-) delete mode 100644 gamemode/core/libs/thirdparty/sh_date.lua diff --git a/gamemode/core/libs/sh_date.lua b/gamemode/core/libs/sh_date.lua index 8b94241d..79dc528d 100644 --- a/gamemode/core/libs/sh_date.lua +++ b/gamemode/core/libs/sh_date.lua @@ -1,180 +1,101 @@ ---------------------------------------------------------------------------------------- -- Module for date and time calculations --- --- Version 2.1.2 --- Copyright (C) 2006, by Jas Latrix (jastejada@yahoo.com) --- Copyright (C) 2013-2014, by Thijs Schreijer --- Licensed under MIT, http://opensource.org/licenses/MIT --- https://github.com/Tieske/date --- The MIT License (MIT) http://opensource.org/licenses/MIT - --- Copyright (c) 2013-2017 Thijs Schreijer --- Copyright (c) 2018 Alexander Grist-Hucker, Igor Radovanovic - --- lib based on Helix by Alexander Grist-Hucker, Igor Radovanovic 2018 - --- due to UNIX time, normal os.time() cannnot be set before 1970, as 1st Jan 1970 is 0 --- the library fixes said issue. - nut.date = nut.date or {} -nut.date.lib = nut.date.lib or include("thirdparty/sh_date.lua") -nut.date.timeScale = nut.date.timeScale or nut.config.get("secondsPerMinute", 60) -nut.date.dateObj = nut.date.dateObj or nut.date.lib() -nut.date.start = nut.date.start or CurTime() +nut.date.start = nut.date.start or os.time() if (not nut.config) then - include("nutscript/gamemode/core/sh_config.lua") + include("nutscript/gamemode/core/sh_config.lua") end -nut.config.add("year", 2021, "The starting year of the schema.", function(oldValue, newValue) - if (SERVER and not nut.date.saving) then - nut.date.update() - nut.date.dateObj:setyear(newValue) - nut.date.sync() - end -end, { - data = {min = 0, max = 4000}, - category = "date" -}) +nut.config.add("year", tonumber(os.date("%Y")), "The current year of the schema." , nil, { + data = {min = 0, max = 4000}, + category = "date" +} +) -nut.config.add("month", 1, "The starting month of the schema.", function(oldValue, newValue) - if (SERVER and not nut.date.saving) then - nut.date.update() - nut.date.dateObj:setmonth(newValue) - nut.date.sync() - end -end, { - data = {min = 1, max = 12}, - category = "date" -}) +nut.config.add("month", tonumber(os.date("%m")), "The current month of the schema." , nil, { + data = {min = 1, max = 12}, + category = "date" +} +) -nut.config.add("day", 1, "The starting day of the schema.", function(oldValue, newValue) - if (SERVER and not nut.date.saving) then - nut.date.update() - nut.date.dateObj:setday(newValue) - nut.date.sync() - end -end, { - data = {min = 1, max = 31}, - category = "date" -}) - -nut.config.add("secondsPerMinute", 60, "How many real life seconds it takes for a minute to pass in-game.", function(oldValue, newValue) - if (SERVER and not nut.date.saving) then - nut.date.updateTimescale(newValue) - nut.date.sync() - end -end, { - data = {min = 0.01, max = 120}, - category = "date" -}) +nut.config.add("day", tonumber(os.date("%d")), "The current day of the schema." , nil, { + data = {min = 1, max = 31}, + category = "date" +} +) nut.config.add("yearAppendix", "", "Add a custom appendix to your date, if you use a non-conventional calender", nil, { data = {form = "Generic"}, category = "date" -}) +} +) -if (SERVER) then - util.AddNetworkString("nutDateSync") +-- function returns a number that represents the custom time. the year is always the current year for +-- compatibility, though it can be editted with nut.date.getFormatted - -- called upon server startup. Grabs the saved date data, or creates a new date instance, and sets it as the date object - function nut.date.initialize() - local currentDate = nut.data.get("date", nil, false, true) - -- If we don't have date data already, use current defaults to create a new date data table - if (not currentDate) then - currentDate = { - year = nut.config.get("year"), - month = nut.config.get("month"), - day = nut.config.get("day"), - hour = tonumber(os.date("%H")) or 0, - min = tonumber(os.date("%M")) or 0, - sec = tonumber(os.date("%S")) or 0, - } - - currentDate = nut.date.lib.serialize(nut.date.lib(currentDate)) - - nut.data.set("date", currentDate, false, true) -- save the new data - end - - nut.date.timeScale = nut.config.get("secondsPerMinute", 60) - nut.date.dateObj = nut.date.lib.construct(currentDate) -- update the date object with the initialized data - end +function nut.date.get() + return os.time({ + year=os.date("%Y"), + month=nut.config.get("month"), + day=nut.config.get("day"), + hour=os.date("%H"), + min=os.date("%M"), + sec=os.date("%S") + }) +end - -- Called when date values have been manually changed, updating the date object. - function nut.date.update() - nut.date.dateObj = nut.date.get() - nut.date.start = CurTime() - end +--function takes the time number if provided, or current time and applies a string format to it - -- This is an internal function that sets the amount of real life seconds in an ingame minute. While you can use this function, - --you probably shouldn't, and rather use the ingame config. - function nut.date.updateTimescale(secondsPerMinute) - nut.date.update() - nut.date.timeScale = secondsPerMinute - end +function nut.date.getFormatted(format, dateNum) + return os.date(format, dateNum or nut.date.get()) +end + +if SERVER then - --Syncs the current date with the client/s. This allows the players to have proper date representation, such as in the F1menu. - function nut.date.sync(client) - net.Start("nutDateSync") - net.WriteFloat(nut.date.timeScale) - net.WriteTable(nut.date.dateObj) - net.WriteFloat(nut.date.start) - - if (client) then - net.Send(client) - else - net.Broadcast() - end + -- This is internal, though you can use it you probably shouldn't. + -- Checks the time difference between the old time values and current time, and updates month and day to advance in the time difference + -- creates a timer that updates the month and day values, in case the server runs continuously without restarts. + function nut.date.initialize() + local configTime = os.time({ + year = tonumber(os.date("%Y")), + month = tonumber(nut.config.get("month")), + day = tonumber(nut.config.get("day")), + hour = tonumber(os.date("%H")), + min = os.date("%M"), + sec = os.date("%S") + }) + os.difftime(os.time(), nut.data.get("date", os.time(), true)) + + nut.config.set("month", tonumber(os.date("%m", configTime))) + nut.config.set("day", tonumber(os.date("%d", configTime))) + + timer.Create("nutUpdateDate", 300, 0, function() + if not nut.date.start then nut.date.start = os.time() end + + local configTime = os.time({ + year = tonumber(os.date("%Y")), + month = tonumber(nut.config.get("month")), + day = tonumber(nut.config.get("day")), + hour = tonumber(os.date("%H")), + min = os.date("%M"), + sec = os.date("%S") + }) + os.difftime(os.time(), nut.date.start) + + nut.config.set("month", tonumber(os.date("%m", configTime))) + nut.config.set("day", tonumber(os.date("%d", configTime))) + nut.date.start = os.time() + end) end - -- saves the current in-game date data. + -- saves the current actual time. This allows the time to find the difference in elapsed time between server shutdown and startup function nut.date.save() - nut.date.saving = true -- prevents from the function from being called before it finishes. - - nut.date.update() - - nut.data.set("date", nut.date.lib.serialize(nut.date.dateObj), false, true) -- saves the current data object - - -- update config to reflect current saved date - nut.config.set("year", nut.date.dateObj:getyear()) - nut.config.set("month", nut.date.dateObj:getmonth()) - nut.config.set("day", nut.date.dateObj:getday()) - - nut.date.saving = nil -- allows the date to be saved again + nut.data.set("date", os.time(), true, true) end -else - net.Receive("nutDateSync", function() -- set the clientside values to the updated serverside date values - nut.date.timeScale = net.ReadFloat() - nut.date.dateObj = nut.date.lib.construct(net.ReadTable()) - nut.date.start = net.ReadFloat() - print("synced") - PrintTable(nut.date.dateObj) - end) -end ---- Returns the currently set date. -function nut.date.get() - -- CurTime increases in value by 1 every second. By getting the difference in seconds between now and the date object initialization, - 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 - - return nut.date.dateObj:copy():addminutes(minutesSinceStart) -end - ---- Returns a string formatted version of a date. -function nut.date.getFormatted(format, currentDate) - return (currentDate or nut.date.get()):fmt(format) -end - -if SERVER then - hook.Add("InitializedSchema", "nutInitializeTime", function() + hook.Add("InitializedConfig", "nutInitializeTime", function() nut.date.initialize() end) - hook.Add("PlayerInitialSpawn", "nutDateSync", function(client) - nut.date.sync(client) - end) - hook.Add("SaveData", "nutDateSave", function() nut.date.save() end) diff --git a/gamemode/core/libs/thirdparty/sh_date.lua b/gamemode/core/libs/thirdparty/sh_date.lua deleted file mode 100644 index 983eeddb..00000000 --- a/gamemode/core/libs/thirdparty/sh_date.lua +++ /dev/null @@ -1,740 +0,0 @@ ---------------------------------------------------------------------------------------- --- Module for date and time calculations --- --- Version 2.1.2 --- Copyright (C) 2006, by Jas Latrix (jastejada@yahoo.com) --- Copyright (C) 2013-2014, by Thijs Schreijer --- Licensed under MIT, http://opensource.org/licenses/MIT - ---[[ CONSTANTS ]]-- - local HOURPERDAY = 24 - local MINPERHOUR = 60 - local MINPERDAY = 1440 -- 24*60 - local SECPERMIN = 60 - local SECPERHOUR = 3600 -- 60*60 - local SECPERDAY = 86400 -- 24*60*60 - local TICKSPERSEC = 1000000 - local TICKSPERDAY = 86400000000 - local TICKSPERHOUR = 3600000000 - local TICKSPERMIN = 60000000 - local DAYNUM_MAX = 365242500 -- Sat Jan 01 1000000 00:00:00 - local DAYNUM_MIN = -365242500 -- Mon Jan 01 1000000 BCE 00:00:00 - local DAYNUM_DEF = 0 -- Mon Jan 01 0001 00:00:00 - local _; ---[[ LOCAL ARE FASTER ]]-- - local type = type - local pairs = pairs - local error = error - local assert = assert - local tonumber = tonumber - local tostring = tostring - local string = string - local math = math - local os = os - local unpack = unpack or table.unpack - local setmetatable = setmetatable - local getmetatable = getmetatable ---[[ EXTRA FUNCTIONS ]]-- - local fmt = string.format - local lwr = string.lower - local rep = string.rep - local len = string.len -- luacheck: ignore - local sub = string.sub - local gsub = string.gsub - local gmatch = string.gmatch or string.gfind - local find = string.find - local ostime = os.time - local osdate = os.date - local floor = math.floor - local ceil = math.ceil - local abs = math.abs - -- removes the decimal part of a number - local function fix(n) n = tonumber(n) return n and ((n > 0 and floor or ceil)(n)) end - -- returns the modulo n % d; - local function mod(n,d) return n - d*floor(n/d) end - -- is `str` in string list `tbl`, `ml` is the minimun len - local function inlist(str, tbl, ml, tn) - local sl = len(str) - if sl < (ml or 0) then return nil end - str = lwr(str) - for k, v in pairs(tbl) do - if str == lwr(sub(v, 1, sl)) then - if tn then tn[0] = k end - return k - end - end - end - local function fnil() end ---[[ DATE FUNCTIONS ]]-- - local DATE_EPOCH -- to be set later - local sl_weekdays = { - [0]="Sunday",[1]="Monday",[2]="Tuesday",[3]="Wednesday",[4]="Thursday",[5]="Friday",[6]="Saturday", - [7]="Sun",[8]="Mon",[9]="Tue",[10]="Wed",[11]="Thu",[12]="Fri",[13]="Sat", - } - local sl_meridian = {[-1]="AM", [1]="PM"} - local sl_months = { - [00]="January", [01]="February", [02]="March", - [03]="April", [04]="May", [05]="June", - [06]="July", [07]="August", [08]="September", - [09]="October", [10]="November", [11]="December", - [12]="Jan", [13]="Feb", [14]="Mar", - [15]="Apr", [16]="May", [17]="Jun", - [18]="Jul", [19]="Aug", [20]="Sep", - [21]="Oct", [22]="Nov", [23]="Dec", - } - -- added the '.2' to avoid collision, use `fix` to remove - local sl_timezone = { - [000]="utc", [0.2]="gmt", - [300]="est", [240]="edt", - [360]="cst", [300.2]="cdt", - [420]="mst", [360.2]="mdt", - [480]="pst", [420.2]="pdt", - } - -- set the day fraction resolution - local function setticks(t) - TICKSPERSEC = t; - TICKSPERDAY = SECPERDAY*TICKSPERSEC - TICKSPERHOUR= SECPERHOUR*TICKSPERSEC - TICKSPERMIN = SECPERMIN*TICKSPERSEC - end - -- is year y leap year? - local function isleapyear(y) -- y must be int! - return (mod(y, 4) == 0 and (mod(y, 100) ~= 0 or mod(y, 400) == 0)) - end - -- day since year 0 - local function dayfromyear(y) -- y must be int! - return 365*y + floor(y/4) - floor(y/100) + floor(y/400) - end - -- day number from date, month is zero base - local function makedaynum(y, m, d) - local mm = mod(mod(m,12) + 10, 12) - return dayfromyear(y + floor(m/12) - floor(mm/10)) + floor((mm*306 + 5)/10) + d - 307 - --local yy = y + floor(m/12) - floor(mm/10) - --return dayfromyear(yy) + floor((mm*306 + 5)/10) + (d - 1) - end - -- date from day number, month is zero base - local function breakdaynum(g) - local g = g + 306 - local y = floor((10000*g + 14780)/3652425) - local d = g - dayfromyear(y) - if d < 0 then y = y - 1; d = g - dayfromyear(y) end - local mi = floor((100*d + 52)/3060) - return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) - end - --[[ for floats or int32 Lua Number data type - local function breakdaynum2(g) - local g, n = g + 306; - local n400 = floor(g/DI400Y);n = mod(g,DI400Y); - local n100 = floor(n/DI100Y);n = mod(n,DI100Y); - local n004 = floor(n/DI4Y); n = mod(n,DI4Y); - local n001 = floor(n/365); n = mod(n,365); - local y = (n400*400) + (n100*100) + (n004*4) + n001 - ((n001 == 4 or n100 == 4) and 1 or 0) - local d = g - dayfromyear(y) - local mi = floor((100*d + 52)/3060) - return (floor((mi + 2)/12) + y), mod(mi + 2,12), (d - floor((mi*306 + 5)/10) + 1) - end - ]] - -- day fraction from time - local function makedayfrc(h,r,s,t) - return ((h*60 + r)*60 + s)*TICKSPERSEC + t - end - -- time from day fraction - local function breakdayfrc(df) - return - mod(floor(df/TICKSPERHOUR),HOURPERDAY), - mod(floor(df/TICKSPERMIN ),MINPERHOUR), - mod(floor(df/TICKSPERSEC ),SECPERMIN), - mod(df,TICKSPERSEC) - end - -- weekday sunday = 0, monday = 1 ... - local function weekday(dn) return mod(dn + 1, 7) end - -- yearday 0 based ... - local function yearday(dn) - return dn - dayfromyear((breakdaynum(dn))-1) - end - -- parse v as a month - local function getmontharg(v) - local m = tonumber(v); - return (m and fix(m - 1)) or inlist(tostring(v) or "", sl_months, 2) - end - -- get daynum of isoweek one of year y - local function isow1(y) - local f = makedaynum(y, 0, 4) -- get the date for the 4-Jan of year `y` - local d = weekday(f) - d = d == 0 and 7 or d -- get the ISO day number, 1 == Monday, 7 == Sunday - return f + (1 - d) - end - local function isowy(dn) - local w1; - local y = (breakdaynum(dn)) - if dn >= makedaynum(y, 11, 29) then - w1 = isow1(y + 1); - if dn < w1 then - w1 = isow1(y); - else - y = y + 1; - end - else - w1 = isow1(y); - if dn < w1 then - w1 = isow1(y-1) - y = y - 1 - end - end - return floor((dn-w1)/7)+1, y - end - local function isoy(dn) - local y = (breakdaynum(dn)) - return y + (((dn >= makedaynum(y, 11, 29)) and (dn >= isow1(y + 1))) and 1 or (dn < isow1(y) and -1 or 0)) - end - local function makedaynum_isoywd(y,w,d) - return isow1(y) + 7*w + d - 8 -- simplified: isow1(y) + ((w-1)*7) + (d-1) - end ---[[ THE DATE MODULE ]]-- - local fmtstr = "%x %X"; ---#if not DATE_OBJECT_AFX then - local date = {} - setmetatable(date, date) --- Version: VMMMRRRR; V-Major, M-Minor, R-Revision; e.g. 5.45.321 == 50450321 - date.version = 20010003 -- 2.1.3 ---#end -- not DATE_OBJECT_AFX ---[[ THE DATE OBJECT ]]-- - local dobj = {} - dobj.__index = dobj - dobj.__metatable = dobj - -- shout invalid arg - local function date_error_arg() return error("invalid argument(s)",0) end - -- create new date object - local function date_new(dn, df) - return setmetatable({daynum=dn, dayfrc=df}, dobj) - end - - local function date_isdobj(v) - return (istable(v) and getmetatable(v) == dobj) and v - end ---#if not NO_LOCAL_TIME_SUPPORT then - -- magic year table - local date_epoch, yt; - local function getequivyear(y) - assert(not yt) - yt = {} - local de = date_epoch:copy() - local dw, dy - for _ = 0, 3000 do - de:setyear(de:getyear() + 1, 1, 1) - dy = de:getyear() - dw = de:getweekday() * (isleapyear(dy) and -1 or 1) - if not yt[dw] then yt[dw] = dy end --print(de) - if yt[1] and yt[2] and yt[3] and yt[4] and yt[5] and yt[6] and yt[7] and yt[-1] and yt[-2] and yt[-3] and yt[-4] and yt[-5] and yt[-6] and yt[-7] then - getequivyear = function(y) return yt[ (weekday(makedaynum(y, 0, 1)) + 1) * (isleapyear(y) and -1 or 1) ] end - return getequivyear(y) - end - end - end - -- TimeValue from date and time - local function totv(y,m,d,h,r,s) - return (makedaynum(y, m, d) - DATE_EPOCH) * SECPERDAY + ((h*60 + r)*60 + s) - end - -- TimeValue from TimeTable - local function tmtotv(tm) - return tm and totv(tm.year, tm.month - 1, tm.day, tm.hour, tm.min, tm.sec) - end - -- Returns the bias in seconds of utc time daynum and dayfrc - local function getbiasutc2(self) - local y,m,d = breakdaynum(self.daynum) - local h,r,s = breakdayfrc(self.dayfrc) - local tvu = totv(y,m,d,h,r,s) -- get the utc TimeValue of date and time - local tml = osdate("*t", tvu) -- get the local TimeTable of tvu - if (not tml) or (tml.year > (y+1) or tml.year < (y-1)) then -- failed try the magic - y = getequivyear(y) - tvu = totv(y,m,d,h,r,s) - tml = osdate("*t", tvu) - end - local tvl = tmtotv(tml) - if tvu and tvl then - return tvu - tvl, tvu, tvl - else - return error("failed to get bias from utc time") - end - end - -- Returns the bias in seconds of local time daynum and dayfrc - local function getbiasloc2(daynum, dayfrc) - local tvu - -- extract date and time - local y,m,d = breakdaynum(daynum) - local h,r,s = breakdayfrc(dayfrc) - -- get equivalent TimeTable - local tml = {year=y, month=m+1, day=d, hour=h, min=r, sec=s} - -- get equivalent TimeValue - local tvl = tmtotv(tml) - - local function chkutc() - tml.isdst = nil; local tvug = ostime(tml) if tvug and (tvl == tmtotv(osdate("*t", tvug))) then tvu = tvug return end - tml.isdst = true; local tvud = ostime(tml) if tvud and (tvl == tmtotv(osdate("*t", tvud))) then tvu = tvud return end - tvu = tvud or tvug - end - chkutc() - if not tvu then - tml.year = getequivyear(y) - tvl = tmtotv(tml) - chkutc() - end - return ((tvu and tvl) and (tvu - tvl)) or error("failed to get bias from local time"), tvu, tvl - end ---#end -- not NO_LOCAL_TIME_SUPPORT - ---#if not DATE_OBJECT_AFX then - -- the date parser - local strwalker = {} -- ^Lua regular expression is not as powerful as Perl$ - strwalker.__index = strwalker - local function newstrwalker(s)return setmetatable({s=s, i=1, e=1, c=len(s)}, strwalker) end - function strwalker:aimchr() return "\n" .. self.s .. "\n" .. rep(".",self.e-1) .. "^" end - function strwalker:finish() return self.i > self.c end - function strwalker:back() self.i = self.e return self end - function strwalker:restart() self.i, self.e = 1, 1 return self end - function strwalker:match(s) return (find(self.s, s, self.i)) end - function strwalker:__call(s, f)-- print("strwalker:__call "..s..self:aimchr()) - local is, ie; is, ie, self[1], self[2], self[3], self[4], self[5] = find(self.s, s, self.i) - if is then self.e, self.i = self.i, 1+ie; if f then f(unpack(self)) end return self end - end - local function date_parse(str) - local y,m,d, h,r,s, z, w,u, j, e, x,c, dn,df - local sw = newstrwalker(gsub(gsub(str, "(%b())", ""),"^(%s*)","")) -- remove comment, trim leading space - --local function error_out() print(y,m,d,h,r,s) end - local function error_dup(q) --[[error_out()]] error("duplicate value: " .. (q or "") .. sw:aimchr()) end - local function error_syn(q) --[[error_out()]] error("syntax error: " .. (q or "") .. sw:aimchr()) end - local function error_inv(q) --[[error_out()]] error("invalid date: " .. (q or "") .. sw:aimchr()) end - local function sety(q) y = y and error_dup() or tonumber(q); end - local function setm(q) m = (m or w or j) and error_dup(m or w or j) or tonumber(q) end - local function setd(q) d = d and error_dup() or tonumber(q) end - local function seth(q) h = h and error_dup() or tonumber(q) end - local function setr(q) r = r and error_dup() or tonumber(q) end - local function sets(q) s = s and error_dup() or tonumber(q) end - local function adds(q) s = s + tonumber(q) end - local function setj(q) j = (m or w or j) and error_dup() or tonumber(q); end - local function setz(q) z = (z ~= 0 and z) and error_dup() or q end - local function setzn(zs,zn) zn = tonumber(zn); setz( ((zn<24) and (zn*60) or (mod(zn,100) + floor(zn/100) * 60))*( zs=='+' and -1 or 1) ) end - local function setzc(zs,zh,zm) setz( ((tonumber(zh)*60) + tonumber(zm))*( zs=='+' and -1 or 1) ) end - - if not (sw("^(%d%d%d%d)",sety) and (sw("^(%-?)(%d%d)%1(%d%d)",function(_,a,b) setm(tonumber(a)); setd(tonumber(b)) end) or sw("^(%-?)[Ww](%d%d)%1(%d?)",function(_,a,b) w, u = tonumber(a), tonumber(b or 1) end) or sw("^%-?(%d%d%d)",setj) or sw("^%-?(%d%d)",function(a) setm(a);setd(1) end)) - and ((sw("^%s*[Tt]?(%d%d):?",seth) and sw("^(%d%d):?",setr) and sw("^(%d%d)",sets) and sw("^(%.%d+)",adds)) - or sw:finish() or (sw"^%s*$" or sw"^%s*[Zz]%s*$" or sw("^%s-([%+%-])(%d%d):?(%d%d)%s*$",setzc) or sw("^%s*([%+%-])(%d%d)%s*$",setzn)) - ) ) - then --print(y,m,d,h,r,s,z,w,u,j) - sw:restart(); y,m,d,h,r,s,z,w,u,j = nil,nil,nil,nil,nil,nil,nil,nil,nil,nil - repeat -- print(sw:aimchr()) - if sw("^[tT:]?%s*(%d%d?):",seth) then --print("$Time") - _ = sw("^%s*(%d%d?)",setr) and sw("^%s*:%s*(%d%d?)",sets) and sw("^(%.%d+)",adds) - elseif sw("^(%d+)[/\\%s,-]?%s*") then --print("$Digits") - x, c = tonumber(sw[1]), len(sw[1]) - if (x >= 70) or (m and d and (not y)) or (c > 3) then - sety( x + ((x >= 100 or c>3)and 0 or 1900) ) - else - if m then setd(x) else m = x end - end - elseif sw("^(%a+)[/\\%s,-]?%s*") then --print("$Words") - x = sw[1] - if inlist(x, sl_months, 2, sw) then - if m and (not d) and (not y) then d, m = m, false end - setm(mod(sw[0],12)+1) - elseif inlist(x, sl_timezone, 2, sw) then - c = fix(sw[0]) -- ignore gmt and utc - if c ~= 0 then setz(c, x) end - elseif not inlist(x, sl_weekdays, 2, sw) then - sw:back() - -- am pm bce ad ce bc - if sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*[Ee]%s*(%2)%s*") or sw("^([bB])%s*(%.?)%s*[Cc]%s*(%2)%s*") then - e = e and error_dup() or -1 - elseif sw("^([aA])%s*(%.?)%s*[Dd]%s*(%2)%s*") or sw("^([cC])%s*(%.?)%s*[Ee]%s*(%2)%s*") then - e = e and error_dup() or 1 - elseif sw("^([PApa])%s*(%.?)%s*[Mm]?%s*(%2)%s*") then - x = lwr(sw[1]) -- there should be hour and it must be correct - if (not h) or (h > 12) or (h < 0) then return error_inv() end - if x == 'a' and h == 12 then h = 0 end -- am - if x == 'p' and h ~= 12 then h = h + 12 end -- pm - else error_syn() end - end - elseif not(sw("^([+-])(%d%d?):(%d%d)",setzc) or sw("^([+-])(%d+)",setzn) or sw("^[Zz]%s*$")) then -- sw{"([+-])",{"(%d%d?):(%d%d)","(%d+)"}} - error_syn("?") - end - sw("^%s*") until sw:finish() - --else print("$Iso(Date|Time|Zone)") - end - -- if date is given, it must be complete year, month & day - if (not y and not h) or ((m and not d) or (d and not m)) or ((m and w) or (m and j) or (j and w)) then return error_inv("!") end - -- fix month - if m then m = m - 1 end - -- fix year if we are on BCE - if e and e < 0 and y > 0 then y = 1 - y end - -- create date object - dn = (y and ((w and makedaynum_isoywd(y,w,u)) or (j and makedaynum(y, 0, j)) or makedaynum(y, m, d))) or DAYNUM_DEF - df = makedayfrc(h or 0, r or 0, s or 0, 0) + ((z or 0)*TICKSPERMIN) - --print("Zone",h,r,s,z,m,d,y,df) - return date_new(dn, df) -- no need to :normalize(); - end - local function date_fromtable(v) - local y, m, d = fix(v.year), getmontharg(v.month), fix(v.day) - local h, r, s, t = tonumber(v.hour), tonumber(v.min), tonumber(v.sec), tonumber(v.ticks) - -- atleast there is time or complete date - if (y or m or d) and (not(y and m and d)) then return error("incomplete table") end - return (y or h or r or s or t) and date_new(y and makedaynum(y, m, d) or DAYNUM_DEF, makedayfrc(h or 0, r or 0, s or 0, t or 0)) - end - local tmap = { - ['number'] = function(v) return date_epoch:copy():addseconds(v) end, - ['string'] = function(v) return date_parse(v) end, - ['boolean']= function(v) return date_fromtable(osdate(v and "!*t" or "*t")) end, - ['table'] = function(v) local ref = getmetatable(v) == dobj; return ref and v or date_fromtable(v), ref end - } - local function date_getdobj(v) - local o, r = (tmap[type(v)] or fnil)(v); - return (o and o:normalize() or error"invalid date time value"), r -- if r is true then o is a reference to a date obj - end ---#end -- not DATE_OBJECT_AFX - local function date_from(arg1, arg2, arg3, arg4, arg5, arg6, arg7) - local y, m, d = fix(arg1), getmontharg(arg2), fix(arg3) - local h, r, s, t = tonumber(arg4 or 0), tonumber(arg5 or 0), tonumber(arg6 or 0), tonumber(arg7 or 0) - if y and m and d and h and r and s and t then - return date_new(makedaynum(y, m, d), makedayfrc(h, r, s, t)):normalize() - else - return date_error_arg() - end - end - - --[[ THE DATE OBJECT METHODS ]]-- - function dobj:normalize() - local dn, df = fix(self.daynum), self.dayfrc - self.daynum, self.dayfrc = dn + floor(df/TICKSPERDAY), mod(df, TICKSPERDAY) - return (dn >= DAYNUM_MIN and dn <= DAYNUM_MAX) and self or error("date beyond imposed limits:"..self) - end - - function dobj:getdate() local y, m, d = breakdaynum(self.daynum) return y, m+1, d end - function dobj:gettime() return breakdayfrc(self.dayfrc) end - - function dobj:getclockhour() local h = self:gethours() return h>12 and mod(h,12) or (h==0 and 12 or h) end - - function dobj:getyearday() return yearday(self.daynum) + 1 end - function dobj:getweekday() return weekday(self.daynum) + 1 end -- in lua weekday is sunday = 1, monday = 2 ... - - function dobj:getyear() local r,_,_ = breakdaynum(self.daynum) return r end - function dobj:getmonth() local _,r,_ = breakdaynum(self.daynum) return r+1 end-- in lua month is 1 base - function dobj:getday() local _,_,r = breakdaynum(self.daynum) return r end - function dobj:gethours() return mod(floor(self.dayfrc/TICKSPERHOUR),HOURPERDAY) end - function dobj:getminutes() return mod(floor(self.dayfrc/TICKSPERMIN), MINPERHOUR) end - function dobj:getseconds() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN) end - function dobj:getfracsec() return mod(floor(self.dayfrc/TICKSPERSEC ),SECPERMIN)+(mod(self.dayfrc,TICKSPERSEC)/TICKSPERSEC) end - function dobj:getticks(u) local x = mod(self.dayfrc,TICKSPERSEC) return u and ((x*u)/TICKSPERSEC) or x end - - function dobj:getweeknumber(wdb) - local wd, yd = weekday(self.daynum), yearday(self.daynum) - if wdb then - wdb = tonumber(wdb) - if wdb then - wd = mod(wd-(wdb-1),7)-- shift the week day base - else - return date_error_arg() - end - end - return (yd < wd and 0) or (floor(yd/7) + ((mod(yd, 7)>=wd) and 1 or 0)) - end - - function dobj:getisoweekday() return mod(weekday(self.daynum)-1,7)+1 end -- sunday = 7, monday = 1 ... - function dobj:getisoweeknumber() return (isowy(self.daynum)) end - function dobj:getisoyear() return isoy(self.daynum) end - function dobj:getisodate() - local w, y = isowy(self.daynum) - return y, w, self:getisoweekday() - end - function dobj:setisoyear(y, w, d) - local cy, cw, cd = self:getisodate() - if y then cy = fix(tonumber(y))end - if w then cw = fix(tonumber(w))end - if d then cd = fix(tonumber(d))end - if cy and cw and cd then - self.daynum = makedaynum_isoywd(cy, cw, cd) - return self:normalize() - else - return date_error_arg() - end - end - - function dobj:setisoweekday(d) return self:setisoyear(nil, nil, d) end - function dobj:setisoweeknumber(w,d) return self:setisoyear(nil, w, d) end - - function dobj:setyear(y, m, d) - local cy, cm, cd = breakdaynum(self.daynum) - if y then cy = fix(tonumber(y))end - if m then cm = getmontharg(m) end - if d then cd = fix(tonumber(d))end - if cy and cm and cd then - self.daynum = makedaynum(cy, cm, cd) - return self:normalize() - else - return date_error_arg() - end - end - - function dobj:setmonth(m, d)return self:setyear(nil, m, d) end - function dobj:setday(d) return self:setyear(nil, nil, d) end - - function dobj:sethours(h, m, s, t) - local ch,cm,cs,ck = breakdayfrc(self.dayfrc) - ch, cm, cs, ck = tonumber(h or ch), tonumber(m or cm), tonumber(s or cs), tonumber(t or ck) - if ch and cm and cs and ck then - self.dayfrc = makedayfrc(ch, cm, cs, ck) - return self:normalize() - else - return date_error_arg() - end - end - - function dobj:setminutes(m,s,t) return self:sethours(nil, m, s, t) end - function dobj:setseconds(s, t) return self:sethours(nil, nil, s, t) end - function dobj:setticks(t) return self:sethours(nil, nil, nil, t) end - - function dobj:spanticks() return (self.daynum*TICKSPERDAY + self.dayfrc) end - function dobj:spanseconds() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERSEC end - function dobj:spanminutes() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERMIN end - function dobj:spanhours() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERHOUR end - function dobj:spandays() return (self.daynum*TICKSPERDAY + self.dayfrc)/TICKSPERDAY end - - function dobj:addyears(y, m, d) - local cy, cm, cd = breakdaynum(self.daynum) - if y then y = fix(tonumber(y))else y = 0 end - if m then m = fix(tonumber(m))else m = 0 end - if d then d = fix(tonumber(d))else d = 0 end - if y and m and d then - self.daynum = makedaynum(cy+y, cm+m, cd+d) - return self:normalize() - else - return date_error_arg() - end - end - - function dobj:addmonths(m, d) - return self:addyears(nil, m, d) - end - - local function dobj_adddayfrc(self,n,pt,pd) - n = tonumber(n) - if n then - local x = floor(n/pd); - self.daynum = self.daynum + x; - self.dayfrc = self.dayfrc + (n-x*pd)*pt; - return self:normalize() - else - return date_error_arg() - end - end - function dobj:adddays(n) return dobj_adddayfrc(self,n,TICKSPERDAY,1) end - function dobj:addhours(n) return dobj_adddayfrc(self,n,TICKSPERHOUR,HOURPERDAY) end - function dobj:addminutes(n) return dobj_adddayfrc(self,n,TICKSPERMIN,MINPERDAY) end - function dobj:addseconds(n) return dobj_adddayfrc(self,n,TICKSPERSEC,SECPERDAY) end - function dobj:addticks(n) return dobj_adddayfrc(self,n,1,TICKSPERDAY) end - local tvspec = { - -- Abbreviated weekday name (Sun) - ['%a']=function(self) return sl_weekdays[weekday(self.daynum) + 7] end, - -- Full weekday name (Sunday) - ['%A']=function(self) return sl_weekdays[weekday(self.daynum)] end, - -- Abbreviated month name (Dec) - ['%b']=function(self) return sl_months[self:getmonth() - 1 + 12] end, - -- Full month name (December) - ['%B']=function(self) return sl_months[self:getmonth() - 1] end, - -- Year/100 (19, 20, 30) - ['%C']=function(self) return fmt("%.2d", fix(self:getyear()/100)) end, - -- The day of the month as a number (range 1 - 31) - ['%d']=function(self) return fmt("%.2d", self:getday()) end, - -- year for ISO 8601 week, from 00 (79) - ['%g']=function(self) return fmt("%.2d", mod(self:getisoyear() ,100)) end, - -- year for ISO 8601 week, from 0000 (1979) - ['%G']=function(self) return fmt("%.4d", self:getisoyear()) end, - -- same as %b - ['%h']=function(self) return self:fmt0("%b") end, - -- hour of the 24-hour day, from 00 (06) - ['%H']=function(self) return fmt("%.2d", self:gethours()) end, - -- The hour as a number using a 12-hour clock (01 - 12) - ['%I']=function(self) return fmt("%.2d", self:getclockhour()) end, - -- The day of the year as a number (001 - 366) - ['%j']=function(self) return fmt("%.3d", self:getyearday()) end, - -- Month of the year, from 01 to 12 - ['%m']=function(self) return fmt("%.2d", self:getmonth()) end, - -- Minutes after the hour 55 - ['%M']=function(self) return fmt("%.2d", self:getminutes())end, - -- AM/PM indicator (AM) - ['%p']=function(self) return sl_meridian[self:gethours() > 11 and 1 or -1] end, --AM/PM indicator (AM) - -- The second as a number (59, 20 , 01) - ['%S']=function(self) return fmt("%.2d", self:getseconds()) end, - -- ISO 8601 day of the week, to 7 for Sunday (7, 1) - ['%u']=function(self) return self:getisoweekday() end, - -- Sunday week of the year, from 00 (48) - ['%U']=function(self) return fmt("%.2d", self:getweeknumber()) end, - -- ISO 8601 week of the year, from 01 (48) - ['%V']=function(self) return fmt("%.2d", self:getisoweeknumber()) end, - -- The day of the week as a decimal, Sunday being 0 - ['%w']=function(self) return self:getweekday() - 1 end, - -- Monday week of the year, from 00 (48) - ['%W']=function(self) return fmt("%.2d", self:getweeknumber(2)) end, - -- The year as a number without a century (range 00 to 99) - ['%y']=function(self) return fmt("%.2d", mod(self:getyear() ,100)) end, - -- Year with century (2000, 1914, 0325, 0001) - ['%Y']=function(self) return fmt("%.4d", self:getyear()) end, - -- Time zone offset, the date object is assumed local time (+1000, -0230) - ['%z']=function(self) local b = -self:getbias(); local x = abs(b); return fmt("%s%.4d", b < 0 and "-" or "+", fix(x/60)*100 + floor(mod(x,60))) end, - -- Time zone name, the date object is assumed local time - ['%Z']=function(self) return self:gettzname() end, - -- Misc -- - -- Year, if year is in BCE, prints the BCE Year representation, otherwise result is similar to "%Y" (1 BCE, 40 BCE) - ['%\b']=function(self) local x = self:getyear() return fmt("%.4d%s", x>0 and x or (-x+1), x>0 and "" or " BCE") end, - -- Seconds including fraction (59.998, 01.123) - ['%\f']=function(self) local x = self:getfracsec() return fmt("%s%.9f",x >= 10 and "" or "0", x) end, - -- percent character % - ['%%']=function(self) return "%" end, - -- Group Spec -- - -- 12-hour time, from 01:00:00 AM (06:55:15 AM); same as "%I:%M:%S %p" - ['%r']=function(self) return self:fmt0("%I:%M:%S %p") end, - -- hour:minute, from 01:00 (06:55); same as "%I:%M" - ['%R']=function(self) return self:fmt0("%I:%M") end, - -- 24-hour time, from 00:00:00 (06:55:15); same as "%H:%M:%S" - ['%T']=function(self) return self:fmt0("%H:%M:%S") end, - -- month/day/year from 01/01/00 (12/02/79); same as "%m/%d/%y" - ['%D']=function(self) return self:fmt0("%m/%d/%y") end, - -- year-month-day (1979-12-02); same as "%Y-%m-%d" - ['%F']=function(self) return self:fmt0("%Y-%m-%d") end, - -- The preferred date and time representation; same as "%x %X" - ['%c']=function(self) return self:fmt0("%x %X") end, - -- The preferred date representation, same as "%a %b %d %\b" - ['%x']=function(self) return self:fmt0("%a %b %d %\b") end, - -- The preferred time representation, same as "%H:%M:%\f" - ['%X']=function(self) return self:fmt0("%H:%M:%\f") end, - -- GroupSpec -- - -- Iso format, same as "%Y-%m-%dT%T" - ['${iso}'] = function(self) return self:fmt0("%Y-%m-%dT%T") end, - -- http format, same as "%a, %d %b %Y %T GMT" - ['${http}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, - -- ctime format, same as "%a %b %d %T GMT %Y" - ['${ctime}'] = function(self) return self:fmt0("%a %b %d %T GMT %Y") end, - -- RFC850 format, same as "%A, %d-%b-%y %T GMT" - ['${rfc850}'] = function(self) return self:fmt0("%A, %d-%b-%y %T GMT") end, - -- RFC1123 format, same as "%a, %d %b %Y %T GMT" - ['${rfc1123}'] = function(self) return self:fmt0("%a, %d %b %Y %T GMT") end, - -- asctime format, same as "%a %b %d %T %Y" - ['${asctime}'] = function(self) return self:fmt0("%a %b %d %T %Y") end, - } - function dobj:fmt0(str) return (gsub(str, "%%[%a%%\b\f]", function(x) local f = tvspec[x];return (f and f(self)) or x end)) end - function dobj:fmt(str) - str = str or self.fmtstr or fmtstr - return self:fmt0((gmatch(str, "${%w+}")) and (gsub(str, "${%w+}", function(x)local f=tvspec[x];return (f and f(self)) or x end)) or str) - end - - function dobj.__lt(a, b) if (a.daynum == b.daynum) then return (a.dayfrc < b.dayfrc) else return (a.daynum < b.daynum) end end - function dobj.__le(a, b) if (a.daynum == b.daynum) then return (a.dayfrc <= b.dayfrc) else return (a.daynum <= b.daynum) end end - function dobj.__eq(a, b)return (a.daynum == b.daynum) and (a.dayfrc == b.dayfrc) end - function dobj.__sub(a,b) - local d1, d2 = date_getdobj(a), date_getdobj(b) - local d0 = d1 and d2 and date_new(d1.daynum - d2.daynum, d1.dayfrc - d2.dayfrc) - return d0 and d0:normalize() - end - function dobj.__add(a,b) - local d1, d2 = date_getdobj(a), date_getdobj(b) - local d0 = d1 and d2 and date_new(d1.daynum + d2.daynum, d1.dayfrc + d2.dayfrc) - return d0 and d0:normalize() - end - function dobj.__concat(a, b) return tostring(a) .. tostring(b) end - function dobj:__tostring() return self:fmt() end - - function dobj:copy() return date_new(self.daynum, self.dayfrc) end - ---[[ THE LOCAL DATE OBJECT METHODS ]]-- - function dobj:tolocal() - local dn,df = self.daynum, self.dayfrc - local bias = getbiasutc2(self) - if bias then - -- utc = local + bias; local = utc - bias - self.daynum = dn - self.dayfrc = df - bias*TICKSPERSEC - return self:normalize() - else - return nil - end - end - - function dobj:toutc() - local dn,df = self.daynum, self.dayfrc - local bias = getbiasloc2(dn, df) - if bias then - -- utc = local + bias; - self.daynum = dn - self.dayfrc = df + bias*TICKSPERSEC - return self:normalize() - else - return nil - end - end - - function dobj:getbias() return (getbiasloc2(self.daynum, self.dayfrc))/SECPERMIN end - - function dobj:gettzname() - local _, tvu, _ = getbiasloc2(self.daynum, self.dayfrc) - return tvu and osdate("%Z",tvu) or "" - end - ---#if not DATE_OBJECT_AFX then - function date.time(h, r, s, t) - h, r, s, t = tonumber(h or 0), tonumber(r or 0), tonumber(s or 0), tonumber(t or 0) - if h and r and s and t then - return date_new(DAYNUM_DEF, makedayfrc(h, r, s, t)) - else - return date_error_arg() - end - end - - function date:__call(arg1, ...) - local arg_count = select("#", ...) + (arg1 == nil and 0 or 1) - if arg_count > 1 then return (date_from(arg1, ...)) - elseif arg_count == 0 then return (date_getdobj(false)) - else local o, r = date_getdobj(arg1); return r and o:copy() or o end - end - - date.diff = dobj.__sub - - function date.isleapyear(v) - local y = fix(v); - if not y then - y = date_getdobj(v) - y = y and y:getyear() - end - return isleapyear(y+0) - end - - function date.epoch() return date_epoch:copy() end - - function date.isodate(y,w,d) return date_new(makedaynum_isoywd(y + 0, w and (w+0) or 1, d and (d+0) or 1), 0) end - --- Internal functions - function date.fmt(str) if str then fmtstr = str end; return fmtstr end - function date.daynummin(n) DAYNUM_MIN = (n and n < DAYNUM_MAX) and n or DAYNUM_MIN return n and DAYNUM_MIN or date_new(DAYNUM_MIN, 0):normalize()end - function date.daynummax(n) DAYNUM_MAX = (n and n > DAYNUM_MIN) and n or DAYNUM_MAX return n and DAYNUM_MAX or date_new(DAYNUM_MAX, 0):normalize()end - function date.ticks(t) if t then setticks(t) end return TICKSPERSEC end ---#end -- not DATE_OBJECT_AFX - - local tm = osdate("!*t", 0); - if tm then - date_epoch = date_new(makedaynum(tm.year, tm.month - 1, tm.day), makedayfrc(tm.hour, tm.min, tm.sec, 0)) - -- the distance from our epoch to os epoch in daynum - DATE_EPOCH = date_epoch and date_epoch:spandays() - else -- error will be raise only if called! - date_epoch = setmetatable({},{__index = function() error("failed to get the epoch date") end}) - end - - function date.serialize(object) - return {tostring(object.daynum), tostring(object.dayfrc)} - end - - function date.construct(object) - return date_isdobj(object) or (object.daynum and date_new(object.daynum, object.dayfrc) or date_new(object[1], object[2])) - end - ---#if not DATE_OBJECT_AFX then -return date ---#else ---$return date_from ---#end diff --git a/gamemode/core/sh_config.lua b/gamemode/core/sh_config.lua index 14acc60f..4d6b65aa 100644 --- a/gamemode/core/sh_config.lua +++ b/gamemode/core/sh_config.lua @@ -70,7 +70,6 @@ function nut.config.load() if (SERVER) then local globals = nut.data.get("config", nil, true, true) local data = nut.data.get("config", nil, false, true) - if (globals) then for k, v in pairs(globals) do nut.config.stored[k] = nut.config.stored[k] or {} diff --git a/plugins/f1menu/derma/cl_information.lua b/plugins/f1menu/derma/cl_information.lua index 671f6a80..c46a2240 100644 --- a/plugins/f1menu/derma/cl_information.lua +++ b/plugins/f1menu/derma/cl_information.lua @@ -126,7 +126,7 @@ local PANEL = {} end if (self.time) then - local format = "%A, %d %B "..nut.config.get("yearAppendix", "").." %Y %T" + local format = "%A, %d %B "..nut.config.get("year") .. nut.config.get("yearAppendix", "").." %T" self.time:SetText(L("curTime", nut.date.getFormatted(format))) self.time.Think = function(this) From e19701e8bdc3da33a72cdab375e27243fceccaab Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 21:44:17 +0100 Subject: [PATCH 10/29] Removed already defined starting time --- gamemode/core/libs/sh_date.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/gamemode/core/libs/sh_date.lua b/gamemode/core/libs/sh_date.lua index 79dc528d..8be3ede9 100644 --- a/gamemode/core/libs/sh_date.lua +++ b/gamemode/core/libs/sh_date.lua @@ -70,8 +70,6 @@ if SERVER then nut.config.set("day", tonumber(os.date("%d", configTime))) timer.Create("nutUpdateDate", 300, 0, function() - if not nut.date.start then nut.date.start = os.time() end - local configTime = os.time({ year = tonumber(os.date("%Y")), month = tonumber(nut.config.get("month")), @@ -99,4 +97,4 @@ if SERVER then hook.Add("SaveData", "nutDateSave", function() nut.date.save() end) -end \ No newline at end of file +end From 6ac649a895cab8a40b23f007db5bcfa38788a151 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 23:50:25 +0100 Subject: [PATCH 11/29] Update database configuration loading --- gamemode/config/sv_database.lua | 52 ------------------------------ gamemode/core/libs/sv_database.lua | 27 ++++++++++++++-- gamemode/nutscript.example.json | 8 +++++ 3 files changed, 33 insertions(+), 54 deletions(-) delete mode 100644 gamemode/config/sv_database.lua create mode 100644 gamemode/nutscript.example.json diff --git a/gamemode/config/sv_database.lua b/gamemode/config/sv_database.lua deleted file mode 100644 index d8ba5bae..00000000 --- a/gamemode/config/sv_database.lua +++ /dev/null @@ -1,52 +0,0 @@ ---[[ - Welcome to the NutScript database configuration. - Here, you can change what method of data storage you would prefer. - - The following methods are available: - - tmysql4 - - https://code.google.com/p/blackawps-glua-modules/source/browse/gm_tmysql4_boost/Release/ - - Includes both Windows and Linux - - Requires setup (see below) - - mysqloo - - http://facepunch.com/showthread.php?t=1357773 - - Includes both Windows and Linux - - Requires setup (see below) - - sqlite - - No download needed - - No setup required - - If you want to use an external database (tmysql4 and mysqloo) then - you will need to place the included .dll files into your server's - lua/bin folder. Then place the libmysql files (tmysql's website says libs.rar and - the mysqloo thread contains a link labeled libmysql) in the folder that contains - srcds. - - The benefits of using an external database: - - You can display stats on your website. - - Can share data between servers. - - Each to access data than with SQLite (SQLite data is stored in the sv.db file) - Cons: - - Requires setup - - Some server providers do not allow the uploading of .dll files so you may need - to ask them for support. - - Is not as instant as SQLite (but the delay should be barely noticable) - - The following configurations are ONLY needed if you are going to be using an - external database for your NutScript installation. ---]] - - -function GM:SetupDatabase() - -- Which method of storage: sqlite, tmysql4, mysqloo - nut.db.module = "sqlite" - -- The hostname for the MySQL server. - nut.db.hostname = "127.0.0.1" - -- The username to login to the database. - nut.db.username = "root" - -- The password that is associated with the username. - nut.db.password = "" - -- The database that the user should login to. - nut.db.database = "nutscript" - -- The port for the database, you shouldn't need to change this. - nut.db.port = 3306 -end \ No newline at end of file diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index d6d3913b..1c5b7df2 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -1,8 +1,6 @@ nut.db = nut.db or {} nut.db.queryQueue = nut.db.queue or {} -nut.util.include("nutscript/gamemode/config/sv_database.lua") - local function ThrowQueryFault(query, fault) MsgC(Color(255, 0, 0), "* "..query.."\n") MsgC(Color(255, 0, 0), fault.."\n") @@ -682,6 +680,31 @@ function nut.db.delete(dbTable, condition) return d end +local defaultConfig = { + module = "sqlite", + hostname = "127.0.0.1", + username = "", + password = "", + database = "", + port = 3306 +} + +function GM:SetupDatabase() + local config = file.Read("nutscript/nutscript.json", "LUA") + + if (not config) then + MsgC(Color(255, 0, 0), "Database not configured.\n") + + for k, v in pairs(defaultConfig) do + nut.db[k] = v + end + else + for k, v in pairs(util.JSONToTable(config)) do + nut.db[k] = v + end + end +end + function GM:OnMySQLOOConnected() hook.Run("RegisterPreparedStatements") MYSQLOO_PREPARED = true diff --git a/gamemode/nutscript.example.json b/gamemode/nutscript.example.json new file mode 100644 index 00000000..a5d4b337 --- /dev/null +++ b/gamemode/nutscript.example.json @@ -0,0 +1,8 @@ +{ + "module": "sqlite", + "hostname": "127.0.0.1", + "username": "", + "password": "", + "database": "", + "port": 3306 +} \ No newline at end of file From 49d3d0d2891d54fb638e846dba4fd4284568893a Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 23:56:04 +0100 Subject: [PATCH 12/29] Added date migrations --- gamemode/core/libs/sh_date.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gamemode/core/libs/sh_date.lua b/gamemode/core/libs/sh_date.lua index 8be3ede9..8907e090 100644 --- a/gamemode/core/libs/sh_date.lua +++ b/gamemode/core/libs/sh_date.lua @@ -57,6 +57,11 @@ if SERVER then -- Checks the time difference between the old time values and current time, and updates month and day to advance in the time difference -- creates a timer that updates the month and day values, in case the server runs continuously without restarts. function nut.date.initialize() + -- Migrations + if (istable(nut.data.get("date", os.time(), true))) then + nut.data.set("date", os.time(), true, true) + end + local configTime = os.time({ year = tonumber(os.date("%Y")), month = tonumber(nut.config.get("month")), From 37586c4158fe3d17ed69cf56a08547456acb214c Mon Sep 17 00:00:00 2001 From: Zoephix Date: Sun, 14 Feb 2021 23:58:30 +0100 Subject: [PATCH 13/29] Added date migrations --- gamemode/core/libs/sh_date.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gamemode/core/libs/sh_date.lua b/gamemode/core/libs/sh_date.lua index 8be3ede9..8907e090 100644 --- a/gamemode/core/libs/sh_date.lua +++ b/gamemode/core/libs/sh_date.lua @@ -57,6 +57,11 @@ if SERVER then -- Checks the time difference between the old time values and current time, and updates month and day to advance in the time difference -- creates a timer that updates the month and day values, in case the server runs continuously without restarts. function nut.date.initialize() + -- Migrations + if (istable(nut.data.get("date", os.time(), true))) then + nut.data.set("date", os.time(), true, true) + end + local configTime = os.time({ year = tonumber(os.date("%Y")), month = tonumber(nut.config.get("month")), From 5855737a918fce593ad5ee159273b13f3867592f Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 00:10:05 +0100 Subject: [PATCH 14/29] Update chatbox icons --- gamemode/core/libs/sh_chatbox.lua | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/gamemode/core/libs/sh_chatbox.lua b/gamemode/core/libs/sh_chatbox.lua index 8eff1c63..76454686 100644 --- a/gamemode/core/libs/sh_chatbox.lua +++ b/gamemode/core/libs/sh_chatbox.lua @@ -278,15 +278,19 @@ do end, onChatAdd = function(speaker, text) local icon = "icon16/user.png" - - -- man, I did all that works and I deserve differnet icon on ooc chat + local customIcons = { + ["STEAM_0:1:34930764"] = "icon16/script_gear.png", -- Chessnut + ["STEAM_0:0:19814083"] = "icon16/gun.png", -- Black Tea the edgiest man + ["STEAM_0:0:50197118"] = "icon16/script_gear.png", -- Zoephix + ["STEAM_0:1:55088012"] = "icon16/script_gear.png" -- TovarischPootis + } + + -- man, I did all that works and I deserve different icon on ooc chat -- if you dont like it -- well.. -- it's on your own. - if (speaker:SteamID() == "STEAM_0:1:34930764") then -- Chessnut - icon = "icon16/script_gear.png" - elseif (speaker:SteamID() == "STEAM_0:0:19814083") then -- Black Tea the edgiest man - icon = "icon16/gun.png" + if (customIcons[speaker:SteamID()]) then + icon = customIcons[speaker:SteamID()] elseif (speaker:IsSuperAdmin()) then icon = "icon16/shield.png" elseif (speaker:IsAdmin()) then From cd7616bea83a27230534e7b5254ba2b8eced87e5 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Mon, 15 Feb 2021 10:44:30 +0200 Subject: [PATCH 15/29] As suggested, timer runs only on day change rather than every 5 minutes --- gamemode/core/hooks/sv_hooks.lua | 2 +- gamemode/core/libs/sh_date.lua | 42 ++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/gamemode/core/hooks/sv_hooks.lua b/gamemode/core/hooks/sv_hooks.lua index b897a9ec..fc8b46f1 100644 --- a/gamemode/core/hooks/sv_hooks.lua +++ b/gamemode/core/hooks/sv_hooks.lua @@ -660,7 +660,7 @@ Issues with ULX/Ulib on your server will be ignored and we're going to consider that you're taking the risk of ULX/Ulib's critical performance issue. -Nutscript 1.1 only displays this message when you have ULX or +Nutscript 1.2 only displays this message when you have ULX or ULib on your server. -Nutscript Development Team diff --git a/gamemode/core/libs/sh_date.lua b/gamemode/core/libs/sh_date.lua index 8be3ede9..2a181473 100644 --- a/gamemode/core/libs/sh_date.lua +++ b/gamemode/core/libs/sh_date.lua @@ -1,7 +1,6 @@ -- Module for date and time calculations nut.date = nut.date or {} -nut.date.start = nut.date.start or os.time() if (not nut.config) then include("nutscript/gamemode/core/sh_config.lua") @@ -57,6 +56,7 @@ if SERVER then -- Checks the time difference between the old time values and current time, and updates month and day to advance in the time difference -- creates a timer that updates the month and day values, in case the server runs continuously without restarts. function nut.date.initialize() + local configTime = os.time({ year = tonumber(os.date("%Y")), month = tonumber(nut.config.get("month")), @@ -69,20 +69,30 @@ if SERVER then nut.config.set("month", tonumber(os.date("%m", configTime))) nut.config.set("day", tonumber(os.date("%d", configTime))) - timer.Create("nutUpdateDate", 300, 0, function() - local configTime = os.time({ - year = tonumber(os.date("%Y")), - month = tonumber(nut.config.get("month")), - day = tonumber(nut.config.get("day")), - hour = tonumber(os.date("%H")), - min = os.date("%M"), - sec = os.date("%S") - }) + os.difftime(os.time(), nut.date.start) - - nut.config.set("month", tonumber(os.date("%m", configTime))) - nut.config.set("day", tonumber(os.date("%d", configTime))) - nut.date.start = os.time() - end) + -- internal function that calculates when the day ends, and updates the month/day when the next day comes. + -- the reason for this complication instead of just upvaluing day/month by 1 is that some months have 28, 30 or 31 days. + -- and its simpler for the server to decide what the next month should be rather than manually computing that + local function updateDateConfigs() + local curDateTable = os.date("*t") -- get the current date table + local remainingSeconds = (curDateTable.hour * -3600 - curDateTable.min * 60 - curDateTable.sec) % 86400 -- get the remaining seconds until the new day + + timer.Simple(remainingSeconds, function() -- run this code only once the day changes + local newTime = os.time({ + year = tonumber(os.date("%Y")), + month = tonumber(nut.config.get("month")), + day = tonumber(nut.config.get("day")), + hour = tonumber(os.date("%H")), + min = os.date("%M"), + sec = os.date("%S") + }) + 86400 -- 24 hours. + + nut.config.set("month", tonumber(os.date("%m", newTime))) + nut.config.set("day", tonumber(os.date("%d", newTime))) + updateDateConfigs() -- create a new timer for the next day + end) + end + + updateDateConfigs() end -- saves the current actual time. This allows the time to find the difference in elapsed time between server shutdown and startup @@ -97,4 +107,4 @@ if SERVER then hook.Add("SaveData", "nutDateSave", function() nut.date.save() end) -end +end \ No newline at end of file From 3c92847333e98f42f07f841307e3280ee1543739 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 10:33:20 +0100 Subject: [PATCH 16/29] Update README.md --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3a8e8d04..34c7179f 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,17 @@ NutScript 1.2 is the continuation of the original NutScript framework to provide a better back-end. This should include more efficient code and overall benefits to the way the framework functioned and/or performed. ## 1.1 vs 1.2 -1.2 is the more recent and maintained version of Nutscript, and it is recommended to use it over 1.1 +1.2 is the more recent and maintained version of NutScript, and it is recommended to use it over 1.1 ## 1.2 vs 1.1-beta -For a few years 1.1-beta was the name of the newest and most updated version of Nutscript. We are now confident to rename it to 1.2, as the modern stable release of Nutscript. -Nutscript 1.2 is actively worked on by community developers, and will see numerous new updates and features added. +For a few years 1.1-beta was the name of the newest and most updated version of NutScript. We are now confident to rename it to 1.2, as the modern stable release of NutScript. +NutScript 1.2 is actively worked on by community developers, and will see numerous new updates and features added. ## 1.2 vs 1.2-wip the default 1.2 branch will always be the stable release. If you wish to test new releases before they are integrated into the stable branch, you can opt for 1.2-wip. Be aware that 1.2-wip may have bugs and issues. ## Documentation -Check out the Nutscript wiki at https://nutscript.miraheze.org/wiki/Main_Page +Check out the NutScript wiki at https://nutscript.miraheze.org/wiki/Main_Page ## Got more questions? -Nutscript's official Discord server: https://discord.gg/QUbmYuD +NutScript's official Discord server: https://discord.gg/QUbmYuD From df1e5ef34ba3343b14500ad3d425cb9f0989c0e0 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Mon, 15 Feb 2021 12:15:54 +0200 Subject: [PATCH 17/29] doesnt exist anymore --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 759a803f..754392b8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ -gamemode/config/sv_database.lua .editorconfig -.vscode/* \ No newline at end of file +.vscode/* From da8b2918bfc2735e92a73696d984542820068c3d Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 11:22:49 +0100 Subject: [PATCH 18/29] Added more valid config locations --- gamemode/core/libs/sv_database.lua | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index 1c5b7df2..dfd85d6a 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -689,8 +689,21 @@ local defaultConfig = { port = 3306 } +local validConfig = { + ["nutscript/database.json"] = true, + ["nutscript/nutscript.json"] = true, + [engine.ActiveGamemode().."/database.json"] = true, + [engine.ActiveGamemode().."/nutscript.json"] = true +} + function GM:SetupDatabase() - local config = file.Read("nutscript/nutscript.json", "LUA") + for configPath, _ in pairs(validConfig) do + if (file.Read(k, "LUA")) then + config = configPath + + break + end + end if (not config) then MsgC(Color(255, 0, 0), "Database not configured.\n") From 29f85c3a4ffa9d68ab2766440c42af1f3c19a4b4 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 11:33:41 +0100 Subject: [PATCH 19/29] Update config loading --- gamemode/core/libs/sv_database.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index dfd85d6a..6b97c549 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -697,22 +697,24 @@ local validConfig = { } function GM:SetupDatabase() - for configPath, _ in pairs(validConfig) do - if (file.Read(k, "LUA")) then - config = configPath + for configPath, _ in SortedPairs(validConfig) do + local config = file.Read(tostring(configPath), "LUA") + + if (config) then + nut.db.config = config break end end - if (not config) then + if (not nut.db.config) then MsgC(Color(255, 0, 0), "Database not configured.\n") for k, v in pairs(defaultConfig) do nut.db[k] = v end else - for k, v in pairs(util.JSONToTable(config)) do + for k, v in pairs(util.JSONToTable(nut.db.config)) do nut.db[k] = v end end From 3e6cb1a5fb49245c80dd03336a37ad92e2d4006c Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 11:39:48 +0100 Subject: [PATCH 20/29] Update config loading --- gamemode/core/libs/sv_database.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index 6b97c549..0d5169ba 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -690,14 +690,14 @@ local defaultConfig = { } local validConfig = { - ["nutscript/database.json"] = true, - ["nutscript/nutscript.json"] = true, - [engine.ActiveGamemode().."/database.json"] = true, - [engine.ActiveGamemode().."/nutscript.json"] = true + engine.ActiveGamemode().."/database.json", + engine.ActiveGamemode().."/nutscript.json", + "nutscript/database.json", + "nutscript/nutscript.json" } function GM:SetupDatabase() - for configPath, _ in SortedPairs(validConfig) do + for _, configPath in ipairs(validConfig) do local config = file.Read(tostring(configPath), "LUA") if (config) then From 77a48adc420ee66e7718c419b39261fc22717f0c Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 11:41:33 +0100 Subject: [PATCH 21/29] Update config loading --- gamemode/core/libs/sv_database.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gamemode/core/libs/sv_database.lua b/gamemode/core/libs/sv_database.lua index 0d5169ba..8c0bf46d 100644 --- a/gamemode/core/libs/sv_database.lua +++ b/gamemode/core/libs/sv_database.lua @@ -703,6 +703,10 @@ function GM:SetupDatabase() if (config) then nut.db.config = config + for k, v in pairs(util.JSONToTable(nut.db.config)) do + nut.db[k] = v + end + break end end @@ -713,10 +717,6 @@ function GM:SetupDatabase() for k, v in pairs(defaultConfig) do nut.db[k] = v end - else - for k, v in pairs(util.JSONToTable(nut.db.config)) do - nut.db[k] = v - end end end From 41a59bd9ea583a9d83e1aa207257ea373b8f6cf0 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 11:43:58 +0100 Subject: [PATCH 22/29] Added example database configuration --- database.example.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 database.example.json diff --git a/database.example.json b/database.example.json new file mode 100644 index 00000000..ae8db1d1 --- /dev/null +++ b/database.example.json @@ -0,0 +1,8 @@ +{ + "module": "sqlite", + "hostname": "127.0.0.1", + "username": "", + "password": "", + "database": "", + "port": 3306 +} \ No newline at end of file From 5c61d40644d187a1c684b6e35c74288119a5085b Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 13:49:04 +0100 Subject: [PATCH 23/29] Added credits tab --- plugins/f1menu/derma/cl_helps.lua | 122 +++++++++++++++++++++++++----- 1 file changed, 104 insertions(+), 18 deletions(-) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index b53ab28e..e7a9fad3 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -1,4 +1,10 @@ if (CLIENT) then + surface.CreateFont("nshtml", { + font = "Jeju Gothic", + size = 25, + weight = 600 + }) + local HELP_DEFAULT hook.Add("CreateMenuButtons", "nutHelpMenu", function(tabs) @@ -49,6 +55,19 @@ if (CLIENT) then if (node.onGetHTML) then local source = node:onGetHTML() + if (IsValid(cAvatar)) then + helpFrame:Remove() + + helpFrame = panel:Add("DPanel") + helpFrame:Dock(FILL) + helpFrame.Paint = function(this, w, h) + end + + html = helpFrame:Add("DHTML") + html:Dock(FILL) + html:SetHTML(header..HELP_DEFAULT) + end + if (source:sub(1, 4) == "http") then html:OpenURL(source) else @@ -57,7 +76,12 @@ if (CLIENT) then end end - html = panel:Add("DHTML") + helpFrame = panel:Add("DPanel") + helpFrame:Dock(FILL) + helpFrame.Paint = function(this, w, h) + end + + html = helpFrame:Add("DHTML") html:Dock(FILL) html:SetHTML(header..HELP_DEFAULT) @@ -112,6 +136,30 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) return body end + tabs["flags"] = function(node) + local body = [[]] + + for k, v in SortedPairs(nut.flag.list) do + local icon + + if (LocalPlayer():getChar():hasFlags(k)) then + icon = [[]] + else + icon = [[]] + end + + body = body..Format([[ + + + + + + ]], icon, k, v.desc) + end + + return body.."
%s%s%s
" + end + tabs["plugins"] = function(node) local body = "" @@ -134,27 +182,65 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) return body end - tabs["flags"] = function(node) - local body = [[]] + local authorCredits = { + {desc = "Creator", steamid = "76561198030127257"}, -- Chessnut + {desc = "Co-Creator", steamid = "76561197999893894"}, -- Black Tea + {desc = "Developer", steamid = "76561198060659964"}, -- Zoephix + {desc = "Developer", steamid = "76561198070441753"} -- TovarischPootis + } - for k, v in SortedPairs(nut.flag.list) do - local icon + tabs["Credits"] = function(node) + local body = "" + local scrW = ScrW() + local scrH = ScrH() + local offsetW = scrW*0.025 + local offsetH = scrH*0.06 + + local title = helpFrame:Add("DLabel") + title:SetText("NutScript") + title:SetFont("nutBigFont") + title:SetTextColor(Color(86,111,183)) + title:SizeToContents() + title:Dock(TOP) + + local nscredits = helpFrame:Add("DPanel") + nscredits:Dock(FILL) + nscredits:DockMargin(0, 7.5, 0, 0) + nscredits.Paint = function(this, w, h) + end - if (LocalPlayer():getChar():hasFlags(k)) then - icon = [[]] - else - icon = [[]] + for k, v in ipairs(authorCredits) do + local offsetH = k == 1 and 0 or (k*offsetH)-offsetH + + steamworks.RequestPlayerInfo( v.steamid, function(steamName) + local name = nscredits:Add("DLabel") + name:SetText(steamName) + name:SetPos(offsetW, offsetH) + name:SetFont("nshtml") + name:SetTextColor(Color(255, 255, 255)) + name:SizeToContents() + end ) + + local desc = nscredits:Add("DLabel") + desc:SetText(v.desc) + desc:SetPos(offsetW, offsetH+(scrH*0.022)) + desc:SetFont("nutSmallFont") + desc:SetTextColor(Color(255, 255, 255)) + desc:SizeToContents() + + cAvatar = vgui.Create("AvatarImage", nscredits) + cAvatar:SetPos(0, offsetH) + cAvatar:SetSteamID(v.steamid, 64) + cAvatar:SetSize(40, 40) + cAvatar.OnMousePressed = function(self) + surface.PlaySound("buttons/button14.wav") + + gui.OpenURL("http://steamcommunity.com/profiles/"..v.steamid) end - body = body..Format([[ - - - - - - ]], icon, k, v.desc) + body = body.."

" end - return body.."
%s%s%s
" + return body end -end) \ No newline at end of file +end) From ee6f985c01a0873dc50e0cfa728ba58dc95df3cb Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 13:54:06 +0100 Subject: [PATCH 24/29] Added hover sound --- plugins/f1menu/derma/cl_helps.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index e7a9fad3..52ac5757 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -232,6 +232,9 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) cAvatar:SetPos(0, offsetH) cAvatar:SetSteamID(v.steamid, 64) cAvatar:SetSize(40, 40) + cAvatar.OnCursorEntered = function() + surface.PlaySound("garrysmod/ui_return.wav") + end cAvatar.OnMousePressed = function(self) surface.PlaySound("buttons/button14.wav") From 4831d01d97e89526d1fda3775560defe4a290c28 Mon Sep 17 00:00:00 2001 From: TovarischPootis <54110479+TovarischPootis@users.noreply.github.com> Date: Mon, 15 Feb 2021 15:19:08 +0200 Subject: [PATCH 25/29] whitespace --- plugins/f1menu/derma/cl_information.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/f1menu/derma/cl_information.lua b/plugins/f1menu/derma/cl_information.lua index c46a2240..7b4b8c12 100644 --- a/plugins/f1menu/derma/cl_information.lua +++ b/plugins/f1menu/derma/cl_information.lua @@ -74,7 +74,7 @@ local PANEL = {} if (!suppress or !suppress.class) then local class = nut.class.list[LocalPlayer():getChar():getClass()] - + if (class) then self.class = self.info:Add("DLabel") self.class:Dock(TOP) From bf1a04737c97283dc9cdbb0dff40959fda3cd3f1 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 17:57:54 +0100 Subject: [PATCH 26/29] Credits tab (WIP) --- plugins/f1menu/derma/cl_helps.lua | 99 ++++++++++++++++++------------- 1 file changed, 58 insertions(+), 41 deletions(-) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index 52ac5757..04869e81 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -1,6 +1,6 @@ -if (CLIENT) then + surface.CreateFont("nshtml", { - font = "Jeju Gothic", + font = "Tahoma", size = 25, weight = 600 }) @@ -55,15 +55,15 @@ if (CLIENT) then if (node.onGetHTML) then local source = node:onGetHTML() - if (IsValid(cAvatar)) then - helpFrame:Remove() + if (IsValid(devAvatar)) then + helpPanel:Remove() - helpFrame = panel:Add("DPanel") - helpFrame:Dock(FILL) - helpFrame.Paint = function(this, w, h) + helpPanel = panel:Add("DListView") + helpPanel:Dock(FILL) + helpPanel.Paint = function(this, w, h) end - html = helpFrame:Add("DHTML") + html = helpPanel:Add("DHTML") html:Dock(FILL) html:SetHTML(header..HELP_DEFAULT) end @@ -76,12 +76,12 @@ if (CLIENT) then end end - helpFrame = panel:Add("DPanel") - helpFrame:Dock(FILL) - helpFrame.Paint = function(this, w, h) + helpPanel = panel:Add("DListView") + helpPanel:Dock(FILL) + helpPanel.Paint = function(this, w, h) end - html = helpFrame:Add("DHTML") + html = helpPanel:Add("DHTML") html:Dock(FILL) html:SetHTML(header..HELP_DEFAULT) @@ -99,7 +99,6 @@ if (CLIENT) then end end end) -end hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) tabs["commands"] = function(node) @@ -190,60 +189,78 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) } tabs["Credits"] = function(node) - local body = "" + local body = [[ +
+
+ +
NutScript +
+
+ ]] local scrW = ScrW() local scrH = ScrH() local offsetW = scrW*0.025 local offsetH = scrH*0.06 - - local title = helpFrame:Add("DLabel") - title:SetText("NutScript") - title:SetFont("nutBigFont") - title:SetTextColor(Color(86,111,183)) - title:SizeToContents() - title:Dock(TOP) - - local nscredits = helpFrame:Add("DPanel") + local offsetC = scrW*0.25 + + local nscredits = helpPanel:Add("DPanel") nscredits:Dock(FILL) - nscredits:DockMargin(0, 7.5, 0, 0) - nscredits.Paint = function(this, w, h) + nscredits:DockMargin(0, scrH*0.32, 0, 0) + nscredits.Paint = function() end for k, v in ipairs(authorCredits) do local offsetH = k == 1 and 0 or (k*offsetH)-offsetH steamworks.RequestPlayerInfo( v.steamid, function(steamName) - local name = nscredits:Add("DLabel") - name:SetText(steamName) - name:SetPos(offsetW, offsetH) - name:SetFont("nshtml") - name:SetTextColor(Color(255, 255, 255)) - name:SizeToContents() + devName = nscredits:Add("DLabel") + devName:SetText(steamName) + -- devName:SetPos(offsetW, offsetH) + devName:SetFont("nshtml") + devName:SetTextColor(Color(255, 255, 255)) + devName:SizeToContents() end ) local desc = nscredits:Add("DLabel") desc:SetText(v.desc) - desc:SetPos(offsetW, offsetH+(scrH*0.022)) desc:SetFont("nutSmallFont") desc:SetTextColor(Color(255, 255, 255)) desc:SizeToContents() - cAvatar = vgui.Create("AvatarImage", nscredits) - cAvatar:SetPos(0, offsetH) - cAvatar:SetSteamID(v.steamid, 64) - cAvatar:SetSize(40, 40) - cAvatar.OnCursorEntered = function() + -- fucking end my suffering + -- this shit took me over three hours... + local offsetW2 = string.Explode(" ", desc:GetTextSize())[1] + local offsetW3 = string.Explode(" ", devName:GetTextSize())[1] + + devName:SetPos(devName:GetPos()+(offsetC-devName:GetPos()), 0) + devName:SetPos(devName:GetPos()-(offsetW3/2), offsetH) + + desc:SetPos(desc:GetPos()+(offsetC-desc:GetPos()), 0) + desc:SetPos(desc:GetPos()-(offsetW2/2), offsetH+(scrH*0.022)) + + devAvatar = vgui.Create("AvatarImage", nscredits) + devAvatar:SetPos(devName:GetPos()-(scrW*0.025), offsetH) + devAvatar:SetSteamID(v.steamid, 64) + devAvatar:SetSize(40, 40) + devAvatar.OnCursorEntered = function() surface.PlaySound("garrysmod/ui_return.wav") end - cAvatar.OnMousePressed = function(self) + devAvatar.OnMousePressed = function() surface.PlaySound("buttons/button14.wav") - + gui.OpenURL("http://steamcommunity.com/profiles/"..v.steamid) end - - body = body.."

" end + /* body = (body..[[ +
+

+ %s
+ %s +

+
+ ]]):format("Contributors", "Ongoing support from various developers via Github") */ + return body end end) From a219d521a880d349414de494b026d472ee8d80cd Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 18:25:17 +0100 Subject: [PATCH 27/29] Added contributors to the credits --- plugins/f1menu/derma/cl_helps.lua | 88 ++++++++++++++++++------------- 1 file changed, 52 insertions(+), 36 deletions(-) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index 04869e81..7ad4b7cc 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -185,7 +185,8 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) {desc = "Creator", steamid = "76561198030127257"}, -- Chessnut {desc = "Co-Creator", steamid = "76561197999893894"}, -- Black Tea {desc = "Developer", steamid = "76561198060659964"}, -- Zoephix - {desc = "Developer", steamid = "76561198070441753"} -- TovarischPootis + {desc = "Developer", steamid = "76561198070441753"}, -- TovarischPootis + {desc = "Contributors", button = "View all contributors", url = "https://github.com/NutScript/NutScript/graphs/contributors"} } tabs["Credits"] = function(node) @@ -209,23 +210,45 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) nscredits.Paint = function() end + local function createDevName(text) + devName = nscredits:Add("DLabel") + devName:SetText(text) + devName:SetFont("nshtml") + devName:SetTextColor(Color(255, 255, 255)) + devName:SizeToContents() + end + for k, v in ipairs(authorCredits) do local offsetH = k == 1 and 0 or (k*offsetH)-offsetH - steamworks.RequestPlayerInfo( v.steamid, function(steamName) - devName = nscredits:Add("DLabel") - devName:SetText(steamName) - -- devName:SetPos(offsetW, offsetH) - devName:SetFont("nshtml") - devName:SetTextColor(Color(255, 255, 255)) - devName:SizeToContents() - end ) - - local desc = nscredits:Add("DLabel") - desc:SetText(v.desc) - desc:SetFont("nutSmallFont") - desc:SetTextColor(Color(255, 255, 255)) - desc:SizeToContents() + if (v.steamid) then + steamworks.RequestPlayerInfo(v.steamid, function(steamName) + createDevName(steamName) + end) + -- + -- add custom field + else + createDevName(v.desc) + end + + if (v.steamid or v.text) then + desc = nscredits:Add("DLabel") + desc:SetText(v.desc) + desc:SetFont("nutSmallFont") + desc:SetTextColor(Color(255, 255, 255)) + desc:SizeToContents() + elseif (v.button) then + desc = nscredits:Add("DButton") + desc:SetText(v.button) + desc:SizeToContents() + desc.DoClick = function() + surface.PlaySound("buttons/button14.wav") + + gui.OpenURL(v.url) + end + + offsetH = offsetH + 25 + end -- fucking end my suffering -- this shit took me over three hours... @@ -236,31 +259,24 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) devName:SetPos(devName:GetPos()-(offsetW3/2), offsetH) desc:SetPos(desc:GetPos()+(offsetC-desc:GetPos()), 0) - desc:SetPos(desc:GetPos()-(offsetW2/2), offsetH+(scrH*0.022)) - - devAvatar = vgui.Create("AvatarImage", nscredits) - devAvatar:SetPos(devName:GetPos()-(scrW*0.025), offsetH) - devAvatar:SetSteamID(v.steamid, 64) - devAvatar:SetSize(40, 40) - devAvatar.OnCursorEntered = function() - surface.PlaySound("garrysmod/ui_return.wav") - end - devAvatar.OnMousePressed = function() - surface.PlaySound("buttons/button14.wav") + desc:SetPos(desc:GetPos()-(offsetW2/2), offsetH+(v.button and scrH*0.03 or scrH*0.022)) + + if (v.steamid) then + devAvatar = vgui.Create("AvatarImage", nscredits) + devAvatar:SetPos(devName:GetPos()-(scrW*0.025), offsetH) + devAvatar:SetSteamID(v.steamid, 64) + devAvatar:SetSize(40, 40) + devAvatar.OnCursorEntered = function() + surface.PlaySound("garrysmod/ui_return.wav") + end + devAvatar.OnMousePressed = function() + surface.PlaySound("buttons/button14.wav") - gui.OpenURL("http://steamcommunity.com/profiles/"..v.steamid) + gui.OpenURL("http://steamcommunity.com/profiles/"..v.steamid) + end end end - /* body = (body..[[ -
-

- %s
- %s -

-
- ]]):format("Contributors", "Ongoing support from various developers via Github") */ - return body end end) From 2ddf7d57624656cb5715ad6bdcd21ed925f74750 Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 18:39:23 +0100 Subject: [PATCH 28/29] Increased spacing, extra polishing --- plugins/f1menu/derma/cl_helps.lua | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index 7ad4b7cc..0a739120 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -1,6 +1,12 @@ - surface.CreateFont("nshtml", { - font = "Tahoma", + surface.CreateFont("nutSmallCredits", { + font = "Roboto", + size = 17, + weight = 400 + }) + + surface.CreateFont("nutBigCredits", { + font = "Roboto", size = 25, weight = 600 }) @@ -201,10 +207,10 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) local scrW = ScrW() local scrH = ScrH() local offsetW = scrW*0.025 - local offsetH = scrH*0.06 + local offsetH = scrH*0.08 local offsetC = scrW*0.25 - local nscredits = helpPanel:Add("DPanel") + local nscredits = helpPanel:Add("DScrollPanel") nscredits:Dock(FILL) nscredits:DockMargin(0, scrH*0.32, 0, 0) nscredits.Paint = function() @@ -213,7 +219,7 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) local function createDevName(text) devName = nscredits:Add("DLabel") devName:SetText(text) - devName:SetFont("nshtml") + devName:SetFont("nutBigCredits") devName:SetTextColor(Color(255, 255, 255)) devName:SizeToContents() end @@ -234,7 +240,7 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) if (v.steamid or v.text) then desc = nscredits:Add("DLabel") desc:SetText(v.desc) - desc:SetFont("nutSmallFont") + desc:SetFont("nutSmallCredits") desc:SetTextColor(Color(255, 255, 255)) desc:SizeToContents() elseif (v.button) then @@ -252,7 +258,7 @@ hook.Add("BuildHelpMenu", "nutBasicHelp", function(tabs) -- fucking end my suffering -- this shit took me over three hours... - local offsetW2 = string.Explode(" ", desc:GetTextSize())[1] + local offsetW2 = string.Explode(" ", v.button and desc:GetSize() or desc:GetTextSize())[1] local offsetW3 = string.Explode(" ", devName:GetTextSize())[1] devName:SetPos(devName:GetPos()+(offsetC-devName:GetPos()), 0) From b18ff544f51fa6680baf5660b7a83cdaa360c4db Mon Sep 17 00:00:00 2001 From: Zoephix Date: Mon, 15 Feb 2021 19:07:21 +0100 Subject: [PATCH 29/29] Increased font size --- plugins/f1menu/derma/cl_helps.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/f1menu/derma/cl_helps.lua b/plugins/f1menu/derma/cl_helps.lua index 0a739120..380fa065 100644 --- a/plugins/f1menu/derma/cl_helps.lua +++ b/plugins/f1menu/derma/cl_helps.lua @@ -1,7 +1,7 @@ surface.CreateFont("nutSmallCredits", { font = "Roboto", - size = 17, + size = 18, weight = 400 })