From a8ce317d28a2fd816a487d1a8bf2931ee0384ed7 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 31 May 2026 07:38:03 +0100 Subject: [PATCH 001/117] Add call action annotation overrides --- custom/Entity.NetworkVar.lua | 9 +++++++++ custom/Entity.NetworkVarElement.lua | 10 ++++++++++ custom/Global.AccessorFunc.lua | 1 + custom/Global.Color.lua | 11 +++++++++++ custom/Global.CreateClientConVar.lua | 13 +++++++++++++ custom/Global.CreateConVar.lua | 1 + custom/Global.DEFINE_BASECLASS.lua | 7 +++++++ custom/Global.DeriveGamemode.lua | 6 ++++++ custom/Panel.Add.lua | 1 + custom/Panel.GetSkin.lua | 6 ++++++ custom/Panel.SetSkin.lua | 8 ++++++++ custom/concommand.Add.lua | 11 +++++++++++ custom/derma.DefineControl.lua | 12 ++++++++++++ custom/derma.DefineSkin.lua | 9 +++++++++ custom/derma.GetDefaultSkin.lua | 6 ++++++ custom/derma.GetNamedSkin.lua | 8 ++++++++ custom/derma.GetSkinTable.lua | 6 ++++++ custom/derma.SkinHook.lua | 10 ++++++++++ custom/hook.Add.lua | 1 + custom/hook.Call.lua | 10 ++++++++++ custom/hook.Run.lua | 9 +++++++++ custom/net.Receive.lua | 7 +++++++ custom/net.Start.lua | 8 ++++++++ custom/timer.Create.lua | 10 ++++++++++ custom/timer.Simple.lua | 8 ++++++++ custom/util.AddNetworkString.lua | 7 +++++++ custom/vgui.Create.lua | 1 + custom/vgui.CreateX.lua | 1 + custom/vgui.Register.lua | 11 +++++++++++ 29 files changed, 208 insertions(+) create mode 100644 custom/Entity.NetworkVar.lua create mode 100644 custom/Entity.NetworkVarElement.lua create mode 100644 custom/Global.Color.lua create mode 100644 custom/Global.CreateClientConVar.lua create mode 100644 custom/Global.DEFINE_BASECLASS.lua create mode 100644 custom/Global.DeriveGamemode.lua create mode 100644 custom/Panel.GetSkin.lua create mode 100644 custom/Panel.SetSkin.lua create mode 100644 custom/concommand.Add.lua create mode 100644 custom/derma.DefineControl.lua create mode 100644 custom/derma.DefineSkin.lua create mode 100644 custom/derma.GetDefaultSkin.lua create mode 100644 custom/derma.GetNamedSkin.lua create mode 100644 custom/derma.GetSkinTable.lua create mode 100644 custom/derma.SkinHook.lua create mode 100644 custom/hook.Call.lua create mode 100644 custom/hook.Run.lua create mode 100644 custom/net.Receive.lua create mode 100644 custom/net.Start.lua create mode 100644 custom/timer.Create.lua create mode 100644 custom/timer.Simple.lua create mode 100644 custom/util.AddNetworkString.lua create mode 100644 custom/vgui.Register.lua diff --git a/custom/Entity.NetworkVar.lua b/custom/Entity.NetworkVar.lua new file mode 100644 index 00000000..891fe3af --- /dev/null +++ b/custom/Entity.NetworkVar.lua @@ -0,0 +1,9 @@ +---Creates a network variable and generated Get/Set accessors for the entity. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:NetworkVar +---@callaction gmod.class kind=network_var type_arg=1 name_arg=3 fallback_name_arg=2 +---@param type string The NetworkVar type. +---@param slot number The NetworkVar slot. +---@param name string Name of the variable, used for generated Get/Set accessors. +---@param extended? table Extra NetworkVar information. +function Entity:NetworkVar(type, slot, name, extended) end diff --git a/custom/Entity.NetworkVarElement.lua b/custom/Entity.NetworkVarElement.lua new file mode 100644 index 00000000..14b039bc --- /dev/null +++ b/custom/Entity.NetworkVarElement.lua @@ -0,0 +1,10 @@ +---Creates Get/Set accessors for a vector or angle element NetworkVar. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:NetworkVarElement +---@callaction gmod.class kind=network_var_element type_arg=1 name_arg=4 fallback_name_arg=3 fallback_name_arg2=2 +---@param type string The NetworkVar type. +---@param slot number The NetworkVar slot. +---@param element number The vector or angle element. +---@param name string Name of the variable, used for generated Get/Set accessors. +---@param extended? table Extra NetworkVar information. +function Entity:NetworkVarElement(type, slot, element, name, extended) end diff --git a/custom/Global.AccessorFunc.lua b/custom/Global.AccessorFunc.lua index 3371b5da..18c4d384 100644 --- a/custom/Global.AccessorFunc.lua +++ b/custom/Global.AccessorFunc.lua @@ -4,6 +4,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.AccessorFunc ---@accessorfunc 2 +---@callaction gmod.class kind=accessor_func ---@param tab table The table to add the accessor functions to. ---@param key any The key of the table to be get/set. ---@param name string The name of the functions (will be prefixed with Get and Set). diff --git a/custom/Global.Color.lua b/custom/Global.Color.lua new file mode 100644 index 00000000..5b82b6de --- /dev/null +++ b/custom/Global.Color.lua @@ -0,0 +1,11 @@ +---Creates a new Color. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.Color +---@callaction gmod.color kind=rgba r_arg=1 g_arg=2 b_arg=3 a_arg=4 +---@param r number The red channel, from 0 to 255. +---@param g number The green channel, from 0 to 255. +---@param b number The blue channel, from 0 to 255. +---@param a? number The alpha channel, from 0 to 255. +---@return Color +function _G.Color(r, g, b, a) end diff --git a/custom/Global.CreateClientConVar.lua b/custom/Global.CreateClientConVar.lua new file mode 100644 index 00000000..092ce65c --- /dev/null +++ b/custom/Global.CreateClientConVar.lua @@ -0,0 +1,13 @@ +---Creates a client-side console variable. +---@realm client +---@source https://wiki.facepunch.com/gmod/Global.CreateClientConVar +---@callaction gmod.system kind=create_client_convar name_arg=1 +---@param name string +---@param default string|number +---@param shouldsave? boolean +---@param userinfo? boolean +---@param helptext? string +---@param min? number +---@param max? number +---@return (instance) ConVar +function _G.CreateClientConVar(name, default, shouldsave, userinfo, helptext, min, max) end diff --git a/custom/Global.CreateConVar.lua b/custom/Global.CreateConVar.lua index a8fcfef7..100e924d 100644 --- a/custom/Global.CreateConVar.lua +++ b/custom/Global.CreateConVar.lua @@ -4,6 +4,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.CreateConVar +---@callaction gmod.system kind=create_convar name_arg=1 ---@param name string ---@param value string|number ---@param flags? FCVAR|number[] diff --git a/custom/Global.DEFINE_BASECLASS.lua b/custom/Global.DEFINE_BASECLASS.lua new file mode 100644 index 00000000..813e9f65 --- /dev/null +++ b/custom/Global.DEFINE_BASECLASS.lua @@ -0,0 +1,7 @@ +---Declares the BaseClass helper for scripted classes. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.DEFINE_BASECLASS +---@callaction gmod.class kind=define_baseclass base_arg=1 +---@param value string Base class name. +function _G.DEFINE_BASECLASS(value) end diff --git a/custom/Global.DeriveGamemode.lua b/custom/Global.DeriveGamemode.lua new file mode 100644 index 00000000..13e53ffc --- /dev/null +++ b/custom/Global.DeriveGamemode.lua @@ -0,0 +1,6 @@ +---Derives the current gamemode from another gamemode. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Global.DeriveGamemode +---@callaction gmod.class kind=derive_gamemode base_arg=1 +---@param base string Base gamemode folder name. +function _G.DeriveGamemode(base) end diff --git a/custom/Panel.Add.lua b/custom/Panel.Add.lua index 0e60e0a0..7b8894d4 100644 --- a/custom/Panel.Add.lua +++ b/custom/Panel.Add.lua @@ -2,6 +2,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/Panel:Add +---@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T : Panel ---@overload fun(self: Panel, panelTable: table): Panel # Creates a panel from a PANEL table and parents it to this panel. ---@param object `T`|T The panel to add, or a panel class name to create and add. diff --git a/custom/Panel.GetSkin.lua b/custom/Panel.GetSkin.lua new file mode 100644 index 00000000..51aad8c9 --- /dev/null +++ b/custom/Panel.GetSkin.lua @@ -0,0 +1,6 @@ +---Returns the table for the derma skin currently being used by this panel object. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:GetSkin +---@return SKIN # The derma skin table currently being used by this object. +function Panel:GetSkin() end diff --git a/custom/Panel.SetSkin.lua b/custom/Panel.SetSkin.lua new file mode 100644 index 00000000..bc3a7692 --- /dev/null +++ b/custom/Panel.SetSkin.lua @@ -0,0 +1,8 @@ +---Sets the derma skin that the panel object will use. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:SetSkin +---@callaction gmod.derma_skin kind=reference name_arg=1 +---@callaction gmod.derma_skin kind=reference name_arg=2 +---@param skinName string The name of the skin to use. The default derma skin is `Default`. +function Panel:SetSkin(skinName) end diff --git a/custom/concommand.Add.lua b/custom/concommand.Add.lua new file mode 100644 index 00000000..74d23ea8 --- /dev/null +++ b/custom/concommand.Add.lua @@ -0,0 +1,11 @@ +---Creates a console command that runs the supplied callback. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/concommand.Add +---@callaction gmod.system kind=concommand_add name_arg=1 callback_arg=2 +---@param name string Name of the console command. +---@param callback fun(ply: Player, cmd: string, args: string[], argStr: string) Callback run when the command is executed. +---@param autoComplete? function +---@param helpText? string +---@param flags? FCVAR|number[] +function concommand.Add(name, callback, autoComplete, helpText, flags) end diff --git a/custom/derma.DefineControl.lua b/custom/derma.DefineControl.lua new file mode 100644 index 00000000..3e2d6554 --- /dev/null +++ b/custom/derma.DefineControl.lua @@ -0,0 +1,12 @@ +---Defines a new Derma control with an optional base. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.DefineControl +---@callaction gmod.class kind=derma_define_control class_arg=1 table_arg=3 base_arg=4 +---@generic T: Panel +---@param name string Name of the newly created control. +---@param description string Description of the control. +---@param tab T Table containing control methods and properties. +---@param base string Derma control to base the new control off of. +---@return T # A table containing the new control's methods and properties. +function derma.DefineControl(name, description, tab, base) end diff --git a/custom/derma.DefineSkin.lua b/custom/derma.DefineSkin.lua new file mode 100644 index 00000000..f14f50e8 --- /dev/null +++ b/custom/derma.DefineSkin.lua @@ -0,0 +1,9 @@ +---Defines a new skin so that it is usable by Derma. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.DefineSkin +---@callaction gmod.derma_skin kind=define_skin name_arg=1 table_arg=3 +---@param name string Name of the skin. +---@param description string Description of the skin. +---@param skin SKIN Table containing skin data. +function derma.DefineSkin(name, description, skin) end diff --git a/custom/derma.GetDefaultSkin.lua b/custom/derma.GetDefaultSkin.lua new file mode 100644 index 00000000..c6447d63 --- /dev/null +++ b/custom/derma.GetDefaultSkin.lua @@ -0,0 +1,6 @@ +---Returns the default skin table. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.GetDefaultSkin +---@return SKIN # The default skin table. +function derma.GetDefaultSkin() end diff --git a/custom/derma.GetNamedSkin.lua b/custom/derma.GetNamedSkin.lua new file mode 100644 index 00000000..4c06dd9f --- /dev/null +++ b/custom/derma.GetNamedSkin.lua @@ -0,0 +1,8 @@ +---Returns the skin table of the skin with the supplied name. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.GetNamedSkin +---@callaction gmod.derma_skin kind=reference name_arg=1 +---@param name string Name of skin. +---@return SKIN? # The skin table. +function derma.GetNamedSkin(name) end diff --git a/custom/derma.GetSkinTable.lua b/custom/derma.GetSkinTable.lua new file mode 100644 index 00000000..59b1e92f --- /dev/null +++ b/custom/derma.GetSkinTable.lua @@ -0,0 +1,6 @@ +---Returns a copy of the table containing every Derma skin. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.GetSkinTable +---@return table # Table of every Derma skin. +function derma.GetSkinTable() end diff --git a/custom/derma.SkinHook.lua b/custom/derma.SkinHook.lua new file mode 100644 index 00000000..65900f2a --- /dev/null +++ b/custom/derma.SkinHook.lua @@ -0,0 +1,10 @@ +---Checks if a matching hook function exists in the panel's skin, then calls it. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/derma.SkinHook +---@param type string The type of hook to run, usually `Paint`. +---@param name string The name of the hook or panel to run. Example: `Button`. +---@param panel Panel The panel to call the hook for. +---@param ... any Arguments forwarded to the skin hook. +---@return any # The returned variable from the skin hook. +function derma.SkinHook(type, name, panel, ...) end diff --git a/custom/hook.Add.lua b/custom/hook.Add.lua index 3843590e..fdaed93d 100644 --- a/custom/hook.Add.lua +++ b/custom/hook.Add.lua @@ -2,6 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/hook.Add +---@callaction gmod.hook kind=add name_arg=1 callback_arg=3 ---@param eventName string The event to hook on to. This can be any GM_Hooks hook, gameevent after using gameevent.Listen, or custom hook run with hook.Call or hook.Run. ---@param identifier any The unique identifier, usually a string. This can be used elsewhere in the code to replace or remove the hook. The identifier **should** be unique so that you do not accidentally override some other mods hook, unless that's what you are trying to do. ---@param func function The function to be called, arguments given to it depend on the identifier used. diff --git a/custom/hook.Call.lua b/custom/hook.Call.lua new file mode 100644 index 00000000..4496fbff --- /dev/null +++ b/custom/hook.Call.lua @@ -0,0 +1,10 @@ +---Calls a hook and returns the first non-nil value returned by hook listeners. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/hook.Call +---@callaction gmod.hook kind=emit name_arg=1 +---@param eventName string The hook event to call. +---@param gamemodeTable? table The gamemode table to call the hook on. +---@param ... any Arguments to pass to the hook. +---@return any +function hook.Call(eventName, gamemodeTable, ...) end diff --git a/custom/hook.Run.lua b/custom/hook.Run.lua new file mode 100644 index 00000000..26d34e66 --- /dev/null +++ b/custom/hook.Run.lua @@ -0,0 +1,9 @@ +---Calls a hook without explicitly passing a gamemode table. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/hook.Run +---@callaction gmod.hook kind=emit name_arg=1 +---@param eventName string The hook event to call. +---@param ... any Arguments to pass to the hook. +---@return any +function hook.Run(eventName, ...) end diff --git a/custom/net.Receive.lua b/custom/net.Receive.lua new file mode 100644 index 00000000..03baa4a9 --- /dev/null +++ b/custom/net.Receive.lua @@ -0,0 +1,7 @@ +---Registers a callback for a network message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.Receive +---@callaction gmod.system kind=net_receive name_arg=1 callback_arg=2 +---@param messageName string The message name to hook to. +---@param callback fun(len: number, ply: Player) The function to be called if the specified message was received. +function net.Receive(messageName, callback) end diff --git a/custom/net.Start.lua b/custom/net.Start.lua new file mode 100644 index 00000000..95992a9c --- /dev/null +++ b/custom/net.Start.lua @@ -0,0 +1,8 @@ +---Begins a new net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.Start +---@callaction gmod.system kind=net_start name_arg=1 +---@param messageName string The name of the message to send. +---@param unreliable? boolean If set to `true`, the message is not guaranteed to reach its destination. +---@return boolean # `true` if the message has been started. +function net.Start(messageName, unreliable) end diff --git a/custom/timer.Create.lua b/custom/timer.Create.lua new file mode 100644 index 00000000..c08ba70e --- /dev/null +++ b/custom/timer.Create.lua @@ -0,0 +1,10 @@ +---Creates a new named timer. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/timer.Create +---@callaction gmod.system kind=timer_create name_arg=1 callback_arg=4 +---@param identifier string Identifier of the timer to create. +---@param delay number The delay interval in seconds. +---@param repetitions number The number of times to repeat the timer. Use `0` for infinite repetitions. +---@param func function Function called when timer has finished the countdown. +function timer.Create(identifier, delay, repetitions, func) end diff --git a/custom/timer.Simple.lua b/custom/timer.Simple.lua new file mode 100644 index 00000000..d759a71c --- /dev/null +++ b/custom/timer.Simple.lua @@ -0,0 +1,8 @@ +---Creates a simple one-shot timer. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/timer.Simple +---@callaction gmod.system kind=timer_simple callback_arg=2 +---@param delay number Delay in seconds. +---@param func function Function called when timer has finished the countdown. +function timer.Simple(delay, func) end diff --git a/custom/util.AddNetworkString.lua b/custom/util.AddNetworkString.lua new file mode 100644 index 00000000..21aa4044 --- /dev/null +++ b/custom/util.AddNetworkString.lua @@ -0,0 +1,7 @@ +---Adds the specified string to the network string table. +---@realm server +---@source https://wiki.facepunch.com/gmod/util.AddNetworkString +---@callaction gmod.system kind=net_message_register name_arg=1 +---@param str string Adds the specified string to the string table. +---@return number # The id of the string that was added to the string table. +function util.AddNetworkString(str) end diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua index 713d9327..8bcb7e5d 100644 --- a/custom/vgui.Create.lua +++ b/custom/vgui.Create.lua @@ -3,6 +3,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.Create +---@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T: Panel ---@param classname `T` Classname of the panel to create. --- diff --git a/custom/vgui.CreateX.lua b/custom/vgui.CreateX.lua index 3ee13eda..18e8ef30 100644 --- a/custom/vgui.CreateX.lua +++ b/custom/vgui.CreateX.lua @@ -3,6 +3,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.CreateX +---@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T : Panel ---@param class `T` Class of the panel to create ---@param parent? Panel If specified, parents created panel to given one diff --git a/custom/vgui.Register.lua b/custom/vgui.Register.lua new file mode 100644 index 00000000..cc64d569 --- /dev/null +++ b/custom/vgui.Register.lua @@ -0,0 +1,11 @@ +---Registers a panel for later creation via vgui.Create. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/vgui.Register +---@callaction gmod.class kind=vgui_register class_arg=1 table_arg=2 base_arg=3 +---@generic T: Panel +---@param classname string Classname of the panel to register. +---@param panelTable T The table containing the panel information. +---@param baseName? string Classname of a panel to inherit functionality from. +---@return T # The given panel table from second argument. +function vgui.Register(classname, panelTable, baseName) end From 6b46a60ff7c53115ea654ef455ec010187446162 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 7 Jun 2026 18:01:16 +0100 Subject: [PATCH 002/117] Use generic call argument --- custom/Entity.NetworkVar.lua | 3 ++- custom/Entity.NetworkVarElement.lua | 3 ++- custom/Global.AccessorFunc.lua | 1 - custom/Global.Color.lua | 5 ++++- custom/Global.CreateClientConVar.lua | 2 +- custom/Global.CreateConVar.lua | 2 +- custom/Global.DEFINE_BASECLASS.lua | 2 +- custom/Global.DeriveGamemode.lua | 2 +- custom/Panel.Add.lua | 2 +- custom/Panel.SetSkin.lua | 3 +-- custom/concommand.Add.lua | 2 +- custom/derma.DefineControl.lua | 3 ++- custom/derma.DefineSkin.lua | 2 +- custom/derma.GetNamedSkin.lua | 2 +- custom/hook.Add.lua | 2 +- custom/hook.Call.lua | 2 +- custom/hook.Run.lua | 2 +- custom/net.Receive.lua | 2 +- custom/net.Start.lua | 2 +- custom/timer.Create.lua | 2 +- custom/timer.Simple.lua | 1 - custom/util.AddNetworkString.lua | 2 +- custom/vgui.Create.lua | 2 +- custom/vgui.CreateX.lua | 2 +- custom/vgui.Register.lua | 3 ++- 25 files changed, 30 insertions(+), 26 deletions(-) diff --git a/custom/Entity.NetworkVar.lua b/custom/Entity.NetworkVar.lua index 891fe3af..d64f9200 100644 --- a/custom/Entity.NetworkVar.lua +++ b/custom/Entity.NetworkVar.lua @@ -1,9 +1,10 @@ ---Creates a network variable and generated Get/Set accessors for the entity. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:NetworkVar ----@callaction gmod.class kind=network_var type_arg=1 name_arg=3 fallback_name_arg=2 +---@[call_arg("gmod.network_var", "type")] ---@param type string The NetworkVar type. ---@param slot number The NetworkVar slot. +---@[call_arg("gmod.network_var", "define")] ---@param name string Name of the variable, used for generated Get/Set accessors. ---@param extended? table Extra NetworkVar information. function Entity:NetworkVar(type, slot, name, extended) end diff --git a/custom/Entity.NetworkVarElement.lua b/custom/Entity.NetworkVarElement.lua index 14b039bc..1f65e296 100644 --- a/custom/Entity.NetworkVarElement.lua +++ b/custom/Entity.NetworkVarElement.lua @@ -1,10 +1,11 @@ ---Creates Get/Set accessors for a vector or angle element NetworkVar. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:NetworkVarElement ----@callaction gmod.class kind=network_var_element type_arg=1 name_arg=4 fallback_name_arg=3 fallback_name_arg2=2 +---@[call_arg("gmod.network_var", "type")] ---@param type string The NetworkVar type. ---@param slot number The NetworkVar slot. ---@param element number The vector or angle element. +---@[call_arg("gmod.network_var", "define")] ---@param name string Name of the variable, used for generated Get/Set accessors. ---@param extended? table Extra NetworkVar information. function Entity:NetworkVarElement(type, slot, element, name, extended) end diff --git a/custom/Global.AccessorFunc.lua b/custom/Global.AccessorFunc.lua index 18c4d384..3371b5da 100644 --- a/custom/Global.AccessorFunc.lua +++ b/custom/Global.AccessorFunc.lua @@ -4,7 +4,6 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.AccessorFunc ---@accessorfunc 2 ----@callaction gmod.class kind=accessor_func ---@param tab table The table to add the accessor functions to. ---@param key any The key of the table to be get/set. ---@param name string The name of the functions (will be prefixed with Get and Set). diff --git a/custom/Global.Color.lua b/custom/Global.Color.lua index 5b82b6de..b82b777f 100644 --- a/custom/Global.Color.lua +++ b/custom/Global.Color.lua @@ -2,10 +2,13 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.Color ----@callaction gmod.color kind=rgba r_arg=1 g_arg=2 b_arg=3 a_arg=4 +---@[call_arg("gmod.color", "r")] ---@param r number The red channel, from 0 to 255. +---@[call_arg("gmod.color", "g")] ---@param g number The green channel, from 0 to 255. +---@[call_arg("gmod.color", "b")] ---@param b number The blue channel, from 0 to 255. +---@[call_arg("gmod.color", "a")] ---@param a? number The alpha channel, from 0 to 255. ---@return Color function _G.Color(r, g, b, a) end diff --git a/custom/Global.CreateClientConVar.lua b/custom/Global.CreateClientConVar.lua index 092ce65c..f3b2a1ff 100644 --- a/custom/Global.CreateClientConVar.lua +++ b/custom/Global.CreateClientConVar.lua @@ -1,7 +1,7 @@ ---Creates a client-side console variable. ---@realm client ---@source https://wiki.facepunch.com/gmod/Global.CreateClientConVar ----@callaction gmod.system kind=create_client_convar name_arg=1 +---@[call_arg("gmod.convar", "define")] ---@param name string ---@param default string|number ---@param shouldsave? boolean diff --git a/custom/Global.CreateConVar.lua b/custom/Global.CreateConVar.lua index 100e924d..564a6c97 100644 --- a/custom/Global.CreateConVar.lua +++ b/custom/Global.CreateConVar.lua @@ -4,7 +4,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.CreateConVar ----@callaction gmod.system kind=create_convar name_arg=1 +---@[call_arg("gmod.convar", "define")] ---@param name string ---@param value string|number ---@param flags? FCVAR|number[] diff --git a/custom/Global.DEFINE_BASECLASS.lua b/custom/Global.DEFINE_BASECLASS.lua index 813e9f65..dc51d60f 100644 --- a/custom/Global.DEFINE_BASECLASS.lua +++ b/custom/Global.DEFINE_BASECLASS.lua @@ -2,6 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.DEFINE_BASECLASS ----@callaction gmod.class kind=define_baseclass base_arg=1 +---@[call_arg("gmod.class_base", "reference")] ---@param value string Base class name. function _G.DEFINE_BASECLASS(value) end diff --git a/custom/Global.DeriveGamemode.lua b/custom/Global.DeriveGamemode.lua index 13e53ffc..d3fa3a5e 100644 --- a/custom/Global.DeriveGamemode.lua +++ b/custom/Global.DeriveGamemode.lua @@ -1,6 +1,6 @@ ---Derives the current gamemode from another gamemode. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Global.DeriveGamemode ----@callaction gmod.class kind=derive_gamemode base_arg=1 +---@[call_arg("gmod.gamemode", "reference")] ---@param base string Base gamemode folder name. function _G.DeriveGamemode(base) end diff --git a/custom/Panel.Add.lua b/custom/Panel.Add.lua index 7b8894d4..d178c229 100644 --- a/custom/Panel.Add.lua +++ b/custom/Panel.Add.lua @@ -2,9 +2,9 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/Panel:Add ----@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T : Panel ---@overload fun(self: Panel, panelTable: table): Panel # Creates a panel from a PANEL table and parents it to this panel. +---@[call_arg("gmod.vgui_panel", "reference")] ---@param object `T`|T The panel to add, or a panel class name to create and add. ---@return (instance) T # The added or created panel function Panel:Add(object) end diff --git a/custom/Panel.SetSkin.lua b/custom/Panel.SetSkin.lua index bc3a7692..e156c8bd 100644 --- a/custom/Panel.SetSkin.lua +++ b/custom/Panel.SetSkin.lua @@ -2,7 +2,6 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/Panel:SetSkin ----@callaction gmod.derma_skin kind=reference name_arg=1 ----@callaction gmod.derma_skin kind=reference name_arg=2 +---@[call_arg("gmod.derma_skin", "reference")] ---@param skinName string The name of the skin to use. The default derma skin is `Default`. function Panel:SetSkin(skinName) end diff --git a/custom/concommand.Add.lua b/custom/concommand.Add.lua index 74d23ea8..23d324c6 100644 --- a/custom/concommand.Add.lua +++ b/custom/concommand.Add.lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/concommand.Add ----@callaction gmod.system kind=concommand_add name_arg=1 callback_arg=2 +---@[call_arg("gmod.concommand", "define")] ---@param name string Name of the console command. ---@param callback fun(ply: Player, cmd: string, args: string[], argStr: string) Callback run when the command is executed. ---@param autoComplete? function diff --git a/custom/derma.DefineControl.lua b/custom/derma.DefineControl.lua index 3e2d6554..09630fff 100644 --- a/custom/derma.DefineControl.lua +++ b/custom/derma.DefineControl.lua @@ -2,11 +2,12 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/derma.DefineControl ----@callaction gmod.class kind=derma_define_control class_arg=1 table_arg=3 base_arg=4 ---@generic T: Panel +---@[call_arg("gmod.vgui_panel", "define")] ---@param name string Name of the newly created control. ---@param description string Description of the control. ---@param tab T Table containing control methods and properties. +---@[call_arg("gmod.vgui_panel", "base")] ---@param base string Derma control to base the new control off of. ---@return T # A table containing the new control's methods and properties. function derma.DefineControl(name, description, tab, base) end diff --git a/custom/derma.DefineSkin.lua b/custom/derma.DefineSkin.lua index f14f50e8..3fd35b3d 100644 --- a/custom/derma.DefineSkin.lua +++ b/custom/derma.DefineSkin.lua @@ -2,7 +2,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/derma.DefineSkin ----@callaction gmod.derma_skin kind=define_skin name_arg=1 table_arg=3 +---@[call_arg("gmod.derma_skin", "define")] ---@param name string Name of the skin. ---@param description string Description of the skin. ---@param skin SKIN Table containing skin data. diff --git a/custom/derma.GetNamedSkin.lua b/custom/derma.GetNamedSkin.lua index 4c06dd9f..c317eb63 100644 --- a/custom/derma.GetNamedSkin.lua +++ b/custom/derma.GetNamedSkin.lua @@ -2,7 +2,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/derma.GetNamedSkin ----@callaction gmod.derma_skin kind=reference name_arg=1 +---@[call_arg("gmod.derma_skin", "reference")] ---@param name string Name of skin. ---@return SKIN? # The skin table. function derma.GetNamedSkin(name) end diff --git a/custom/hook.Add.lua b/custom/hook.Add.lua index fdaed93d..43d09b89 100644 --- a/custom/hook.Add.lua +++ b/custom/hook.Add.lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/hook.Add ----@callaction gmod.hook kind=add name_arg=1 callback_arg=3 +---@[call_arg("gmod.hook", "add")] ---@param eventName string The event to hook on to. This can be any GM_Hooks hook, gameevent after using gameevent.Listen, or custom hook run with hook.Call or hook.Run. ---@param identifier any The unique identifier, usually a string. This can be used elsewhere in the code to replace or remove the hook. The identifier **should** be unique so that you do not accidentally override some other mods hook, unless that's what you are trying to do. ---@param func function The function to be called, arguments given to it depend on the identifier used. diff --git a/custom/hook.Call.lua b/custom/hook.Call.lua index 4496fbff..21feb956 100644 --- a/custom/hook.Call.lua +++ b/custom/hook.Call.lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/hook.Call ----@callaction gmod.hook kind=emit name_arg=1 +---@[call_arg("gmod.hook", "emit")] ---@param eventName string The hook event to call. ---@param gamemodeTable? table The gamemode table to call the hook on. ---@param ... any Arguments to pass to the hook. diff --git a/custom/hook.Run.lua b/custom/hook.Run.lua index 26d34e66..c0e6c025 100644 --- a/custom/hook.Run.lua +++ b/custom/hook.Run.lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/hook.Run ----@callaction gmod.hook kind=emit name_arg=1 +---@[call_arg("gmod.hook", "emit")] ---@param eventName string The hook event to call. ---@param ... any Arguments to pass to the hook. ---@return any diff --git a/custom/net.Receive.lua b/custom/net.Receive.lua index 03baa4a9..226d13e0 100644 --- a/custom/net.Receive.lua +++ b/custom/net.Receive.lua @@ -1,7 +1,7 @@ ---Registers a callback for a network message. ---@realm shared ---@source https://wiki.facepunch.com/gmod/net.Receive ----@callaction gmod.system kind=net_receive name_arg=1 callback_arg=2 +---@[call_arg("gmod.net_message", "receive")] ---@param messageName string The message name to hook to. ---@param callback fun(len: number, ply: Player) The function to be called if the specified message was received. function net.Receive(messageName, callback) end diff --git a/custom/net.Start.lua b/custom/net.Start.lua index 95992a9c..1bc40b2e 100644 --- a/custom/net.Start.lua +++ b/custom/net.Start.lua @@ -1,7 +1,7 @@ ---Begins a new net message. ---@realm shared ---@source https://wiki.facepunch.com/gmod/net.Start ----@callaction gmod.system kind=net_start name_arg=1 +---@[call_arg("gmod.net_message", "start")] ---@param messageName string The name of the message to send. ---@param unreliable? boolean If set to `true`, the message is not guaranteed to reach its destination. ---@return boolean # `true` if the message has been started. diff --git a/custom/timer.Create.lua b/custom/timer.Create.lua index c08ba70e..35f45b6a 100644 --- a/custom/timer.Create.lua +++ b/custom/timer.Create.lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/timer.Create ----@callaction gmod.system kind=timer_create name_arg=1 callback_arg=4 +---@[call_arg("gmod.timer", "define")] ---@param identifier string Identifier of the timer to create. ---@param delay number The delay interval in seconds. ---@param repetitions number The number of times to repeat the timer. Use `0` for infinite repetitions. diff --git a/custom/timer.Simple.lua b/custom/timer.Simple.lua index d759a71c..1e5e03de 100644 --- a/custom/timer.Simple.lua +++ b/custom/timer.Simple.lua @@ -2,7 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/timer.Simple ----@callaction gmod.system kind=timer_simple callback_arg=2 ---@param delay number Delay in seconds. ---@param func function Function called when timer has finished the countdown. function timer.Simple(delay, func) end diff --git a/custom/util.AddNetworkString.lua b/custom/util.AddNetworkString.lua index 21aa4044..3523c774 100644 --- a/custom/util.AddNetworkString.lua +++ b/custom/util.AddNetworkString.lua @@ -1,7 +1,7 @@ ---Adds the specified string to the network string table. ---@realm server ---@source https://wiki.facepunch.com/gmod/util.AddNetworkString ----@callaction gmod.system kind=net_message_register name_arg=1 +---@[call_arg("gmod.net_message", "define")] ---@param str string Adds the specified string to the string table. ---@return number # The id of the string that was added to the string table. function util.AddNetworkString(str) end diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua index 8bcb7e5d..3f7d59af 100644 --- a/custom/vgui.Create.lua +++ b/custom/vgui.Create.lua @@ -3,8 +3,8 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.Create ----@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T: Panel +---@[call_arg("gmod.vgui_panel", "reference")] ---@param classname `T` Classname of the panel to create. --- --- Default panel classnames can be found on the VGUI Element List. diff --git a/custom/vgui.CreateX.lua b/custom/vgui.CreateX.lua index 18e8ef30..4cff4a81 100644 --- a/custom/vgui.CreateX.lua +++ b/custom/vgui.CreateX.lua @@ -3,8 +3,8 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.CreateX ----@callaction gmod.vgui_panel kind=reference name_arg=1 ---@generic T : Panel +---@[call_arg("gmod.vgui_panel", "reference")] ---@param class `T` Class of the panel to create ---@param parent? Panel If specified, parents created panel to given one ---@param name? string Name of the created panel diff --git a/custom/vgui.Register.lua b/custom/vgui.Register.lua index cc64d569..19d20b00 100644 --- a/custom/vgui.Register.lua +++ b/custom/vgui.Register.lua @@ -2,10 +2,11 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.Register ----@callaction gmod.class kind=vgui_register class_arg=1 table_arg=2 base_arg=3 ---@generic T: Panel +---@[call_arg("gmod.vgui_panel", "define")] ---@param classname string Classname of the panel to register. ---@param panelTable T The table containing the panel information. +---@[call_arg("gmod.vgui_panel", "base")] ---@param baseName? string Classname of a panel to inherit functionality from. ---@return T # The given panel table from second argument. function vgui.Register(classname, panelTable, baseName) end From 6e30871f5d189764d76ea7f8df434835370f0a09 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 7 Jun 2026 20:40:33 +0100 Subject: [PATCH 003/117] Annotate system call_arg --- custom/Global.CreateClientConVar.lua | 2 +- custom/Global.CreateConVar.lua | 2 +- custom/concommand.Add.lua | 1 + custom/timer.Create.lua | 1 + custom/timer.Simple.lua | 1 + 5 files changed, 5 insertions(+), 2 deletions(-) diff --git a/custom/Global.CreateClientConVar.lua b/custom/Global.CreateClientConVar.lua index f3b2a1ff..9fc30dfc 100644 --- a/custom/Global.CreateClientConVar.lua +++ b/custom/Global.CreateClientConVar.lua @@ -1,7 +1,7 @@ ---Creates a client-side console variable. ---@realm client ---@source https://wiki.facepunch.com/gmod/Global.CreateClientConVar ----@[call_arg("gmod.convar", "define")] +---@[call_arg("gmod.convar", "define_client")] ---@param name string ---@param default string|number ---@param shouldsave? boolean diff --git a/custom/Global.CreateConVar.lua b/custom/Global.CreateConVar.lua index 564a6c97..5416bcc7 100644 --- a/custom/Global.CreateConVar.lua +++ b/custom/Global.CreateConVar.lua @@ -4,7 +4,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.CreateConVar ----@[call_arg("gmod.convar", "define")] +---@[call_arg("gmod.convar", "define_server")] ---@param name string ---@param value string|number ---@param flags? FCVAR|number[] diff --git a/custom/concommand.Add.lua b/custom/concommand.Add.lua index 23d324c6..19b81695 100644 --- a/custom/concommand.Add.lua +++ b/custom/concommand.Add.lua @@ -4,6 +4,7 @@ ---@source https://wiki.facepunch.com/gmod/concommand.Add ---@[call_arg("gmod.concommand", "define")] ---@param name string Name of the console command. +---@[call_arg("gmod.concommand", "callback")] ---@param callback fun(ply: Player, cmd: string, args: string[], argStr: string) Callback run when the command is executed. ---@param autoComplete? function ---@param helpText? string diff --git a/custom/timer.Create.lua b/custom/timer.Create.lua index 35f45b6a..4b3bfa03 100644 --- a/custom/timer.Create.lua +++ b/custom/timer.Create.lua @@ -6,5 +6,6 @@ ---@param identifier string Identifier of the timer to create. ---@param delay number The delay interval in seconds. ---@param repetitions number The number of times to repeat the timer. Use `0` for infinite repetitions. +---@[call_arg("gmod.timer", "callback")] ---@param func function Function called when timer has finished the countdown. function timer.Create(identifier, delay, repetitions, func) end diff --git a/custom/timer.Simple.lua b/custom/timer.Simple.lua index 1e5e03de..c265c14f 100644 --- a/custom/timer.Simple.lua +++ b/custom/timer.Simple.lua @@ -3,5 +3,6 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/timer.Simple ---@param delay number Delay in seconds. +---@[call_arg("gmod.timer", "simple")] ---@param func function Function called when timer has finished the countdown. function timer.Simple(delay, func) end From 30cf125c130ef30a975ffc81387b767057654c8c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 7 Jun 2026 21:55:47 +0100 Subject: [PATCH 004/117] Annotate VGUI call_arg --- custom/derma.DefineControl.lua | 1 + custom/vgui.Register.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/custom/derma.DefineControl.lua b/custom/derma.DefineControl.lua index 09630fff..31ba5cdf 100644 --- a/custom/derma.DefineControl.lua +++ b/custom/derma.DefineControl.lua @@ -6,6 +6,7 @@ ---@[call_arg("gmod.vgui_panel", "define")] ---@param name string Name of the newly created control. ---@param description string Description of the control. +---@[call_arg("gmod.vgui_panel", "table")] ---@param tab T Table containing control methods and properties. ---@[call_arg("gmod.vgui_panel", "base")] ---@param base string Derma control to base the new control off of. diff --git a/custom/vgui.Register.lua b/custom/vgui.Register.lua index 19d20b00..859b3f8b 100644 --- a/custom/vgui.Register.lua +++ b/custom/vgui.Register.lua @@ -5,6 +5,7 @@ ---@generic T: Panel ---@[call_arg("gmod.vgui_panel", "define")] ---@param classname string Classname of the panel to register. +---@[call_arg("gmod.vgui_panel", "table")] ---@param panelTable T The table containing the panel information. ---@[call_arg("gmod.vgui_panel", "base")] ---@param baseName? string Classname of a panel to inherit functionality from. From bf9cadfecc29055d96620b913578183eef5d01d7 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 8 Jun 2026 04:24:37 +0100 Subject: [PATCH 005/117] Annotate Derma control and hook call_arg --- custom/derma.DefineControl.lua | 2 +- custom/hook.Call.lua | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/custom/derma.DefineControl.lua b/custom/derma.DefineControl.lua index 31ba5cdf..1660641a 100644 --- a/custom/derma.DefineControl.lua +++ b/custom/derma.DefineControl.lua @@ -3,7 +3,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/derma.DefineControl ---@generic T: Panel ----@[call_arg("gmod.vgui_panel", "define")] +---@[call_arg("gmod.vgui_panel", "define_control")] ---@param name string Name of the newly created control. ---@param description string Description of the control. ---@[call_arg("gmod.vgui_panel", "table")] diff --git a/custom/hook.Call.lua b/custom/hook.Call.lua index 21feb956..80a71a72 100644 --- a/custom/hook.Call.lua +++ b/custom/hook.Call.lua @@ -4,6 +4,7 @@ ---@source https://wiki.facepunch.com/gmod/hook.Call ---@[call_arg("gmod.hook", "emit")] ---@param eventName string The hook event to call. +---@[call_arg("gmod.hook", "gamemode_table")] ---@param gamemodeTable? table The gamemode table to call the hook on. ---@param ... any Arguments to pass to the hook. ---@return any From 9bb60475de352592dcafe543991f555e76e203a1 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 8 Jun 2026 05:33:32 +0100 Subject: [PATCH 006/117] Annotation NetworkVarElement call_arg --- custom/Entity.NetworkVarElement.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/Entity.NetworkVarElement.lua b/custom/Entity.NetworkVarElement.lua index 1f65e296..f4da8fd1 100644 --- a/custom/Entity.NetworkVarElement.lua +++ b/custom/Entity.NetworkVarElement.lua @@ -5,7 +5,7 @@ ---@param type string The NetworkVar type. ---@param slot number The NetworkVar slot. ---@param element number The vector or angle element. ----@[call_arg("gmod.network_var", "define")] +---@[call_arg("gmod.network_var", "define_element")] ---@param name string Name of the variable, used for generated Get/Set accessors. ---@param extended? table Extra NetworkVar information. function Entity:NetworkVarElement(type, slot, element, name, extended) end From d3f14bcac007a00f0527caafa60ad21f50a3870a Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 8 Jun 2026 05:34:15 +0100 Subject: [PATCH 007/117] Stop zip stream after missing input --- src/utils/filesystem.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/utils/filesystem.ts b/src/utils/filesystem.ts index 889b34ab..821db352 100644 --- a/src/utils/filesystem.ts +++ b/src/utils/filesystem.ts @@ -62,6 +62,13 @@ export async function zipFiles(outputFile: string, filePaths: string[], trimPath return new Promise(async (resolve, reject) => { const outputDirectory = path.dirname(outputFile); + for (const filePath of filePaths) { + if (!fs.existsSync(filePath)) { + reject(new Error(`File ${filePath} does not exist.`)); + return; + } + } + if (!fs.existsSync(outputDirectory)) fs.mkdirSync(outputDirectory, { recursive: true }); @@ -72,6 +79,10 @@ export async function zipFiles(outputFile: string, filePaths: string[], trimPath resolve(archive); }); + outputStream.on('error', function (err) { + reject(err); + }); + archive.on('error', function (err) { reject(err); }); @@ -79,9 +90,6 @@ export async function zipFiles(outputFile: string, filePaths: string[], trimPath archive.pipe(outputStream); for (const filePath of filePaths) { - if (!fs.existsSync(filePath)) - reject(new Error(`File ${filePath} does not exist.`)); - archive.file(filePath, { name: trimPath ? path.relative(trimPath, filePath) : filePath }); } From ed382e723b507087005042fa1f1be067edd44ed0 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 8 Jun 2026 16:41:04 +0100 Subject: [PATCH 008/117] Annotate hook callbacks --- custom/hook.Add.lua | 1 + custom/hook.Remove.lua | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 custom/hook.Remove.lua diff --git a/custom/hook.Add.lua b/custom/hook.Add.lua index 43d09b89..9a38735c 100644 --- a/custom/hook.Add.lua +++ b/custom/hook.Add.lua @@ -5,5 +5,6 @@ ---@[call_arg("gmod.hook", "add")] ---@param eventName string The event to hook on to. This can be any GM_Hooks hook, gameevent after using gameevent.Listen, or custom hook run with hook.Call or hook.Run. ---@param identifier any The unique identifier, usually a string. This can be used elsewhere in the code to replace or remove the hook. The identifier **should** be unique so that you do not accidentally override some other mods hook, unless that's what you are trying to do. +---@[call_arg("gmod.hook", "callback")] ---@param func function The function to be called, arguments given to it depend on the identifier used. function hook.Add(eventName, identifier, func) end diff --git a/custom/hook.Remove.lua b/custom/hook.Remove.lua new file mode 100644 index 00000000..c888d0fb --- /dev/null +++ b/custom/hook.Remove.lua @@ -0,0 +1,8 @@ +---Removes a hook registered with hook.Add. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/hook.Remove +---@[call_arg("gmod.hook", "remove")] +---@param eventName string The hook event name to remove from. +---@param identifier any The unique identifier previously used with hook.Add. +function hook.Remove(eventName, identifier) end From d4db618a349cd938aff5f467a312b6f1211c7c51 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 8 Jun 2026 18:54:12 +0100 Subject: [PATCH 009/117] Update CI --- .github/workflows/release-gluals.yml | 281 ++++++++++++++++++++------- .github/workflows/release-test.yml | 72 ------- 2 files changed, 214 insertions(+), 139 deletions(-) delete mode 100644 .github/workflows/release-test.yml diff --git a/.github/workflows/release-gluals.yml b/.github/workflows/release-gluals.yml index e5bbabe6..689d87cc 100644 --- a/.github/workflows/release-gluals.yml +++ b/.github/workflows/release-gluals.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "22" - - name: Decide what to publish + - name: Decide what to generate id: gate env: EVENT: ${{ github.event_name }} @@ -28,20 +28,21 @@ jobs: run: | set -e - # schedule + manual dispatch always publish everything (wiki content may have changed) + # Scheduled and manual runs need generation because upstream wiki content can change + # without any repository diff. Publishing is still gated later by output comparison. if [ "$EVENT" != "push" ]; then - echo "Reason: event=$EVENT -> publish all" - echo "should_run=true" >> "$GITHUB_OUTPUT" + echo "Reason: event=$EVENT -> generate and compare output" + echo "should_generate=true" >> "$GITHUB_OUTPUT" echo "publish_base=true" >> "$GITHUB_OUTPUT" echo "publish_all_plugins=true" >> "$GITHUB_OUTPUT" exit 0 fi - # push event -> diff against parent. Fail open (publish everything) if we can't. + # Push event -> diff against parent. Fail open (generate everything) if we can't. if [ -z "$BEFORE_SHA" ] || [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ] \ || ! git cat-file -e "$BEFORE_SHA" 2>/dev/null; then - echo "No usable base SHA (got '$BEFORE_SHA') -> publish all to be safe" - echo "should_run=true" >> "$GITHUB_OUTPUT" + echo "No usable base SHA (got '$BEFORE_SHA') -> generate all to be safe" + echo "should_generate=true" >> "$GITHUB_OUTPUT" echo "publish_base=true" >> "$GITHUB_OUTPUT" echo "publish_all_plugins=true" >> "$GITHUB_OUTPUT" exit 0 @@ -88,45 +89,36 @@ jobs: PLUGINS_TO_PUBLISH="$CHANGED_PLUGIN_IDS" fi - SHOULD_RUN=false + SHOULD_GENERATE=false if $PUBLISH_BASE || $PUBLISH_ALL_PLUGINS || [ -n "$PLUGINS_TO_PUBLISH" ]; then - SHOULD_RUN=true + SHOULD_GENERATE=true fi - echo "should_run=$SHOULD_RUN" >> "$GITHUB_OUTPUT" + echo "should_generate=$SHOULD_GENERATE" >> "$GITHUB_OUTPUT" echo "publish_base=$PUBLISH_BASE" >> "$GITHUB_OUTPUT" echo "publish_all_plugins=$PUBLISH_ALL_PLUGINS" >> "$GITHUB_OUTPUT" echo "plugins_to_publish=$PLUGINS_TO_PUBLISH" >> "$GITHUB_OUTPUT" { - echo "## release-gluals gate" + echo "## release-gluals generation gate" echo "- event: \`$EVENT\`" - echo "- should_run: \`$SHOULD_RUN\`" + echo "- should_generate: \`$SHOULD_GENERATE\`" echo "- publish_base: \`$PUBLISH_BASE\`" echo "- publish_all_plugins: \`$PUBLISH_ALL_PLUGINS\`" echo "- plugins_to_publish: \`${PLUGINS_TO_PUBLISH:-}\`" } >> "$GITHUB_STEP_SUMMARY" - name: Install dependencies - if: steps.gate.outputs.should_run == 'true' + if: steps.gate.outputs.should_generate == 'true' run: npm ci - name: Set build timestamp id: build_ts - if: steps.gate.outputs.should_run == 'true' + if: steps.gate.outputs.should_generate == 'true' run: echo "value=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_OUTPUT" - name: Scrape wiki - if: steps.gate.outputs.should_run == 'true' + if: steps.gate.outputs.should_generate == 'true' run: npm run scrape-wiki - - name: Stamp metadata with build timestamp - if: steps.gate.outputs.should_run == 'true' - run: | - node -e " - const fs = require('fs'); - const meta = JSON.parse(fs.readFileSync('output/__metadata.json', 'utf8')); - meta.lastUpdate = '${{ steps.build_ts.outputs.value }}'; - fs.writeFileSync('output/__metadata.json', JSON.stringify(meta, null, 2)); - " - name: Regenerate Lua + plugin artifacts metadata - if: steps.gate.outputs.should_run == 'true' + if: steps.gate.outputs.should_generate == 'true' run: | npm run generate-lua -- \ --output ./output \ @@ -142,41 +134,28 @@ jobs: --artifactManifest plugin.json \ --version "${{ steps.build_ts.outputs.value }}" \ --generatedAt "${{ steps.build_ts.outputs.value }}" - - name: Run tests - if: steps.gate.outputs.should_run == 'true' - run: npm test - name: Format the output with StyLua - if: steps.gate.outputs.should_run == 'true' + if: steps.gate.outputs.should_generate == 'true' uses: JohnnyMorganz/stylua-action@v2.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} version: latest args: --no-editorconfig output/ - - name: Tag latest scrape revision - if: steps.gate.outputs.should_run == 'true' - run: | - build_tag=$(echo "${{ steps.build_ts.outputs.value }}" | sed 's/:/-/g' | sed 's/T/_/' | sed 's/Z//') - tag="$build_tag" - if git rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then - echo "Tag $tag already exists. Falling back to build run number." - tag="${build_tag}-${GITHUB_RUN_NUMBER}" - fi - git tag "$tag" - git push origin "refs/tags/$tag" - - name: Publish annotations to branch - if: steps.gate.outputs.should_run == 'true' + - name: Prepare release payloads + id: payloads + if: steps.gate.outputs.should_generate == 'true' env: PUBLISH_BASE: ${{ steps.gate.outputs.publish_base }} PUBLISH_ALL_PLUGINS: ${{ steps.gate.outputs.publish_all_plugins }} PLUGINS_TO_PUBLISH: ${{ steps.gate.outputs.plugins_to_publish }} run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" + set -e tmpdir=$(mktemp -d) base_dir="$tmpdir/base" plugin_stage_dir="$tmpdir/plugins" - mkdir -p "$base_dir/plugin" "$plugin_stage_dir" + compare_dir="$tmpdir/compare" + mkdir -p "$base_dir/plugin" "$plugin_stage_dir" "$compare_dir" # Stage base annotation branch payload (core lua + metadata + plugin index only). find output/ -maxdepth 1 -type f -name '*.lua' -exec cp {} "$base_dir/" \; @@ -186,6 +165,188 @@ jobs: cp -R output-plugins/. "$plugin_stage_dir/" fi + normalize_payload() { + local source_dir="$1" + local target_dir="$2" + + mkdir -p "$target_dir" + cp -R "$source_dir/." "$target_dir/" + + node - "$target_dir" <<'NODE' + const fs = require('fs'); + const path = require('path'); + + const root = process.argv[2]; + const rewriteJson = (relativePath, rewrite) => { + const filePath = path.join(root, relativePath); + if (!fs.existsSync(filePath)) return; + const json = JSON.parse(fs.readFileSync(filePath, 'utf8')); + rewrite(json); + fs.writeFileSync(filePath, `${JSON.stringify(json, null, 2)}\n`, 'utf8'); + }; + + rewriteJson('__metadata.json', (json) => { + json.lastUpdate = '__normalized__'; + }); + + rewriteJson(path.join('plugin', 'index.json'), (json) => { + delete json.generatedAt; + if (Array.isArray(json.plugins)) { + for (const plugin of json.plugins) { + if (plugin.artifact) delete plugin.artifact.version; + } + } + }); + NODE + } + + branch_has_changes() { + local branch_name="$1" + local source_dir="$2" + local existing_dir="$compare_dir/existing-$branch_name" + local normalized_existing="$compare_dir/existing-normalized-$branch_name" + local normalized_source="$compare_dir/source-normalized-$branch_name" + + rm -rf "$existing_dir" "$normalized_existing" "$normalized_source" + mkdir -p "$existing_dir" + + set +e + git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1 + remote_status=$? + set -e + + if [ "$remote_status" -eq 2 ]; then + echo "Branch '$branch_name' does not exist yet." + return 0 + fi + + if [ "$remote_status" -ne 0 ]; then + echo "Unable to check remote branch '$branch_name'." + exit "$remote_status" + fi + + git fetch --depth=1 origin "refs/heads/$branch_name" >/dev/null + git archive "FETCH_HEAD" | tar -x -C "$existing_dir" + + normalize_payload "$existing_dir" "$normalized_existing" + normalize_payload "$source_dir" "$normalized_source" + + if diff -qr "$normalized_existing" "$normalized_source" >/dev/null; then + return 1 + fi + + return 0 + } + + should_publish_plugin() { + local plugin_id="$1" + + if [ "$PUBLISH_ALL_PLUGINS" = "true" ]; then + return 0 + fi + + for pid in $PLUGINS_TO_PUBLISH; do + if [ "$pid" = "$plugin_id" ]; then + return 0 + fi + done + + return 1 + } + + changed_base=false + changed_plugins="" + + if [ "$PUBLISH_BASE" = "true" ]; then + if branch_has_changes "gluals-annotations" "$base_dir"; then + changed_base=true + else + echo "Base annotations payload matches gluals-annotations." + fi + else + echo "Skipping base annotations comparison (no relevant source changes)." + fi + + for plugin_dir in "$plugin_stage_dir"/*; do + [ -d "$plugin_dir" ] || continue + plugin_id="$(basename "$plugin_dir")" + + if ! should_publish_plugin "$plugin_id"; then + echo "Skipping plugin '$plugin_id' comparison (no related source changes)." + continue + fi + + plugin_branch="gluals-annotations-plugin-${plugin_id}" + if branch_has_changes "$plugin_branch" "$plugin_dir"; then + changed_plugins="$changed_plugins $plugin_id" + else + echo "Plugin '$plugin_id' payload matches $plugin_branch." + fi + done + + changed_plugins=$(echo "$changed_plugins" | xargs || true) + if [ -n "$changed_plugins" ] && [ "$PUBLISH_BASE" = "true" ] && [ "$changed_base" != "true" ]; then + echo "Plugin payload changed; publishing base index so artifact versions stay current." + changed_base=true + fi + + should_publish=false + if [ "$changed_base" = "true" ] || [ -n "$changed_plugins" ]; then + should_publish=true + fi + + echo "tmpdir=$tmpdir" >> "$GITHUB_OUTPUT" + echo "base_dir=$base_dir" >> "$GITHUB_OUTPUT" + echo "plugin_stage_dir=$plugin_stage_dir" >> "$GITHUB_OUTPUT" + echo "should_publish=$should_publish" >> "$GITHUB_OUTPUT" + echo "changed_base=$changed_base" >> "$GITHUB_OUTPUT" + echo "changed_plugins=$changed_plugins" >> "$GITHUB_OUTPUT" + + { + echo "## release-gluals output comparison" + echo "- should_publish: \`$should_publish\`" + echo "- changed_base: \`$changed_base\`" + echo "- changed_plugins: \`${changed_plugins:-}\`" + } >> "$GITHUB_STEP_SUMMARY" + - name: Stop when generated output is unchanged + if: steps.gate.outputs.should_generate == 'true' && steps.payloads.outputs.should_publish != 'true' + run: echo "Generated output matches published branches. Nothing to release." + - name: Run tests + if: steps.payloads.outputs.should_publish == 'true' + run: npm test + - name: Stamp metadata with build timestamp + if: steps.payloads.outputs.should_publish == 'true' + run: | + node -e " + const fs = require('fs'); + const meta = JSON.parse(fs.readFileSync('${{ steps.payloads.outputs.base_dir }}/__metadata.json', 'utf8')); + meta.lastUpdate = '${{ steps.build_ts.outputs.value }}'; + fs.writeFileSync('${{ steps.payloads.outputs.base_dir }}/__metadata.json', JSON.stringify(meta, null, 2) + '\n'); + " + - name: Tag latest scrape revision + if: steps.payloads.outputs.should_publish == 'true' + run: | + build_tag=$(echo "${{ steps.build_ts.outputs.value }}" | sed 's/:/-/g' | sed 's/T/_/' | sed 's/Z//') + tag="$build_tag" + if git rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then + echo "Tag $tag already exists. Falling back to build run number." + tag="${build_tag}-${GITHUB_RUN_NUMBER}" + fi + git tag "$tag" + git push origin "refs/tags/$tag" + - name: Publish annotations to branch + if: steps.payloads.outputs.should_publish == 'true' + env: + BASE_DIR: ${{ steps.payloads.outputs.base_dir }} + PLUGIN_STAGE_DIR: ${{ steps.payloads.outputs.plugin_stage_dir }} + CHANGED_BASE: ${{ steps.payloads.outputs.changed_base }} + CHANGED_PLUGINS: ${{ steps.payloads.outputs.changed_plugins }} + run: | + set -e + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + publish_branch() { local branch_name="$1" local source_dir="$2" @@ -203,31 +364,17 @@ jobs: now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" - if [ "$PUBLISH_BASE" = "true" ]; then - publish_branch "gluals-annotations" "$base_dir" "Update GLuaLS annotations - $now" + if [ "$CHANGED_BASE" = "true" ]; then + publish_branch "gluals-annotations" "$BASE_DIR" "Update GLuaLS annotations - $now" else - echo "Skipping base annotations branch (no relevant changes)." + echo "Skipping base annotations branch (generated output unchanged)." fi - for plugin_dir in "$plugin_stage_dir"/*; do - [ -d "$plugin_dir" ] || continue - plugin_id="$(basename "$plugin_dir")" - - should_publish=false - if [ "$PUBLISH_ALL_PLUGINS" = "true" ]; then - should_publish=true - else - for pid in $PLUGINS_TO_PUBLISH; do - if [ "$pid" = "$plugin_id" ]; then - should_publish=true - break - fi - done - fi - - if [ "$should_publish" != "true" ]; then - echo "Skipping plugin '$plugin_id' (no related changes)." - continue + for plugin_id in $CHANGED_PLUGINS; do + plugin_dir="$PLUGIN_STAGE_DIR/$plugin_id" + if [ ! -d "$plugin_dir" ]; then + echo "Expected plugin payload directory missing: $plugin_dir" + exit 1 fi plugin_branch="gluals-annotations-plugin-${plugin_id}" diff --git a/.github/workflows/release-test.yml b/.github/workflows/release-test.yml deleted file mode 100644 index ee35c7e5..00000000 --- a/.github/workflows/release-test.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: release-test -on: - workflow_dispatch: - push: - branches: - #- main - - test-plugin - # Only trigger on changes that actually impact our final output - paths: - - 'src/**' - - 'custom/**' - - '__tests__/**' - - 'package.json' - - 'package-lock.json' - - 'yarn.lock' - - 'jest.config.ts' - - 'tsconfig.json' - -jobs: - release: - permissions: write-all - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - uses: actions/setup-node@v4 - with: - node-version: "22" - - - name: Install dependencies - run: npm ci - - - name: Scrape wiki - run: npm run scrape-wiki - - name: Run tests - run: npm test - - name: Format the output with StyLua - uses: JohnnyMorganz/stylua-action@v2.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - version: latest - args: --no-editorconfig output/ - - name: Publish annotations to test branch - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - # Stash only the final .lua files + metadata to a temp dir before switching branches. - # This is necessary because `git rm -rf .` only removes tracked files; output/ is - # gitignored (untracked) so it survives, causing duplicates when git add -A picks it up. - tmpdir=$(mktemp -d) - find output/ -maxdepth 1 -type f -name '*.lua' -exec cp {} "$tmpdir/" \; - cp output/__metadata.json "$tmpdir/" - - # Create orphan branch, then wipe EVERYTHING (tracked + untracked + ignored) - branchName="gluals-annotations-test" - git checkout --orphan "$branchName" - git rm -rf . - git clean -fdx - - # Restore only the final annotation files - cp "$tmpdir/"*.lua . - cp "$tmpdir/__metadata.json" . - git add -A - - # Commit with timestamp and branch reference - commitMsg="Update GLuaLS annotations (test) - $(date -u +%Y-%m-%dT%H:%M:%SZ) - from ${{ github.ref_name }}" - git commit -m "$commitMsg" - - # Force push to publish - git push -f origin "$branchName" From 548ea4dd264f56a6fbfb284172c0d9e9781760c0 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:49:00 +0100 Subject: [PATCH 010/117] Restore override and support call_arg on override --- custom/Entity.NetworkVar.lua | 3 +++ custom/Entity.NetworkVarElement.lua | 5 ++++- custom/Panel.Add.lua | 7 ++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/custom/Entity.NetworkVar.lua b/custom/Entity.NetworkVar.lua index d64f9200..cc386a21 100644 --- a/custom/Entity.NetworkVar.lua +++ b/custom/Entity.NetworkVar.lua @@ -1,6 +1,9 @@ ---Creates a network variable and generated Get/Set accessors for the entity. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:NetworkVar +---@[overload_call_arg(0, "gmod.network_var", "type")] +---@[overload_call_arg(1, "gmod.network_var", "define")] +---@overload fun(type: string, name: string, extended?: table) ---@[call_arg("gmod.network_var", "type")] ---@param type string The NetworkVar type. ---@param slot number The NetworkVar slot. diff --git a/custom/Entity.NetworkVarElement.lua b/custom/Entity.NetworkVarElement.lua index f4da8fd1..5ec1707e 100644 --- a/custom/Entity.NetworkVarElement.lua +++ b/custom/Entity.NetworkVarElement.lua @@ -1,10 +1,13 @@ ---Creates Get/Set accessors for a vector or angle element NetworkVar. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:NetworkVarElement +---@[overload_call_arg(0, "gmod.network_var", "type")] +---@[overload_call_arg(2, "gmod.network_var", "define_element")] +---@overload fun(type: string, element: string, name: string, extended?: table) ---@[call_arg("gmod.network_var", "type")] ---@param type string The NetworkVar type. ---@param slot number The NetworkVar slot. ----@param element number The vector or angle element. +---@param element string The vector or angle element. ---@[call_arg("gmod.network_var", "define_element")] ---@param name string Name of the variable, used for generated Get/Set accessors. ---@param extended? table Extra NetworkVar information. diff --git a/custom/Panel.Add.lua b/custom/Panel.Add.lua index d178c229..d6ecac34 100644 --- a/custom/Panel.Add.lua +++ b/custom/Panel.Add.lua @@ -3,8 +3,9 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/Panel:Add ---@generic T : Panel +---@overload fun(self: Panel, panel: Panel): Panel # Parents an existing panel to this panel. ---@overload fun(self: Panel, panelTable: table): Panel # Creates a panel from a PANEL table and parents it to this panel. ---@[call_arg("gmod.vgui_panel", "reference")] ----@param object `T`|T The panel to add, or a panel class name to create and add. ----@return (instance) T # The added or created panel -function Panel:Add(object) end +---@param className `T` The panel class name to create and add. +---@return (instance) T # The created panel. +function Panel:Add(className) end From 47b46579383295f1cde3e2a3fc50d56620842c12 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Wed, 10 Jun 2026 06:11:14 +0100 Subject: [PATCH 011/117] Fix annotation generation not including custom by default --- __tests__/cli-generate-lua.spec.ts | 130 ++++++++++++++++++++++++++++- package.json | 2 +- src/cli-generate-lua.ts | 9 +- 3 files changed, 134 insertions(+), 7 deletions(-) diff --git a/__tests__/cli-generate-lua.spec.ts b/__tests__/cli-generate-lua.spec.ts index 9a87f689..c3501b3a 100644 --- a/__tests__/cli-generate-lua.spec.ts +++ b/__tests__/cli-generate-lua.spec.ts @@ -19,7 +19,7 @@ describe('cli-generate-lua', () => { try { const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const result = spawnSync( - `${command} run generate-lua -- --output "${outputPath}" --customOverrides ./custom`, + `${command} run generate-lua -- --output "${outputPath}" --custom-overrides ./custom`, [], { cwd: process.cwd(), @@ -69,7 +69,7 @@ describe('cli-generate-lua', () => { try { const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; const result = spawnSync( - `${command} run generate-lua -- --output "${outputPath}" --customOverrides ./custom`, + `${command} run generate-lua -- --output "${outputPath}" --custom-overrides ./custom`, [], { cwd: process.cwd(), @@ -94,4 +94,130 @@ describe('cli-generate-lua', () => { fs.rmSync(tmpRoot, { recursive: true, force: true }); } }); + + test('applies custom overrides by default when --custom-overrides is not specified', () => { + const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-defaults-')); + const outputPath = path.join(tmpRoot, 'output'); + const vectorDir = path.join(outputPath, 'vector'); + + fs.mkdirSync(vectorDir, { recursive: true }); + fs.writeFileSync( + path.join(vectorDir, 'pages.json'), + JSON.stringify([ + { + type: 'class', + address: 'Vector', + name: 'Vector', + description: 'A 3D vector.', + realm: 'shared', + url: 'https://wiki.facepunch.com/gmod/Vector', + parent: '', + }, + ], null, 2), + 'utf8', + ); + + try { + const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const result = spawnSync( + `${command} run generate-lua -- --output "${outputPath}"`, + [], + { + cwd: process.cwd(), + encoding: 'utf8', + shell: true, + }, + ); + + expect(result.status).toBe(0); + const vectorLua = fs.readFileSync(path.join(outputPath, 'vector.lua'), 'utf8'); + // These lines come from custom/class.Vector.lua overrides + expect(vectorLua).toContain('---@field x number'); + expect(vectorLua).toContain('---@field y number'); + expect(vectorLua).toContain('---@field z number'); + expect(vectorLua).toContain('---@operator add(Vector): Vector'); + } finally { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + } + }); + + test('--raw-wiki skips applying custom overrides', () => { + const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-rawwiki-')); + const outputPath = path.join(tmpRoot, 'output'); + const vectorDir = path.join(outputPath, 'vector'); + + fs.mkdirSync(vectorDir, { recursive: true }); + fs.writeFileSync( + path.join(vectorDir, 'pages.json'), + JSON.stringify([ + { + type: 'class', + address: 'Vector', + name: 'Vector', + description: 'A 3D vector.', + realm: 'shared', + url: 'https://wiki.facepunch.com/gmod/Vector', + parent: '', + }, + ], null, 2), + 'utf8', + ); + + try { + const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const result = spawnSync( + `${command} run generate-lua -- --output "${outputPath}" --raw-wiki`, + [], + { + cwd: process.cwd(), + encoding: 'utf8', + shell: true, + }, + ); + + expect(result.status).toBe(0); + const vectorLua = fs.readFileSync(path.join(outputPath, 'vector.lua'), 'utf8'); + // With --raw-wiki, custom overrides are skipped so @field/@operator from custom/class.Vector.lua should NOT appear + expect(vectorLua).not.toContain('---@field x number'); + expect(vectorLua).not.toContain('---@operator add(Vector): Vector'); + } finally { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + } + }); + + test('--no-wipe-lua preserves existing Lua files', () => { + const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-nowipe-')); + const outputPath = path.join(tmpRoot, 'output'); + const vectorDir = path.join(outputPath, 'vector'); + + fs.mkdirSync(vectorDir, { recursive: true }); + fs.writeFileSync( + path.join(vectorDir, 'pages.json'), + JSON.stringify([], null, 2), + 'utf8', + ); + + // Write a sentinel file that should survive when wipe is disabled + const sentinelFile = path.join(outputPath, 'sentinel.lua'); + fs.writeFileSync(sentinelFile, '-- sentinel', 'utf8'); + + try { + const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const result = spawnSync( + `${command} run generate-lua -- --output "${outputPath}" --no-wipe-lua --raw-wiki`, + [], + { + cwd: process.cwd(), + encoding: 'utf8', + shell: true, + }, + ); + + expect(result.status).toBe(0); + expect(fs.existsSync(sentinelFile)).toBe(true); + expect(fs.readFileSync(sentinelFile, 'utf8')).toBe('-- sentinel'); + } finally { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + } + }); }); diff --git a/package.json b/package.json index e11cc866..b14eb379 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "wiki-check-changed": "tsx ./src/cli-change-checker.ts", "scrape-wiki": "tsx ./src/cli-scraper.ts --output ./output/ --customOverrides ./custom/ --wipe && npm run generate-all", "generate-lua": "tsx ./src/cli-generate-lua.ts", - "generate-all": "npm run generate-lua -- --output ./output --customOverrides ./custom --wipeLua && npm run generate-plugin-index && npm run generate-plugin-artifacts", + "generate-all": "npm run generate-lua -- --output ./output --custom-overrides ./custom && npm run generate-plugin-index && npm run generate-plugin-artifacts", "generate-plugin-index": "tsx ./src/cli-generate-plugin-index.ts --pluginRoot ./plugin --output ./plugin/index.json", "generate-plugin-artifacts": "tsx ./src/cli-generate-plugin-artifacts.ts --pluginRoot ./plugin --indexOutput ./plugin/index.json --annotationsOutput ./output --pluginBundlesOutput ./output-plugins", "pack-release": "tsx ./src/cli-release-packer.ts --input ./output/ --output ./dist/release/", diff --git a/src/cli-generate-lua.ts b/src/cli-generate-lua.ts index 2eb4eed6..a61a78b4 100644 --- a/src/cli-generate-lua.ts +++ b/src/cli-generate-lua.ts @@ -53,13 +53,14 @@ async function main() { program .description('Regenerate Lua annotations from existing JSON pages (no wiki scrape)') .option('-o, --output ', 'Output directory containing wiki JSON and Lua files', './output') - .option('-c, --customOverrides [path]', 'Custom override directory') - .option('--wipeLua', 'Delete existing top-level Lua files before regenerating', true) + .option('-c, --custom-overrides ', 'Custom override directory', './custom') + .option('--no-wipe-lua', 'Skip deleting existing top-level Lua files before regenerating') + .option('--raw-wiki', 'Skip applying custom overrides (use raw wiki data only)') .parse(process.argv); const options = program.opts(); const outputDirectory = options.output.replace(/\/$/, ''); - const customDirectory = options.customOverrides?.replace(/\/$/, ''); + const customDirectory = options.customOverrides.replace(/\/$/, ''); if (!fs.existsSync(outputDirectory)) { throw new Error(`Output directory does not exist: ${outputDirectory}`); @@ -71,7 +72,7 @@ async function main() { wipeLuaFiles(outputDirectory); } - if (customDirectory) { + if (!options.rawWiki) { if (!fs.existsSync(customDirectory)) { throw new Error(`Custom overrides directory does not exist: ${customDirectory}`); } From d8e0b9b189ccd007ac4d00585f1715703f594e38 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 11 Jun 2026 06:21:42 +0100 Subject: [PATCH 012/117] Fix debug.getmetatable annotation --- __tests__/cli-generate-lua.spec.ts | 70 ++++++++++++++++++++++++++++++ custom/debug.getmetatable.lua | 9 ++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/__tests__/cli-generate-lua.spec.ts b/__tests__/cli-generate-lua.spec.ts index c3501b3a..6bb289c6 100644 --- a/__tests__/cli-generate-lua.spec.ts +++ b/__tests__/cli-generate-lua.spec.ts @@ -36,6 +36,76 @@ describe('cli-generate-lua', () => { } }); + test('uses runtime-generic debug.getmetatable annotation override', () => { + const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-debug-getmetatable-')); + const outputPath = path.join(tmpRoot, 'output'); + const customOverridesPath = path.join(tmpRoot, 'custom'); + const debugDir = path.join(outputPath, 'debug'); + + fs.mkdirSync(debugDir, { recursive: true }); + fs.mkdirSync(customOverridesPath, { recursive: true }); + + fs.copyFileSync( + path.join(process.cwd(), 'custom', 'debug.getmetatable.lua'), + path.join(customOverridesPath, 'debug.getmetatable.lua'), + ); + + const pagesPath = path.join(debugDir, 'getmetatable.json'); + fs.writeFileSync( + pagesPath, + JSON.stringify([ + { + type: 'libraryfunc', + parent: 'debug', + name: 'getmetatable', + address: 'debug.getmetatable', + description: 'Returns the metatable of the specified value.', + realm: 'shared', + url: 'https://wiki.facepunch.com/gmod/debug.getmetatable', + arguments: [ + { + args: [ + { + name: 'object', + type: 'any', + }, + ], + }, + ], + returns: [ + { + type: 'any', + }, + ], + }, + ], null, 2), + 'utf8', + ); + + try { + const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const result = spawnSync( + `${command} run generate-lua -- --output "${outputPath}" --custom-overrides "${customOverridesPath}"`, + [], + { + cwd: process.cwd(), + encoding: 'utf8', + shell: true, + }, + ); + + expect(result.status).toBe(0); + const debugLua = fs.readFileSync(path.join(outputPath, 'debug.lua'), 'utf8'); + expect(debugLua).toContain('---@generic T'); + expect(debugLua).toContain('---@param object T The value to get the metatable of.'); + expect(debugLua).toContain('---@return (definition) T # The metatable of the value.'); + expect(debugLua).not.toContain('`T`'); + expect(debugLua).not.toContain('---@generic T : table'); + } finally { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + } + }); + test('applies typed Entity networked getter overrides', () => { const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-')); const outputPath = path.join(tmpRoot, 'output'); diff --git a/custom/debug.getmetatable.lua b/custom/debug.getmetatable.lua index f10834ef..aefd2b30 100644 --- a/custom/debug.getmetatable.lua +++ b/custom/debug.getmetatable.lua @@ -1,8 +1,9 @@ ----Returns the metatable of the specified value. Can return any value. +---Returns the metatable of an object. This function ignores the metatable's __metatable field. +---@deprecated ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/debug.getmetatable ----@generic T : table ----@param object `T` The value to get the metatable of. ----@return (definition) `T` # The metatable of the value. +---@generic T +---@param object T The value to get the metatable of. +---@return (definition) T # The metatable of the value. function debug.getmetatable(object) end From 3eb045bd1145a08b962247728d20e931876544a9 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 01:09:00 +0100 Subject: [PATCH 013/117] Add error annotation --- custom/Global.error(lowercase).lua | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 custom/Global.error(lowercase).lua diff --git a/custom/Global.error(lowercase).lua b/custom/Global.error(lowercase).lua new file mode 100644 index 00000000..64d54b08 --- /dev/null +++ b/custom/Global.error(lowercase).lua @@ -0,0 +1,8 @@ +---Throws a Lua error and breaks out of the current call stack. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.error(lowercase) +---@param message string # The error message to throw. +---@param errorLevel? number # The level to throw the error at. +---@return never +function _G.error(message, errorLevel) end From d7a0cf5aa8ade612805178abdadb8ffc0e09ef0c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:03:17 +0100 Subject: [PATCH 014/117] Add IsHostingGame menu annotation --- custom/Global.IsHostingGame.lua | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 custom/Global.IsHostingGame.lua diff --git a/custom/Global.IsHostingGame.lua b/custom/Global.IsHostingGame.lua new file mode 100644 index 00000000..16decdb6 --- /dev/null +++ b/custom/Global.IsHostingGame.lua @@ -0,0 +1,4 @@ +---Returns true when the current menu session is hosting a local game. +---@realm menu +---@return boolean #True if the local client is hosting the active game session. +function _G.IsHostingGame() end From 16e86a9f8a69073dcc328e56af338137102d2098 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:18:09 +0100 Subject: [PATCH 015/117] Fix PropertyAdd optional fields --- custom/PropertyAdd.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 custom/PropertyAdd.lua diff --git a/custom/PropertyAdd.lua b/custom/PropertyAdd.lua new file mode 100644 index 00000000..ebe48fa0 --- /dev/null +++ b/custom/PropertyAdd.lua @@ -0,0 +1,16 @@ +---Structure used for [properties.Add](https://wiki.facepunch.com/gmod/properties.Add). +---@realm shared +---@source https://wiki.facepunch.com/gmod/Structures/PropertyAdd +---@class (partial) PropertyAdd +---@field Type? string|"simple"|"toggle" Can be set to "toggle" to make this property a toggle property. +---@field MenuLabel string Label to show on opened menu. +---@field MenuIcon? string Icon to show on opened menu for this item. Optional for simple properties and unused for toggle properties. +---@field Order number Where in the list this property should be positioned, relative to other properties. +---@field PrependSpacer? boolean Whether to add a spacer before this property. +---@field Filter fun(self: table, ent: Entity, player: Player):(check: boolean) Used clientside to decide whether this property should be shown for an entity. +---@field Checked? fun(self: table, ent: Entity, tr: table):(check: boolean) Required only for toggle properties. +---@field Action fun(self: table, ent: Entity, tr: table) Called clientside when the property is clicked. +---@field Receive? fun(self: table, len: number, ply: Player) Called serverside if the client sends a message in the Action function. +---@field MenuOpen? fun(self: table, option: DMenuOption, ent: Entity, tr: table) Called clientside when the property option has been created in the right-click menu. +---@field OnCreate? fun(self: table, menu: DMenu, option: DMenuOption) Called clientside after the property option has been created. +local PropertyAdd = {} From 5c5e83c2f757eee878f46a027bcd2a40f643da60 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:22:44 +0100 Subject: [PATCH 016/117] Fix scripted entity registration table type --- custom/scripted_ents.Register.lua | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 custom/scripted_ents.Register.lua diff --git a/custom/scripted_ents.Register.lua b/custom/scripted_ents.Register.lua new file mode 100644 index 00000000..4951dcab --- /dev/null +++ b/custom/scripted_ents.Register.lua @@ -0,0 +1,8 @@ +---Registers an ENT table with a classname. Reregistering an existing classname will automatically update the functions of all existing entities of that class. +--- +---The input is a registration table. Garry's Mod fills and inherits fields such as `ClassName`, `BaseClass`, and base-provided `Type` later during scripted entity registration and lookup. +---@realm shared +---@source https://wiki.facepunch.com/gmod/scripted_ents.Register +---@param ENT table The ENT table to register. +---@param classname string The classname to register. +function scripted_ents.Register(ENT, classname) end From 39cad47d8b4f5159c791437034176d1b4b72a5e8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:27:48 +0100 Subject: [PATCH 017/117] Fix VideoData optional lockfps --- custom/VideoData.lua | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 custom/VideoData.lua diff --git a/custom/VideoData.lua b/custom/VideoData.lua new file mode 100644 index 00000000..00fe4b02 --- /dev/null +++ b/custom/VideoData.lua @@ -0,0 +1,16 @@ +---Table structure used by [video.Record](https://wiki.facepunch.com/gmod/video.Record). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Structures/VideoData +---@class (partial) VideoData +---@field container string The video container format. +---@field video string The video codec. +---@field audio string The audio codec. +---@field quality number The video quality. +---@field bitrate number The record bitrate. +---@field fps number Frames per second. +---@field lockfps? boolean Lock the frame count per second. +---@field name string The file name for the video. +---@field width number The video's width. +---@field height number The video's height. +local VideoData = {} From dbcb8c68366c22c76d681cfe561c86018f15bd2e Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 03:42:31 +0100 Subject: [PATCH 018/117] Add vgui.RegisterTable call metadata --- custom/vgui.RegisterTable.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 custom/vgui.RegisterTable.lua diff --git a/custom/vgui.RegisterTable.lua b/custom/vgui.RegisterTable.lua new file mode 100644 index 00000000..a053cc24 --- /dev/null +++ b/custom/vgui.RegisterTable.lua @@ -0,0 +1,13 @@ +---Registers a table to use as a panel, to be used with [vgui.CreateFromTable](https://wiki.facepunch.com/gmod/vgui.CreateFromTable). +--- +--- All this function does is assigns Base key to your table and returns the table. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/vgui.RegisterTable +---@generic T: table +---@[call_arg("gmod.vgui_panel", "register_table")] +---@param panel T The PANEL table. +---@[call_arg("gmod.vgui_panel", "base")] +---@param base? string A base for the panel. +---@return T # The PANEL table +function vgui.RegisterTable(panel, base) end From 4417a3731c145a1b14cdf1bde099ff3bc85a3235 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 04:18:39 +0100 Subject: [PATCH 019/117] Add vgui.CreateFromTable call metadata --- custom/vgui.CreateFromTable.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/custom/vgui.CreateFromTable.lua b/custom/vgui.CreateFromTable.lua index 1ff486ef..ecfa4005 100644 --- a/custom/vgui.CreateFromTable.lua +++ b/custom/vgui.CreateFromTable.lua @@ -2,7 +2,10 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.CreateFromTable ----@param metatable table Your PANEL table. +---@generic T: table +---@[call_arg("gmod.vgui_panel", "register_table")] +---@[call_arg_field("gmod.vgui_panel", "base", "Base")] +---@param metatable T Your PANEL table. ---@param parent? Panel Which panel to parent the newly created panel to. ---@param name? string Custom name of the created panel for scripting/debugging purposes. Can be retrieved with Panel:GetName. ---@return (instance) Panel # The created panel, or `nil` if creation failed for whatever reason. From 2039de61a1b3c99cf3e128db500f23e8c817fdad Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 08:31:45 +0100 Subject: [PATCH 020/117] Fix material proxy callback annotations --- __tests__/custom-annotations.spec.ts | 6 ++++++ custom/IMaterial.SetTexture.lua | 9 +++++++++ custom/MatProxyData.lua | 21 +++++++++++++++++++++ 3 files changed, 36 insertions(+) create mode 100644 custom/IMaterial.SetTexture.lua create mode 100644 custom/MatProxyData.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index c46671f3..d09c1039 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -44,6 +44,8 @@ describe('custom and plugin annotation smoke checks', () => { const propRagdoll = fs.readFileSync(path.join(customRoot, 'class.prop_ragdoll.lua'), 'utf8'); const propDynamicOverride = fs.readFileSync(path.join(customRoot, 'class.prop_dynamic_override.lua'), 'utf8'); const envFire = fs.readFileSync(path.join(customRoot, 'class.env_fire.lua'), 'utf8'); + const matProxyData = fs.readFileSync(path.join(customRoot, 'MatProxyData.lua'), 'utf8'); + const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); expect(globals).toMatch(/---@alias GPlayer Player/); expect(globals).toMatch(/---@class NULL : Entity/); @@ -104,6 +106,10 @@ describe('custom and plugin annotation smoke checks', () => { expect(propRagdoll).toMatch(/---@class prop_ragdoll : Entity/); expect(propDynamicOverride).toMatch(/---@class prop_dynamic_override : Entity/); expect(envFire).toMatch(/---@class env_fire : Entity/); + + expect(matProxyData).toMatch(/---@field init\? fun\(self: MatProxyData, mat: IMaterial, values: table\)/); + expect(matProxyData).toMatch(/---@field bind fun\(self: MatProxyData, mat: IMaterial, ent: Entity\)/); + expect(iMaterialSetTexture).toMatch(/---@param texture ITexture\|string/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/IMaterial.SetTexture.lua b/custom/IMaterial.SetTexture.lua new file mode 100644 index 00000000..a835f143 --- /dev/null +++ b/custom/IMaterial.SetTexture.lua @@ -0,0 +1,9 @@ +---Sets the specified material texture to the specified texture, does nothing on a type mismatch. +--- +---Calls [IMaterial:Recompute](https://wiki.facepunch.com/gmod/IMaterial:Recompute) internally. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/IMaterial:SetTexture +---@param materialTexture string The name of the keyvalue on the material to store the texture on. +---@param texture ITexture|string The new texture. This can also be a string, the name of the new texture. +function IMaterial:SetTexture(materialTexture, texture) end diff --git a/custom/MatProxyData.lua b/custom/MatProxyData.lua new file mode 100644 index 00000000..5bc5cbd1 --- /dev/null +++ b/custom/MatProxyData.lua @@ -0,0 +1,21 @@ +---Table structure used by [matproxy.Add](https://wiki.facepunch.com/gmod/matproxy.Add). +---@realm client +---@source https://wiki.facepunch.com/gmod/Structures/MatProxyData +---@class (partial) MatProxyData +---The name of the material proxy. +---@field name string +---The function used to get variables from the ".vmt". Called once per each ".vmt". +--- +---Function argument(s): +---* MatProxyData `self` - The table structure itself. +---* IMaterial `mat` - Material the material proxy is applied to. +---* table `values` - The material key values. +---@field init? fun(self: MatProxyData, mat: IMaterial, values: table) +---The function used to apply the proxy. This is called every frame while any materials with this proxy are used in world. +--- +---Function argument(s): +---* MatProxyData `self` - The table structure itself. +---* IMaterial `mat` - Material the material proxy is applied to. +---* Entity `ent` - The entity the material instance is applied to, if any. +---@field bind fun(self: MatProxyData, mat: IMaterial, ent: Entity) +local MatProxyData = {} From 361cba1b1c603ba240246dea1ea9c588e225b1b3 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:18:18 +0100 Subject: [PATCH 021/117] Add vgui.RegisterFile call metadata --- __tests__/custom-annotations.spec.ts | 6 ++++++ custom/vgui.RegisterFile.lua | 12 ++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 custom/vgui.RegisterFile.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index d09c1039..d0c4caa8 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -46,6 +46,8 @@ describe('custom and plugin annotation smoke checks', () => { const envFire = fs.readFileSync(path.join(customRoot, 'class.env_fire.lua'), 'utf8'); const matProxyData = fs.readFileSync(path.join(customRoot, 'MatProxyData.lua'), 'utf8'); const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); + const vguiRegisterFile = fs.readFileSync(path.join(customRoot, 'vgui.RegisterFile.lua'), 'utf8'); + const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); expect(globals).toMatch(/---@alias GPlayer Player/); expect(globals).toMatch(/---@class NULL : Entity/); @@ -110,6 +112,10 @@ describe('custom and plugin annotation smoke checks', () => { expect(matProxyData).toMatch(/---@field init\? fun\(self: MatProxyData, mat: IMaterial, values: table\)/); expect(matProxyData).toMatch(/---@field bind fun\(self: MatProxyData, mat: IMaterial, ent: Entity\)/); expect(iMaterialSetTexture).toMatch(/---@param texture ITexture\|string/); + expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); + expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); + expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); + expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/vgui.RegisterFile.lua b/custom/vgui.RegisterFile.lua new file mode 100644 index 00000000..c1f506d5 --- /dev/null +++ b/custom/vgui.RegisterFile.lua @@ -0,0 +1,12 @@ +---Registers a new VGUI panel from a file, to be used with vgui.CreateFromTable. +--- +---The loaded file receives a temporary global `PANEL` table before it is included. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/vgui.RegisterFile +---@generic T: table +---@[call_arg("gmod.load", "include")] +---@[call_arg("gmod.vgui_panel", "register_file")] +---@param file string The file to register. +---@return T # A table containing info about the panel. +function vgui.RegisterFile(file) end From 9cc3b5577423526c50642bf7f4992902d6b01248 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:18:59 +0100 Subject: [PATCH 022/117] Fix ViewData optional fields --- __tests__/custom-annotations.spec.ts | 8 +++++++ custom/ViewData.lua | 33 ++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 custom/ViewData.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index d0c4caa8..d838e3db 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -47,7 +47,9 @@ describe('custom and plugin annotation smoke checks', () => { const matProxyData = fs.readFileSync(path.join(customRoot, 'MatProxyData.lua'), 'utf8'); const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); const vguiRegisterFile = fs.readFileSync(path.join(customRoot, 'vgui.RegisterFile.lua'), 'utf8'); + const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); + const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); expect(globals).toMatch(/---@alias GPlayer Player/); expect(globals).toMatch(/---@class NULL : Entity/); @@ -116,6 +118,12 @@ describe('custom and plugin annotation smoke checks', () => { expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); + expect(viewData).toMatch(/---@field origin\? Vector/); + expect(viewData).toMatch(/---@field angles\? Angle/); + expect(viewData).toMatch(/---@field offcenter\? table/); + expect(generatedStructures).toMatch(/---@field origin\? Vector/); + expect(generatedStructures).toMatch(/---@field angles\? Angle/); + expect(generatedStructures).toMatch(/---@field offcenter\? table/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/ViewData.lua b/custom/ViewData.lua new file mode 100644 index 00000000..05be6cee --- /dev/null +++ b/custom/ViewData.lua @@ -0,0 +1,33 @@ +---Table structure used for [render.RenderView](https://wiki.facepunch.com/gmod/render.RenderView). +--- +---Missing values are inherited from the current engine view setup. +---@realm client +---@source https://wiki.facepunch.com/gmod/Structures/ViewData +---@class (partial) ViewData +---@field origin? Vector The view's original position. +---@field angles? Angle The view's angles. +---@field aspect? number Default width divided by height. Has a deprecated alias `aspectratio`. +---@field x? number The x position of the viewport to render in. +---@field y? number The y position of the viewport to render in. +---@field w? number The width of the viewport to render in. +---@field h? number The height of the viewport to render in. +---@field drawhud? boolean Draw the HUD and call the hud painting related hooks. +---@field drawmonitors? boolean Draw monitors. +---@field drawviewmodel? boolean The weapon's viewmodel. +---@field drawviewer? boolean Whether to force draw the local player or not. +---@field viewmodelfov? number The viewmodel's FOV. +---@field fov? number The main view's FOV. +---@field ortho? table If set, renders the view orthogonally. +---@field ortholeft? number Deprecated left clipping plane coordinate. +---@field orthoright? number Deprecated right clipping plane coordinate. +---@field orthotop? number Deprecated top clipping plane coordinate. +---@field orthobottom? number Deprecated bottom clipping plane coordinate. +---@field znear? number The distance of the view's origin to the near clipping plane. +---@field zfar? number The distance of the view's origin to the far clipping plane. +---@field znearviewmodel? number The distance to the near clipping plane for the viewmodel. +---@field zfarviewmodel? number The distance to the far clipping plane for the viewmodel. +---@field dopostprocess? boolean Disables post processing. +---@field bloomtone? boolean Disables default engine bloom and pauses HDR brightness changes. +---@field viewid? VIEW Which logical part of the scene an entity is rendered in. +---@field offcenter? table Portion of the screen to draw for off-center rendering. +local ViewData = {} From ae88f49ffc9b8483e6b86f5a98ba3466239e21f7 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:36:53 +0100 Subject: [PATCH 023/117] Add engine entity and panel class annotations --- __tests__/custom-annotations.spec.ts | 15 +++++++++++++++ custom/class.EngineEntities.lua | 26 ++++++++++++++++++++++++++ custom/class.EnginePanels.lua | 5 +++++ 3 files changed, 46 insertions(+) create mode 100644 custom/class.EngineEntities.lua create mode 100644 custom/class.EnginePanels.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index d838e3db..1695a8b8 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -48,6 +48,9 @@ describe('custom and plugin annotation smoke checks', () => { const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); const vguiRegisterFile = fs.readFileSync(path.join(customRoot, 'vgui.RegisterFile.lua'), 'utf8'); const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); + const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); + const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); + const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); @@ -124,6 +127,18 @@ describe('custom and plugin annotation smoke checks', () => { expect(generatedStructures).toMatch(/---@field origin\? Vector/); expect(generatedStructures).toMatch(/---@field angles\? Angle/); expect(generatedStructures).toMatch(/---@field offcenter\? table/); + expect(engineEntities).toMatch(/---@class phys_constraintsystem : Entity/); + expect(engineEntities).toMatch(/---@class gmod_winch_controller : Entity/); + expect(engineEntities).toMatch(/---@class hunter_flechette : Entity/); + expect(enginePanels).toMatch(/---@class \(partial\) Chromium : HTML/); + expect(enginePanels).toMatch(/---@class \(partial\) ModelImage : Panel/); + expect(enginePanels).toMatch(/---@class \(partial\) URLLabel : Label/); + expect(generatedCustomClasses).toMatch(/---@class phys_constraintsystem : Entity/); + expect(generatedCustomClasses).toMatch(/---@class gmod_winch_controller : Entity/); + expect(generatedCustomClasses).toMatch(/---@class hunter_flechette : Entity/); + expect(generatedCustomClasses).toMatch(/---@class \(partial\) Chromium : HTML/); + expect(generatedCustomClasses).toMatch(/---@class \(partial\) ModelImage : Panel/); + expect(generatedCustomClasses).toMatch(/---@class \(partial\) URLLabel : Label/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/class.EngineEntities.lua b/custom/class.EngineEntities.lua new file mode 100644 index 00000000..5604edc9 --- /dev/null +++ b/custom/class.EngineEntities.lua @@ -0,0 +1,26 @@ +---Built-in engine entity classes used by base Garry's Mod Lua. +--- +---These are created by engine-side entity factories such as `ents.Create`. +---@class gmod_anchor : Entity +---@class gmod_hands : Entity +---@class gmod_winch_controller : Entity +---@class hunter_flechette : Entity +---@class keyframe_rope : Entity +---@class logic_collision_pair : Entity +---@class phys_ballsocket : Entity +---@class phys_bone_follower : Entity +---@class phys_constraint : Entity +---@class phys_constraintsystem : Entity +---@class phys_hinge : Entity +---@class phys_keepupright : Entity +---@class phys_lengthconstraint : Entity +---@class phys_magnet : Entity +---@class phys_pulleyconstraint : Entity +---@class phys_ragdollconstraint : Entity +---@class phys_slideconstraint : Entity +---@class phys_spring : Entity +---@class phys_torque : Entity +---@class point_viewcontrol : Entity +---@class ragdoll_motion : Entity +---@class widget_bone : Entity +local EngineEntities = {} diff --git a/custom/class.EnginePanels.lua b/custom/class.EnginePanels.lua new file mode 100644 index 00000000..3626a6b5 --- /dev/null +++ b/custom/class.EnginePanels.lua @@ -0,0 +1,5 @@ +---Built-in panel classes missing base-class information in generated docs. +---@class (partial) Chromium : HTML +---@class (partial) ModelImage : Panel +---@class (partial) URLLabel : Label +local EnginePanels = {} From 8e678a434c633ef225be6749f19aacbf1d805d7a Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:39:28 +0100 Subject: [PATCH 024/117] Fix DMenu AddPanel panel parameter --- __tests__/custom-annotations.spec.ts | 2 ++ custom/DMenu.AddPanel.lua | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 1695a8b8..f327bde6 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -24,6 +24,7 @@ describe('custom and plugin annotation smoke checks', () => { const dCheckBoxLabel = fs.readFileSync(path.join(customRoot, 'class.DCheckBoxLabel.lua'), 'utf8'); const dHtmlControls = fs.readFileSync(path.join(customRoot, 'class.DHTMLControls.lua'), 'utf8'); const dPanelList = fs.readFileSync(path.join(customRoot, 'class.DPanelList.lua'), 'utf8'); + const dMenuAddPanel = fs.readFileSync(path.join(customRoot, 'DMenu.AddPanel.lua'), 'utf8'); const httpRequest = fs.readFileSync(path.join(customRoot, 'HTTPRequest.lua'), 'utf8'); const globalHttp = fs.readFileSync(path.join(customRoot, 'Global.HTTP.lua'), 'utf8'); const entsCreate = fs.readFileSync(path.join(customRoot, 'ents.Create.lua'), 'utf8'); @@ -68,6 +69,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(dPanelList).toMatch(/---@class DPanelList : DPanel/); expect(dPanelList).toMatch(/---@field Items Panel\[]/); + expect(dMenuAddPanel).toMatch(/---@param pnl T The panel that you want to add\./); expect(httpRequest).toMatch(/---@alias HTTPRequestMethodWithParameters/); expect(httpRequest).toMatch(/---@class \(exact\) HTTPRequestWithParameters : HTTPRequest/); diff --git a/custom/DMenu.AddPanel.lua b/custom/DMenu.AddPanel.lua index 4dbcf3fb..4b4c0484 100644 --- a/custom/DMenu.AddPanel.lua +++ b/custom/DMenu.AddPanel.lua @@ -5,5 +5,5 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/DMenu:AddPanel ---@generic T : Panel ----@param pnl `T` The panel that you want to add. +---@param pnl T The panel that you want to add. function DMenu:AddPanel(pnl) end From b095693335a83af8df7d1eb0a96c4bc8c8f92d49 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:00:37 +0100 Subject: [PATCH 025/117] Fix coerced checkbox and cookie annotations --- __tests__/custom-annotations.spec.ts | 21 +++++++++++++++++++++ custom/DCheckBox.SetChecked.lua | 9 +++++++++ custom/DCheckBox.SetValue.lua | 9 +++++++++ custom/DCheckBoxLabel.SetChecked.lua | 7 +++++++ custom/DCheckBoxLabel.SetValue.lua | 7 +++++++ custom/DFileBrowser.SetOpen.lua | 9 +++++++++ custom/Panel.GetCookie.lua | 9 +++++++++ custom/Panel.GetCookieNumber.lua | 9 +++++++++ custom/Panel.SetCookie.lua | 8 ++++++++ custom/cookie.Set.lua | 8 ++++++++ 10 files changed, 96 insertions(+) create mode 100644 custom/DCheckBox.SetChecked.lua create mode 100644 custom/DCheckBox.SetValue.lua create mode 100644 custom/DCheckBoxLabel.SetChecked.lua create mode 100644 custom/DCheckBoxLabel.SetValue.lua create mode 100644 custom/DFileBrowser.SetOpen.lua create mode 100644 custom/Panel.GetCookie.lua create mode 100644 custom/Panel.GetCookieNumber.lua create mode 100644 custom/Panel.SetCookie.lua create mode 100644 custom/cookie.Set.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index f327bde6..01da1186 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -25,6 +25,15 @@ describe('custom and plugin annotation smoke checks', () => { const dHtmlControls = fs.readFileSync(path.join(customRoot, 'class.DHTMLControls.lua'), 'utf8'); const dPanelList = fs.readFileSync(path.join(customRoot, 'class.DPanelList.lua'), 'utf8'); const dMenuAddPanel = fs.readFileSync(path.join(customRoot, 'DMenu.AddPanel.lua'), 'utf8'); + const dCheckBoxSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetValue.lua'), 'utf8'); + const dCheckBoxSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetChecked.lua'), 'utf8'); + const dCheckBoxLabelSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetValue.lua'), 'utf8'); + const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); + const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); + const panelGetCookie = fs.readFileSync(path.join(customRoot, 'Panel.GetCookie.lua'), 'utf8'); + const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); + const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); + const cookieSet = fs.readFileSync(path.join(customRoot, 'cookie.Set.lua'), 'utf8'); const httpRequest = fs.readFileSync(path.join(customRoot, 'HTTPRequest.lua'), 'utf8'); const globalHttp = fs.readFileSync(path.join(customRoot, 'Global.HTTP.lua'), 'utf8'); const entsCreate = fs.readFileSync(path.join(customRoot, 'ents.Create.lua'), 'utf8'); @@ -70,6 +79,18 @@ describe('custom and plugin annotation smoke checks', () => { expect(dPanelList).toMatch(/---@class DPanelList : DPanel/); expect(dPanelList).toMatch(/---@field Items Panel\[]/); expect(dMenuAddPanel).toMatch(/---@param pnl T The panel that you want to add\./); + expect(dCheckBoxSetValue).toMatch(/---@param checked any/); + expect(dCheckBoxSetChecked).toMatch(/---@param checked any/); + expect(dCheckBoxLabelSetValue).toMatch(/---@param checked any/); + expect(dCheckBoxLabelSetChecked).toMatch(/---@param checked any/); + expect(dFileBrowserSetOpen).toMatch(/---@param open any/); + expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); + expect(panelGetCookie).toMatch(/---@param default\? string/); + expect(panelGetCookie).toMatch(/---@return string\|nil/); + expect(panelGetCookieNumber).toMatch(/---@param default\? number/); + expect(panelGetCookieNumber).toMatch(/---@return number\|nil/); + expect(panelSetCookie).toMatch(/---@param value\? string\|number\|boolean/); + expect(cookieSet).toMatch(/---@param value\? string\|number\|boolean/); expect(httpRequest).toMatch(/---@alias HTTPRequestMethodWithParameters/); expect(httpRequest).toMatch(/---@class \(exact\) HTTPRequestWithParameters : HTTPRequest/); diff --git a/custom/DCheckBox.SetChecked.lua b/custom/DCheckBox.SetChecked.lua new file mode 100644 index 00000000..86f54428 --- /dev/null +++ b/custom/DCheckBox.SetChecked.lua @@ -0,0 +1,9 @@ +---Sets the checked state of the checkbox. +--- +--- This is backed by AccessorFunc with FORCE_BOOL, so the input is coerced with tobool before storage. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DCheckBox:SetChecked +---@param checked any Value to coerce into the checked state. +function DCheckBox:SetChecked(checked) end + diff --git a/custom/DCheckBox.SetValue.lua b/custom/DCheckBox.SetValue.lua new file mode 100644 index 00000000..1a01e81c --- /dev/null +++ b/custom/DCheckBox.SetValue.lua @@ -0,0 +1,9 @@ +---Sets the checked state of the checkbox, and calls the checkbox's DCheckBox:OnChange and Panel:ConVarChanged methods. +--- +--- The value is coerced with tobool before the checked state is stored. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DCheckBox:SetValue +---@param checked any Value to coerce into the checked state. +function DCheckBox:SetValue(checked) end + diff --git a/custom/DCheckBoxLabel.SetChecked.lua b/custom/DCheckBoxLabel.SetChecked.lua new file mode 100644 index 00000000..5376208f --- /dev/null +++ b/custom/DCheckBoxLabel.SetChecked.lua @@ -0,0 +1,7 @@ +---Sets the checked state of the checkbox label's embedded DCheckBox. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DCheckBoxLabel:SetChecked +---@param checked any Value forwarded to DCheckBox:SetChecked. +function DCheckBoxLabel:SetChecked(checked) end + diff --git a/custom/DCheckBoxLabel.SetValue.lua b/custom/DCheckBoxLabel.SetValue.lua new file mode 100644 index 00000000..81fa6753 --- /dev/null +++ b/custom/DCheckBoxLabel.SetValue.lua @@ -0,0 +1,7 @@ +---Sets the checked state of the checkbox label's embedded DCheckBox. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DCheckBoxLabel:SetValue +---@param checked any Value forwarded to DCheckBox:SetValue. +function DCheckBoxLabel:SetValue(checked) end + diff --git a/custom/DFileBrowser.SetOpen.lua b/custom/DFileBrowser.SetOpen.lua new file mode 100644 index 00000000..4594bc89 --- /dev/null +++ b/custom/DFileBrowser.SetOpen.lua @@ -0,0 +1,9 @@ +---Opens or closes the file tree. +--- +--- The open state is coerced with tobool before it is stored. +---@realm client +---@source https://wiki.facepunch.com/gmod/DFileBrowser:SetOpen +---@param open any Value to coerce into the open state. +---@param useAnim? boolean If true, the DTree open/close animation is used. +function DFileBrowser:SetOpen(open, useAnim) end + diff --git a/custom/Panel.GetCookie.lua b/custom/Panel.GetCookie.lua new file mode 100644 index 00000000..d7a57a99 --- /dev/null +++ b/custom/Panel.GetCookie.lua @@ -0,0 +1,9 @@ +---Gets the value of a cookie stored by the panel object. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:GetCookie +---@param cookieName string The name of the cookie from which to retrieve the value. +---@param default? string The default value to return if the cookie does not exist. +---@return string|nil # The value of the stored cookie, the default value, or nil if neither exists. +function Panel:GetCookie(cookieName, default) end + diff --git a/custom/Panel.GetCookieNumber.lua b/custom/Panel.GetCookieNumber.lua new file mode 100644 index 00000000..5af3a324 --- /dev/null +++ b/custom/Panel.GetCookieNumber.lua @@ -0,0 +1,9 @@ +---Gets the value of a cookie stored by the panel object, as a number. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:GetCookieNumber +---@param cookieName string The name of the cookie from which to retrieve the value. +---@param default? number The default value to return if the cookie does not exist. +---@return number|nil # The numeric cookie value, the default value, or nil if neither exists. +function Panel:GetCookieNumber(cookieName, default) end + diff --git a/custom/Panel.SetCookie.lua b/custom/Panel.SetCookie.lua new file mode 100644 index 00000000..6276744f --- /dev/null +++ b/custom/Panel.SetCookie.lua @@ -0,0 +1,8 @@ +---Stores a value in the named cookie using Panel:GetCookieName as prefix. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:SetCookie +---@param cookieName string The name of the cookie to set. +---@param value? string|number|boolean The value to store, or nil to clear the value. +function Panel:SetCookie(cookieName, value) end + diff --git a/custom/cookie.Set.lua b/custom/cookie.Set.lua new file mode 100644 index 00000000..0ab6a34e --- /dev/null +++ b/custom/cookie.Set.lua @@ -0,0 +1,8 @@ +---Creates or updates a cookie in the database. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/cookie.Set +---@param key string The name of the cookie. +---@param value? string|number|boolean The value to store, or nil to clear the value. +function cookie.Set(key, value) end + From 091475c7fed266393699eccced35e2328b96e1ef Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:13:16 +0100 Subject: [PATCH 026/117] Fix render clear target color annotation --- __tests__/custom-annotations.spec.ts | 4 ++++ custom/render.ClearRenderTarget.lua | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 custom/render.ClearRenderTarget.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 01da1186..47c5cf50 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -56,12 +56,14 @@ describe('custom and plugin annotation smoke checks', () => { const envFire = fs.readFileSync(path.join(customRoot, 'class.env_fire.lua'), 'utf8'); const matProxyData = fs.readFileSync(path.join(customRoot, 'MatProxyData.lua'), 'utf8'); const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); + const renderClearRenderTarget = fs.readFileSync(path.join(customRoot, 'render.ClearRenderTarget.lua'), 'utf8'); const vguiRegisterFile = fs.readFileSync(path.join(customRoot, 'vgui.RegisterFile.lua'), 'utf8'); const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); + const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); expect(globals).toMatch(/---@alias GPlayer Player/); @@ -140,6 +142,8 @@ describe('custom and plugin annotation smoke checks', () => { expect(matProxyData).toMatch(/---@field init\? fun\(self: MatProxyData, mat: IMaterial, values: table\)/); expect(matProxyData).toMatch(/---@field bind fun\(self: MatProxyData, mat: IMaterial, ent: Entity\)/); expect(iMaterialSetTexture).toMatch(/---@param texture ITexture\|string/); + expect(renderClearRenderTarget).toMatch(/---@param color Color/); + expect(generatedRender).toMatch(/---@param color Color The color\./); expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); diff --git a/custom/render.ClearRenderTarget.lua b/custom/render.ClearRenderTarget.lua new file mode 100644 index 00000000..cfc1d8b2 --- /dev/null +++ b/custom/render.ClearRenderTarget.lua @@ -0,0 +1,8 @@ +---Clears a render target. +--- +--- It uses [render.Clear](https://wiki.facepunch.com/gmod/render.Clear) then [render.SetRenderTarget](https://wiki.facepunch.com/gmod/render.SetRenderTarget) on the modified render target. +---@realm client +---@source https://wiki.facepunch.com/gmod/render.ClearRenderTarget +---@param texture ITexture +---@param color Color The color. +function render.ClearRenderTarget(texture, color) end From fbdcd5f708e9ce93b0ec0feb304561232b76db5a Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:14:52 +0100 Subject: [PATCH 027/117] Fix DProperty value changed annotation --- __tests__/custom-annotations.spec.ts | 4 ++++ custom/DProperty_Generic.ValueChanged.lua | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 custom/DProperty_Generic.ValueChanged.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 47c5cf50..6a4b1920 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -30,6 +30,7 @@ describe('custom and plugin annotation smoke checks', () => { const dCheckBoxLabelSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetValue.lua'), 'utf8'); const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); + const dPropertyGenericValueChanged = fs.readFileSync(path.join(customRoot, 'DProperty_Generic.ValueChanged.lua'), 'utf8'); const panelGetCookie = fs.readFileSync(path.join(customRoot, 'Panel.GetCookie.lua'), 'utf8'); const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); @@ -62,6 +63,7 @@ describe('custom and plugin annotation smoke checks', () => { const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); + const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); @@ -87,6 +89,8 @@ describe('custom and plugin annotation smoke checks', () => { expect(dCheckBoxLabelSetChecked).toMatch(/---@param checked any/); expect(dFileBrowserSetOpen).toMatch(/---@param open any/); expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); + expect(dPropertyGenericValueChanged).toMatch(/---@param force\? boolean/); + expect(generatedDPropertyGeneric).toMatch(/---@param force\? boolean/); expect(panelGetCookie).toMatch(/---@param default\? string/); expect(panelGetCookie).toMatch(/---@return string\|nil/); expect(panelGetCookieNumber).toMatch(/---@param default\? number/); diff --git a/custom/DProperty_Generic.ValueChanged.lua b/custom/DProperty_Generic.ValueChanged.lua new file mode 100644 index 00000000..b25e4f2d --- /dev/null +++ b/custom/DProperty_Generic.ValueChanged.lua @@ -0,0 +1,6 @@ +---Called by this control, or a derived control, to alert the row of the change. +---@realm client +---@source https://wiki.facepunch.com/gmod/DProperty_Generic:ValueChanged +---@param newVal any The new value. +---@param force? boolean Force an update. +function DProperty_Generic:ValueChanged(newVal, force) end From 7dea993cde8cdccc2a9dcb409b2d3c5777e15448 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:20:05 +0100 Subject: [PATCH 028/117] Fix panel helper annotations --- __tests__/custom-annotations.spec.ts | 11 +++++++++++ custom/DSlider.SetNotches.lua | 6 ++++++ custom/Panel.Add.lua | 1 + custom/Panel.SetSelectionCanvas.lua | 6 ++++++ 4 files changed, 24 insertions(+) create mode 100644 custom/DSlider.SetNotches.lua create mode 100644 custom/Panel.SetSelectionCanvas.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 6a4b1920..3aa3b7fc 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -31,6 +31,9 @@ describe('custom and plugin annotation smoke checks', () => { const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); const dPropertyGenericValueChanged = fs.readFileSync(path.join(customRoot, 'DProperty_Generic.ValueChanged.lua'), 'utf8'); + const dSliderSetNotches = fs.readFileSync(path.join(customRoot, 'DSlider.SetNotches.lua'), 'utf8'); + const panelAdd = fs.readFileSync(path.join(customRoot, 'Panel.Add.lua'), 'utf8'); + const panelSetSelectionCanvas = fs.readFileSync(path.join(customRoot, 'Panel.SetSelectionCanvas.lua'), 'utf8'); const panelGetCookie = fs.readFileSync(path.join(customRoot, 'Panel.GetCookie.lua'), 'utf8'); const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); @@ -64,6 +67,8 @@ describe('custom and plugin annotation smoke checks', () => { const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); + const generatedDSlider = fs.readFileSync(path.join(process.cwd(), 'output', 'dslider.lua'), 'utf8'); + const generatedPanel = fs.readFileSync(path.join(process.cwd(), 'output', 'panel.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); @@ -91,6 +96,12 @@ describe('custom and plugin annotation smoke checks', () => { expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); expect(dPropertyGenericValueChanged).toMatch(/---@param force\? boolean/); expect(generatedDPropertyGeneric).toMatch(/---@param force\? boolean/); + expect(dSliderSetNotches).toMatch(/---@param notches\? number/); + expect(generatedDSlider).toMatch(/---@param notches\? number/); + expect(panelAdd).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); + expect(panelSetSelectionCanvas).toMatch(/---@param set boolean\|Panel/); + expect(generatedPanel).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); + expect(generatedPanel).toMatch(/---@param set boolean\|Panel/); expect(panelGetCookie).toMatch(/---@param default\? string/); expect(panelGetCookie).toMatch(/---@return string\|nil/); expect(panelGetCookieNumber).toMatch(/---@param default\? number/); diff --git a/custom/DSlider.SetNotches.lua b/custom/DSlider.SetNotches.lua new file mode 100644 index 00000000..a00250b8 --- /dev/null +++ b/custom/DSlider.SetNotches.lua @@ -0,0 +1,6 @@ +---Appears to be non functioning, however is still used by panels such as [DNumSlider](https://wiki.facepunch.com/gmod/DNumSlider). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DSlider:SetNotches +---@param notches? number +function DSlider:SetNotches(notches) end diff --git a/custom/Panel.Add.lua b/custom/Panel.Add.lua index d6ecac34..1f4eec89 100644 --- a/custom/Panel.Add.lua +++ b/custom/Panel.Add.lua @@ -5,6 +5,7 @@ ---@generic T : Panel ---@overload fun(self: Panel, panel: Panel): Panel # Parents an existing panel to this panel. ---@overload fun(self: Panel, panelTable: table): Panel # Creates a panel from a PANEL table and parents it to this panel. +---@overload fun(self: Panel, className: `T`, parent: Panel): T # Creates a panel by class name with an explicit parent. ---@[call_arg("gmod.vgui_panel", "reference")] ---@param className `T` The panel class name to create and add. ---@return (instance) T # The created panel. diff --git a/custom/Panel.SetSelectionCanvas.lua b/custom/Panel.SetSelectionCanvas.lua new file mode 100644 index 00000000..3efba164 --- /dev/null +++ b/custom/Panel.SetSelectionCanvas.lua @@ -0,0 +1,6 @@ +---Enables the panel object for selection (much like the spawn menu). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:SetSelectionCanvas +---@param set boolean|Panel Whether to enable selection, or an existing selection canvas value. +function Panel:SetSelectionCanvas(set) end From 2fb55ccbb98b6dd5dd04ef7be41a61ab0e495c34 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:31:09 +0100 Subject: [PATCH 029/117] Fix PropertyAdd callback self annotation --- __tests__/custom-annotations.spec.ts | 11 +++++++++++ custom/PropertyAdd.lua | 20 ++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 3aa3b7fc..ce14652c 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -38,6 +38,7 @@ describe('custom and plugin annotation smoke checks', () => { const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); const cookieSet = fs.readFileSync(path.join(customRoot, 'cookie.Set.lua'), 'utf8'); + const propertyAdd = fs.readFileSync(path.join(customRoot, 'PropertyAdd.lua'), 'utf8'); const httpRequest = fs.readFileSync(path.join(customRoot, 'HTTPRequest.lua'), 'utf8'); const globalHttp = fs.readFileSync(path.join(customRoot, 'Global.HTTP.lua'), 'utf8'); const entsCreate = fs.readFileSync(path.join(customRoot, 'ents.Create.lua'), 'utf8'); @@ -108,6 +109,16 @@ describe('custom and plugin annotation smoke checks', () => { expect(panelGetCookieNumber).toMatch(/---@return number\|nil/); expect(panelSetCookie).toMatch(/---@param value\? string\|number\|boolean/); expect(cookieSet).toMatch(/---@param value\? string\|number\|boolean/); + expect(propertyAdd).toMatch(/---@field Filter fun\(self: PropertyAddRuntime, ent: Entity, player: Player\):\(check: boolean\)/); + expect(propertyAdd).toMatch(/---@class \(partial\) PropertyAddRuntime : PropertyAdd/); + expect(propertyAdd).toMatch(/---@field \[string\] any/); + expect(propertyAdd).toMatch(/---@field MsgStart fun\(self: PropertyAddRuntime\)/); + expect(propertyAdd).toMatch(/---@field MsgEnd fun\(self: PropertyAddRuntime\)/); + expect(generatedStructures).toMatch(/---@field Filter fun\(self: PropertyAddRuntime, ent: Entity, player: Player\):\(check: boolean\)/); + expect(generatedStructures).toMatch(/---@class \(partial\) PropertyAddRuntime : PropertyAdd/); + expect(generatedStructures).toMatch(/---@field \[string\] any/); + expect(generatedStructures).toMatch(/---@field MsgStart fun\(self: PropertyAddRuntime\)/); + expect(generatedStructures).toMatch(/---@field MsgEnd fun\(self: PropertyAddRuntime\)/); expect(httpRequest).toMatch(/---@alias HTTPRequestMethodWithParameters/); expect(httpRequest).toMatch(/---@class \(exact\) HTTPRequestWithParameters : HTTPRequest/); diff --git a/custom/PropertyAdd.lua b/custom/PropertyAdd.lua index ebe48fa0..9b58faaf 100644 --- a/custom/PropertyAdd.lua +++ b/custom/PropertyAdd.lua @@ -7,10 +7,18 @@ ---@field MenuIcon? string Icon to show on opened menu for this item. Optional for simple properties and unused for toggle properties. ---@field Order number Where in the list this property should be positioned, relative to other properties. ---@field PrependSpacer? boolean Whether to add a spacer before this property. ----@field Filter fun(self: table, ent: Entity, player: Player):(check: boolean) Used clientside to decide whether this property should be shown for an entity. ----@field Checked? fun(self: table, ent: Entity, tr: table):(check: boolean) Required only for toggle properties. ----@field Action fun(self: table, ent: Entity, tr: table) Called clientside when the property is clicked. ----@field Receive? fun(self: table, len: number, ply: Player) Called serverside if the client sends a message in the Action function. ----@field MenuOpen? fun(self: table, option: DMenuOption, ent: Entity, tr: table) Called clientside when the property option has been created in the right-click menu. ----@field OnCreate? fun(self: table, menu: DMenu, option: DMenuOption) Called clientside after the property option has been created. +---@field InternalName? string Internal lower-case property name assigned by properties.Add. +---@field Filter fun(self: PropertyAddRuntime, ent: Entity, player: Player):(check: boolean) Used clientside to decide whether this property should be shown for an entity. +---@field Checked? fun(self: PropertyAddRuntime, ent: Entity, tr: table):(check: boolean) Required only for toggle properties. +---@field Action fun(self: PropertyAddRuntime, ent: Entity, tr: table) Called clientside when the property is clicked. +---@field Receive? fun(self: PropertyAddRuntime, len: number, ply: Player) Called serverside if the client sends a message in the Action function. +---@field MenuOpen? fun(self: PropertyAddRuntime, option: DMenuOption, ent: Entity, tr: table) Called clientside when the property option has been created in the right-click menu. +---@field OnCreate? fun(self: PropertyAddRuntime, menu: DMenu, option: DMenuOption) Called clientside after the property option has been created. local PropertyAdd = {} + +---@class (partial) PropertyAddRuntime : PropertyAdd +---@field [string] any Additional property-specific data or helper methods. +---@field InternalName string Internal lower-case property name assigned by properties.Add. +---@field MsgStart fun(self: PropertyAddRuntime) Starts a properties net message for this property. +---@field MsgEnd fun(self: PropertyAddRuntime) Sends the current properties net message to the server. +local PropertyAddRuntime = {} From 6d7099b7673417d7d9171401650060495f948acd Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:34:06 +0100 Subject: [PATCH 030/117] Fix os.date return annotation --- __tests__/custom-annotations.spec.ts | 1 + custom/os.date.lua | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index ce14652c..2842210a 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -153,6 +153,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(duplicatorCreateEntityFromTable).toMatch(/---@param entTable EntityCopyData/); expect(osDate).toMatch(/---@param format\? string/); + expect(osDate).toMatch(/---@return string\|DateData/); expect(tableCopy).toMatch(/---@generic T : table/); expect(tableCopy).toMatch(/---@param originalTable T/); expect(tableCopy).toMatch(/---@return T/); diff --git a/custom/os.date.lua b/custom/os.date.lua index 004fc9cb..9c48e132 100644 --- a/custom/os.date.lua +++ b/custom/os.date.lua @@ -27,5 +27,5 @@ ---@overload fun(fmt:"!*t", time?: number):DateData ---@param format? string # The format string. If `*t` or `!*t`, returns a [Structures/DateData](https://wiki.facepunch.com/gmod/Structures/DateData) table instead. ---@param time? number # Time to use for the format. ----@return string # Formatted date string, or a [Structures/DateData](https://wiki.facepunch.com/gmod/Structures/DateData) table if format is `*t` or `!*t`. +---@return string|DateData # Formatted date string, or a [Structures/DateData](https://wiki.facepunch.com/gmod/Structures/DateData) table if format is `*t` or `!*t`. function os.date(format, time) end From e8b241861dc7aab57fe45172301033e2f098ed48 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:37:34 +0100 Subject: [PATCH 031/117] Fix VGUI optional reset annotations --- __tests__/custom-annotations.spec.ts | 24 +++++++++++++++++++ custom/DImage.SetMatName.lua | 8 +++++++ custom/DMenu.SetOpenSubMenu.lua | 8 +++++++ custom/DTree_Node.ChildExpanded.lua | 6 +++++ custom/DTree_Node.PopulateChildrenAndSelf.lua | 8 +++++++ custom/DTree_Node.SetShowFiles.lua | 6 +++++ custom/DTree_Node.SetWildCard.lua | 8 +++++++ custom/Panel.SetParent.lua | 6 +++++ 8 files changed, 74 insertions(+) create mode 100644 custom/DImage.SetMatName.lua create mode 100644 custom/DMenu.SetOpenSubMenu.lua create mode 100644 custom/DTree_Node.ChildExpanded.lua create mode 100644 custom/DTree_Node.PopulateChildrenAndSelf.lua create mode 100644 custom/DTree_Node.SetShowFiles.lua create mode 100644 custom/DTree_Node.SetWildCard.lua create mode 100644 custom/Panel.SetParent.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 2842210a..068d8141 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -30,10 +30,17 @@ describe('custom and plugin annotation smoke checks', () => { const dCheckBoxLabelSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetValue.lua'), 'utf8'); const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); + const dImageSetMatName = fs.readFileSync(path.join(customRoot, 'DImage.SetMatName.lua'), 'utf8'); + const dMenuSetOpenSubMenu = fs.readFileSync(path.join(customRoot, 'DMenu.SetOpenSubMenu.lua'), 'utf8'); const dPropertyGenericValueChanged = fs.readFileSync(path.join(customRoot, 'DProperty_Generic.ValueChanged.lua'), 'utf8'); const dSliderSetNotches = fs.readFileSync(path.join(customRoot, 'DSlider.SetNotches.lua'), 'utf8'); + const dTreeNodeChildExpanded = fs.readFileSync(path.join(customRoot, 'DTree_Node.ChildExpanded.lua'), 'utf8'); + const dTreeNodePopulateChildrenAndSelf = fs.readFileSync(path.join(customRoot, 'DTree_Node.PopulateChildrenAndSelf.lua'), 'utf8'); + const dTreeNodeSetShowFiles = fs.readFileSync(path.join(customRoot, 'DTree_Node.SetShowFiles.lua'), 'utf8'); + const dTreeNodeSetWildCard = fs.readFileSync(path.join(customRoot, 'DTree_Node.SetWildCard.lua'), 'utf8'); const panelAdd = fs.readFileSync(path.join(customRoot, 'Panel.Add.lua'), 'utf8'); const panelSetSelectionCanvas = fs.readFileSync(path.join(customRoot, 'Panel.SetSelectionCanvas.lua'), 'utf8'); + const panelSetParent = fs.readFileSync(path.join(customRoot, 'Panel.SetParent.lua'), 'utf8'); const panelGetCookie = fs.readFileSync(path.join(customRoot, 'Panel.GetCookie.lua'), 'utf8'); const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); @@ -67,8 +74,11 @@ describe('custom and plugin annotation smoke checks', () => { const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); + const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); + const generatedDMenu = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenu.lua'), 'utf8'); const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); const generatedDSlider = fs.readFileSync(path.join(process.cwd(), 'output', 'dslider.lua'), 'utf8'); + const generatedDTreeNode = fs.readFileSync(path.join(process.cwd(), 'output', 'dtree_node.lua'), 'utf8'); const generatedPanel = fs.readFileSync(path.join(process.cwd(), 'output', 'panel.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); @@ -95,14 +105,28 @@ describe('custom and plugin annotation smoke checks', () => { expect(dCheckBoxLabelSetChecked).toMatch(/---@param checked any/); expect(dFileBrowserSetOpen).toMatch(/---@param open any/); expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); + expect(dImageSetMatName).toMatch(/---@param mat\? string/); + expect(dMenuSetOpenSubMenu).toMatch(/---@param item\? Panel/); expect(dPropertyGenericValueChanged).toMatch(/---@param force\? boolean/); expect(generatedDPropertyGeneric).toMatch(/---@param force\? boolean/); expect(dSliderSetNotches).toMatch(/---@param notches\? number/); expect(generatedDSlider).toMatch(/---@param notches\? number/); + expect(dTreeNodeChildExpanded).toMatch(/---@param expanded\? boolean/); + expect(dTreeNodePopulateChildrenAndSelf).toMatch(/---@param expand\? boolean/); + expect(dTreeNodeSetShowFiles).toMatch(/---@param showFiles\? boolean/); + expect(dTreeNodeSetWildCard).toMatch(/---@param wildcard\? string/); + expect(generatedDImage).toMatch(/---@param mat\? string/); + expect(generatedDMenu).toMatch(/---@param item\? Panel/); + expect(generatedDTreeNode).toMatch(/---@param expanded\? boolean/); + expect(generatedDTreeNode).toMatch(/---@param expand\? boolean/); + expect(generatedDTreeNode).toMatch(/---@param showFiles\? boolean/); + expect(generatedDTreeNode).toMatch(/---@param wildcard\? string/); expect(panelAdd).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); expect(panelSetSelectionCanvas).toMatch(/---@param set boolean\|Panel/); + expect(panelSetParent).toMatch(/---@param parent\? Panel/); expect(generatedPanel).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); expect(generatedPanel).toMatch(/---@param set boolean\|Panel/); + expect(generatedPanel).toMatch(/---@param parent\? Panel/); expect(panelGetCookie).toMatch(/---@param default\? string/); expect(panelGetCookie).toMatch(/---@return string\|nil/); expect(panelGetCookieNumber).toMatch(/---@param default\? number/); diff --git a/custom/DImage.SetMatName.lua b/custom/DImage.SetMatName.lua new file mode 100644 index 00000000..b2d03350 --- /dev/null +++ b/custom/DImage.SetMatName.lua @@ -0,0 +1,8 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +---Sets the material to be loaded when the image is first rendered. Used by [DImage:SetOnViewMaterial](https://wiki.facepunch.com/gmod/DImage:SetOnViewMaterial). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DImage:SetMatName +---@param mat? string +function DImage:SetMatName(mat) end diff --git a/custom/DMenu.SetOpenSubMenu.lua b/custom/DMenu.SetOpenSubMenu.lua new file mode 100644 index 00000000..0f7c9d2a --- /dev/null +++ b/custom/DMenu.SetOpenSubMenu.lua @@ -0,0 +1,8 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +---Used internally to store the open submenu by [DMenu:Hide](https://wiki.facepunch.com/gmod/DMenu:Hide), [DMenu:OpenSubMenu](https://wiki.facepunch.com/gmod/DMenu:OpenSubMenu), [DMenu:CloseSubMenu](https://wiki.facepunch.com/gmod/DMenu:CloseSubMenu) +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DMenu:SetOpenSubMenu +---@param item? Panel The menu to store. +function DMenu:SetOpenSubMenu(item) end diff --git a/custom/DTree_Node.ChildExpanded.lua b/custom/DTree_Node.ChildExpanded.lua new file mode 100644 index 00000000..34aab120 --- /dev/null +++ b/custom/DTree_Node.ChildExpanded.lua @@ -0,0 +1,6 @@ +---Called when a child node is expanded or collapsed to propagate this event to parent nodes to update layout. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:ChildExpanded +---@param expanded? boolean +function DTree_Node:ChildExpanded(expanded) end diff --git a/custom/DTree_Node.PopulateChildrenAndSelf.lua b/custom/DTree_Node.PopulateChildrenAndSelf.lua new file mode 100644 index 00000000..a9940267 --- /dev/null +++ b/custom/DTree_Node.PopulateChildrenAndSelf.lua @@ -0,0 +1,8 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +---Called automatically from [DTree_Node:SetExpanded](https://wiki.facepunch.com/gmod/DTree_Node:SetExpanded) to populate the node with sub-nodes from the filesystem if this was enabled via [DTree_Node:MakeFolder](https://wiki.facepunch.com/gmod/DTree_Node:MakeFolder). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:PopulateChildrenAndSelf +---@param expand? boolean Expand self once population process is finished. +function DTree_Node:PopulateChildrenAndSelf(expand) end diff --git a/custom/DTree_Node.SetShowFiles.lua b/custom/DTree_Node.SetShowFiles.lua new file mode 100644 index 00000000..8278ff01 --- /dev/null +++ b/custom/DTree_Node.SetShowFiles.lua @@ -0,0 +1,6 @@ +---Sets whether or not nodes for files should be added when populating the node from filesystem. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:SetShowFiles +---@param showFiles? boolean +function DTree_Node:SetShowFiles(showFiles) end diff --git a/custom/DTree_Node.SetWildCard.lua b/custom/DTree_Node.SetWildCard.lua new file mode 100644 index 00000000..f676a766 --- /dev/null +++ b/custom/DTree_Node.SetWildCard.lua @@ -0,0 +1,8 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +---Sets the wildcard filter for populating the node from filesystem. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:SetWildCard +---@param wildcard? string The wildcard to set. +function DTree_Node:SetWildCard(wildcard) end diff --git a/custom/Panel.SetParent.lua b/custom/Panel.SetParent.lua new file mode 100644 index 00000000..f8ee2fed --- /dev/null +++ b/custom/Panel.SetParent.lua @@ -0,0 +1,6 @@ +---Sets the parent of the panel. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:SetParent +---@param parent? Panel The new parent of the panel, or nil to detach it. +function Panel:SetParent(parent) end From d0345f39b85c51ce38b20feab963752a0c065d26 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:07:27 +0100 Subject: [PATCH 032/117] Fix known engine entity create annotations --- __tests__/custom-annotations.spec.ts | 6 ++++++ custom/class.EngineEntities.lua | 1 + custom/ents.Create.lua | 23 +++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 068d8141..df7bd6eb 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -153,6 +153,11 @@ describe('custom and plugin annotation smoke checks', () => { expect(globalHttp).toMatch(/---@overload fun\(parameters: HTTPRequestWithParameters\): boolean/); expect(globalHttp).toMatch(/---@param parameters HTTPRequest The request parameters/); + expect(entsCreate).toMatch(/---@alias KnownEngineEntityClass/); + expect(entsCreate).toMatch(/"phys_constraint"/); + expect(entsCreate).not.toMatch(/"phys_hinge"/); + expect(entsCreate).not.toMatch(/"widget_bones"/); + expect(entsCreate).toMatch(/---@overload fun\(class: KnownEngineEntityClass\): Entity/); expect(entsCreate).toMatch(/---@return \(instance\) T\|NULL/); expect(vehicleGetDriver).toMatch(/---@return Player\|NULL driver/); expect(getNWEntity).toMatch(/---@overload fun\(self: Entity, key: string\): Entity\|NULL/); @@ -208,6 +213,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(engineEntities).toMatch(/---@class phys_constraintsystem : Entity/); expect(engineEntities).toMatch(/---@class gmod_winch_controller : Entity/); expect(engineEntities).toMatch(/---@class hunter_flechette : Entity/); + expect(engineEntities).toMatch(/---@class widget_bones : Entity/); expect(enginePanels).toMatch(/---@class \(partial\) Chromium : HTML/); expect(enginePanels).toMatch(/---@class \(partial\) ModelImage : Panel/); expect(enginePanels).toMatch(/---@class \(partial\) URLLabel : Label/); diff --git a/custom/class.EngineEntities.lua b/custom/class.EngineEntities.lua index 5604edc9..f83d3ebe 100644 --- a/custom/class.EngineEntities.lua +++ b/custom/class.EngineEntities.lua @@ -23,4 +23,5 @@ ---@class point_viewcontrol : Entity ---@class ragdoll_motion : Entity ---@class widget_bone : Entity +---@class widget_bones : Entity local EngineEntities = {} diff --git a/custom/ents.Create.lua b/custom/ents.Create.lua index 74bbe988..38bef88c 100644 --- a/custom/ents.Create.lua +++ b/custom/ents.Create.lua @@ -3,6 +3,29 @@ --- If you need to perform entity creation when the game starts, create a hook for GM:InitPostEntity and do it there. ---@realm server ---@source https://wiki.facepunch.com/gmod/ents.Create +---@alias KnownEngineEntityClass +---| "gmod_anchor" +---| "gmod_hands" +---| "gmod_winch_controller" +---| "hunter_flechette" +---| "keyframe_rope" +---| "logic_collision_pair" +---| "phys_ballsocket" +---| "phys_bone_follower" +---| "phys_constraint" +---| "phys_constraintsystem" +---| "phys_keepupright" +---| "phys_lengthconstraint" +---| "phys_magnet" +---| "phys_pulleyconstraint" +---| "phys_ragdollconstraint" +---| "phys_slideconstraint" +---| "phys_spring" +---| "phys_torque" +---| "point_viewcontrol" +---| "ragdoll_motion" +---| "widget_bone" +---@overload fun(class: KnownEngineEntityClass): Entity ---@generic T : Entity ---@param class `T` The classname of the entity to create. ---@return (instance) T|NULL # The created entity, or `NULL` if failed. From 5d8bd3d08161af45edb186e5b3dfd7289a22570f Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:45:09 +0100 Subject: [PATCH 033/117] Fix SkeletonConvertor annotations --- __tests__/custom-annotations.spec.ts | 14 ++++++++++++++ custom/ServerQueryData.lua | 21 +++++++++++++++++++++ custom/class.SkeletonConvertor.lua | 11 +++++++++++ custom/list.Set.lua | 7 +++++++ 4 files changed, 53 insertions(+) create mode 100644 custom/ServerQueryData.lua create mode 100644 custom/class.SkeletonConvertor.lua create mode 100644 custom/list.Set.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index df7bd6eb..213b5995 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -73,7 +73,11 @@ describe('custom and plugin annotation smoke checks', () => { const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); + const skeletonConvertor = fs.readFileSync(path.join(customRoot, 'class.SkeletonConvertor.lua'), 'utf8'); + const listSet = fs.readFileSync(path.join(customRoot, 'list.Set.lua'), 'utf8'); + const serverQueryData = fs.readFileSync(path.join(customRoot, 'ServerQueryData.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); + const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); const generatedDMenu = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenu.lua'), 'utf8'); const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); @@ -217,12 +221,22 @@ describe('custom and plugin annotation smoke checks', () => { expect(enginePanels).toMatch(/---@class \(partial\) Chromium : HTML/); expect(enginePanels).toMatch(/---@class \(partial\) ModelImage : Panel/); expect(enginePanels).toMatch(/---@class \(partial\) URLLabel : Label/); + expect(skeletonConvertor).toMatch(/---@class ModelEntity/); + expect(skeletonConvertor).toMatch(/---@field GetModel fun\(self: ModelEntity\): string/); + expect(skeletonConvertor).toMatch(/---@class SkeletonConvertor/); + expect(skeletonConvertor).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); + expect(listSet).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); + expect(serverQueryData).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); expect(generatedCustomClasses).toMatch(/---@class phys_constraintsystem : Entity/); expect(generatedCustomClasses).toMatch(/---@class gmod_winch_controller : Entity/); expect(generatedCustomClasses).toMatch(/---@class hunter_flechette : Entity/); expect(generatedCustomClasses).toMatch(/---@class \(partial\) Chromium : HTML/); expect(generatedCustomClasses).toMatch(/---@class \(partial\) ModelImage : Panel/); expect(generatedCustomClasses).toMatch(/---@class \(partial\) URLLabel : Label/); + expect(generatedCustomClasses).toMatch(/---@class ModelEntity/); + expect(generatedCustomClasses).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); + expect(generatedList).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); + expect(generatedStructures).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/ServerQueryData.lua b/custom/ServerQueryData.lua new file mode 100644 index 00000000..268a6389 --- /dev/null +++ b/custom/ServerQueryData.lua @@ -0,0 +1,21 @@ +--- Used for [serverlist.Query](https://wiki.facepunch.com/gmod/serverlist.Query). +---@realm menu +---@source https://wiki.facepunch.com/gmod/Structures/ServerQueryData +---@class (partial) ServerQueryData +---The game directory to get the servers for. +--- +--- Default: `garrysmod` +---@field GameDir string +---Type of servers to retrieve. Valid values are `internet`, `favorite`, `history` and `lan`. +---@field Type string +---Steam application ID to get the servers for. +--- +--- Default: `4000` +---@field AppID number +---Called when a new server is found and queried. +---@field Callback fun(ping: number, name: string, desc: string, map: string, players: number, maxplayers: number, botplayers: number, pass: boolean, lastplayed: number, address: string, gamemode: string, workshopid: number, isanon: boolean, netversion: string, luaversion: string, localization: string, gmcategory: string):(stop: boolean) +---Called if the query has failed, called with the server IP address. +---@field CallbackFailed function +---Called when the query is finished. No arguments. +---@field Finished function +local ServerQueryData = {} diff --git a/custom/class.SkeletonConvertor.lua b/custom/class.SkeletonConvertor.lua new file mode 100644 index 00000000..4c134101 --- /dev/null +++ b/custom/class.SkeletonConvertor.lua @@ -0,0 +1,11 @@ +---@meta + +---@class ModelEntity +---@field GetModel fun(self: ModelEntity): string + +---@class SkeletonConvertor +---@field IsApplicable fun(self: SkeletonConvertor, ent: ModelEntity): boolean +---@field PositionTable? table +---@field AnglesTable? table +---@field SpecialVectorTable? table +---@field Complete? fun(self: SkeletonConvertor, sensor: table, ply: Player, rotation: Angle) diff --git a/custom/list.Set.lua b/custom/list.Set.lua new file mode 100644 index 00000000..e04b5dc9 --- /dev/null +++ b/custom/list.Set.lua @@ -0,0 +1,7 @@ +---@meta + +---@overload fun(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor) +---@param identifier string The identifier for the list. +---@param key any The key in the list. +---@param item any The value to set. +function list.Set(identifier, key, item) end From f96477f21afd9d4dc50f20fa19dc8c6c43955b0e Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:56:36 +0100 Subject: [PATCH 034/117] Fix Derma skin annotations --- __tests__/custom-annotations.spec.ts | 17 +++++++ custom/DButton.UpdateColours.lua | 6 +++ custom/DLabel.UpdateColours.lua | 6 +++ custom/class.SKIN.lua | 68 ++++++++++++++++++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 custom/DButton.UpdateColours.lua create mode 100644 custom/DLabel.UpdateColours.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 213b5995..8995831f 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -29,6 +29,7 @@ describe('custom and plugin annotation smoke checks', () => { const dCheckBoxSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetChecked.lua'), 'utf8'); const dCheckBoxLabelSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetValue.lua'), 'utf8'); const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); + const dButtonUpdateColours = fs.readFileSync(path.join(customRoot, 'DButton.UpdateColours.lua'), 'utf8'); const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); const dImageSetMatName = fs.readFileSync(path.join(customRoot, 'DImage.SetMatName.lua'), 'utf8'); const dMenuSetOpenSubMenu = fs.readFileSync(path.join(customRoot, 'DMenu.SetOpenSubMenu.lua'), 'utf8'); @@ -55,6 +56,7 @@ describe('custom and plugin annotation smoke checks', () => { const getNetworkedEntity = fs.readFileSync(path.join(customRoot, 'Entity.GetNetworkedEntity.lua'), 'utf8'); const getNetworked2Entity = fs.readFileSync(path.join(customRoot, 'Entity.GetNetworked2Entity.lua'), 'utf8'); const dPropertySheetAddSheet = fs.readFileSync(path.join(customRoot, 'DPropertySheet.AddSheet.lua'), 'utf8'); + const dLabelUpdateColours = fs.readFileSync(path.join(customRoot, 'DLabel.UpdateColours.lua'), 'utf8'); const ctrlColor = fs.readFileSync(path.join(customRoot, 'class.CtrlColor.lua'), 'utf8'); const controlPanelAddControl = fs.readFileSync(path.join(customRoot, 'ControlPanel.AddControl.lua'), 'utf8'); const entityCopyData = fs.readFileSync(path.join(customRoot, 'EntityCopyData.lua'), 'utf8'); @@ -76,9 +78,12 @@ describe('custom and plugin annotation smoke checks', () => { const skeletonConvertor = fs.readFileSync(path.join(customRoot, 'class.SkeletonConvertor.lua'), 'utf8'); const listSet = fs.readFileSync(path.join(customRoot, 'list.Set.lua'), 'utf8'); const serverQueryData = fs.readFileSync(path.join(customRoot, 'ServerQueryData.lua'), 'utf8'); + const skin = fs.readFileSync(path.join(customRoot, 'class.SKIN.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); + const generatedDButton = fs.readFileSync(path.join(process.cwd(), 'output', 'dbutton.lua'), 'utf8'); + const generatedDLabel = fs.readFileSync(path.join(process.cwd(), 'output', 'dlabel.lua'), 'utf8'); const generatedDMenu = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenu.lua'), 'utf8'); const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); const generatedDSlider = fs.readFileSync(path.join(process.cwd(), 'output', 'dslider.lua'), 'utf8'); @@ -107,6 +112,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(dCheckBoxSetChecked).toMatch(/---@param checked any/); expect(dCheckBoxLabelSetValue).toMatch(/---@param checked any/); expect(dCheckBoxLabelSetChecked).toMatch(/---@param checked any/); + expect(dButtonUpdateColours).toMatch(/---@param skin SKIN/); expect(dFileBrowserSetOpen).toMatch(/---@param open any/); expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); expect(dImageSetMatName).toMatch(/---@param mat\? string/); @@ -120,6 +126,8 @@ describe('custom and plugin annotation smoke checks', () => { expect(dTreeNodeSetShowFiles).toMatch(/---@param showFiles\? boolean/); expect(dTreeNodeSetWildCard).toMatch(/---@param wildcard\? string/); expect(generatedDImage).toMatch(/---@param mat\? string/); + expect(generatedDButton).toMatch(/---@param skin SKIN/); + expect(generatedDLabel).toMatch(/---@param skin SKIN/); expect(generatedDMenu).toMatch(/---@param item\? Panel/); expect(generatedDTreeNode).toMatch(/---@param expanded\? boolean/); expect(generatedDTreeNode).toMatch(/---@param expand\? boolean/); @@ -172,6 +180,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(dPropertySheetAddSheet).toMatch(/---@class DPropertySheetSheet/); expect(dPropertySheetAddSheet).toMatch(/---@field Tab DTab/); expect(dPropertySheetAddSheet).toMatch(/---@return DPropertySheetSheet/); + expect(dLabelUpdateColours).toMatch(/---@param skin SKIN/); expect(ctrlColor).toMatch(/---@class CtrlColor : Panel/); expect(ctrlColor).toMatch(/---@field Mixer DColorMixer/); expect(controlPanelAddControl).toMatch(/---@overload fun\(self: ControlPanel, type: "color", controlinfo: table\): CtrlColor/); @@ -227,6 +236,10 @@ describe('custom and plugin annotation smoke checks', () => { expect(skeletonConvertor).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); expect(listSet).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); expect(serverQueryData).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); + expect(skin).toMatch(/---@class SKINColoursProperties/); + expect(skin).toMatch(/---@field Column_Disabled Color/); + expect(skin).toMatch(/---@field Border Color/); + expect(skin).toMatch(/---@field Colours SKINColours/); expect(generatedCustomClasses).toMatch(/---@class phys_constraintsystem : Entity/); expect(generatedCustomClasses).toMatch(/---@class gmod_winch_controller : Entity/); expect(generatedCustomClasses).toMatch(/---@class hunter_flechette : Entity/); @@ -235,6 +248,10 @@ describe('custom and plugin annotation smoke checks', () => { expect(generatedCustomClasses).toMatch(/---@class \(partial\) URLLabel : Label/); expect(generatedCustomClasses).toMatch(/---@class ModelEntity/); expect(generatedCustomClasses).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); + expect(generatedCustomClasses).toMatch(/---@class SKINColoursProperties/); + expect(generatedCustomClasses).toMatch(/---@field Column_Disabled Color/); + expect(generatedCustomClasses).toMatch(/---@field Border Color/); + expect(generatedCustomClasses).toMatch(/---@field Colours SKINColours/); expect(generatedList).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); expect(generatedStructures).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); }); diff --git a/custom/DButton.UpdateColours.lua b/custom/DButton.UpdateColours.lua new file mode 100644 index 00000000..857db78d --- /dev/null +++ b/custom/DButton.UpdateColours.lua @@ -0,0 +1,6 @@ +---A hook called from within DLabel's PANEL:ApplySchemeSettings to determine the color of the text on display. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DButton:UpdateColours +---@param skin SKIN The active Derma skin table. +function DButton:UpdateColours(skin) end diff --git a/custom/DLabel.UpdateColours.lua b/custom/DLabel.UpdateColours.lua new file mode 100644 index 00000000..d7343255 --- /dev/null +++ b/custom/DLabel.UpdateColours.lua @@ -0,0 +1,6 @@ +---A hook called from within PANEL:ApplySchemeSettings to determine the color of the text on display. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DLabel:UpdateColours +---@param skin SKIN The active Derma skin table. +function DLabel:UpdateColours(skin) end diff --git a/custom/class.SKIN.lua b/custom/class.SKIN.lua index 98f2c7c3..63f14b66 100644 --- a/custom/class.SKIN.lua +++ b/custom/class.SKIN.lua @@ -2,8 +2,76 @@ --- Source: https://github.com/Facepunch/garrysmod/blob/b2bff902adf7f5b87ec543f873e74e3267e93f26/garrysmod/lua/skins/default.lua +---@class SKINColoursState +---@field Normal Color +---@field Hover Color +---@field Down Color +---@field Disabled Color + +---@class SKINColoursWindow +---@field TitleActive Color +---@field TitleInactive Color + +---@class SKINColoursTab +---@field Active SKINColoursState +---@field Inactive SKINColoursState + +---@class SKINColoursLabel +---@field Default Color +---@field Bright Color +---@field Dark Color +---@field Highlight Color + +---@class SKINColoursTree +---@field Lines Color +---@field Normal Color +---@field Hover Color +---@field Selected Color + +---@class SKINColoursProperties +---@field Line_Normal Color +---@field Line_Selected Color +---@field Line_Hover Color +---@field Title Color +---@field Column_Normal Color +---@field Column_Selected Color +---@field Column_Hover Color +---@field Column_Disabled Color +---@field Border Color +---@field Label_Normal Color +---@field Label_Selected Color +---@field Label_Hover Color +---@field Label_Disabled Color + +---@class SKINColoursCategoryLine +---@field Text Color +---@field Text_Hover Color +---@field Text_Selected Color +---@field Text_Disabled Color +---@field Button Color +---@field Button_Hover Color +---@field Button_Selected Color +---@field Button_Disabled Color + +---@class SKINColoursCategory +---@field Header Color +---@field Header_Closed Color +---@field Line SKINColoursCategoryLine +---@field LineAlt SKINColoursCategoryLine + +---@class SKINColours +---@field Window SKINColoursWindow +---@field Button SKINColoursState +---@field Tab SKINColoursTab +---@field Label SKINColoursLabel +---@field Tree SKINColoursTree +---@field Properties SKINColoursProperties +---@field Category SKINColoursCategory +---@field TooltipText Color + --- Active Derma skin table used by derma and GWEN. ---@class SKIN +---@field Colours SKINColours ---@field PaintPanel fun(self: SKIN, panel: Panel, w: number, h: number) ---@field PaintShadow fun(self: SKIN, panel: Panel, w: number, h: number) ---@field PaintFrame fun(self: SKIN, panel: Panel, w: number, h: number) From 3c3448fb1e22ef02ae6e1e14af303e32f88cc29c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 14:28:08 +0100 Subject: [PATCH 035/117] Fix SkeletonConvertor callback annotations --- __tests__/custom-annotations.spec.ts | 3 +++ custom/class.SkeletonConvertor.lua | 9 +++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 8995831f..9c0c2830 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -234,6 +234,8 @@ describe('custom and plugin annotation smoke checks', () => { expect(skeletonConvertor).toMatch(/---@field GetModel fun\(self: ModelEntity\): string/); expect(skeletonConvertor).toMatch(/---@class SkeletonConvertor/); expect(skeletonConvertor).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); + expect(skeletonConvertor).toMatch(/---@field PrePosition\? fun\(self: SkeletonConvertor, sensor: table\)/); + expect(skeletonConvertor).toMatch(/---@field Complete\? fun\(self: SkeletonConvertor, ply: Player, sensor: table, rotation: Angle, pos: table, ang: table\)/); expect(listSet).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); expect(serverQueryData).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); expect(skin).toMatch(/---@class SKINColoursProperties/); @@ -248,6 +250,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(generatedCustomClasses).toMatch(/---@class \(partial\) URLLabel : Label/); expect(generatedCustomClasses).toMatch(/---@class ModelEntity/); expect(generatedCustomClasses).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); + expect(generatedCustomClasses).toMatch(/---@field Complete\? fun\(self: SkeletonConvertor, ply: Player, sensor: table, rotation: Angle, pos: table, ang: table\)/); expect(generatedCustomClasses).toMatch(/---@class SKINColoursProperties/); expect(generatedCustomClasses).toMatch(/---@field Column_Disabled Color/); expect(generatedCustomClasses).toMatch(/---@field Border Color/); diff --git a/custom/class.SkeletonConvertor.lua b/custom/class.SkeletonConvertor.lua index 4c134101..875933e5 100644 --- a/custom/class.SkeletonConvertor.lua +++ b/custom/class.SkeletonConvertor.lua @@ -5,7 +5,8 @@ ---@class SkeletonConvertor ---@field IsApplicable fun(self: SkeletonConvertor, ent: ModelEntity): boolean ----@field PositionTable? table ----@field AnglesTable? table ----@field SpecialVectorTable? table ----@field Complete? fun(self: SkeletonConvertor, sensor: table, ply: Player, rotation: Angle) +---@field PrePosition? fun(self: SkeletonConvertor, sensor: table) +---@field PositionTable? table +---@field AnglesTable? table +---@field SpecialVectorTable? table +---@field Complete? fun(self: SkeletonConvertor, ply: Player, sensor: table, rotation: Angle, pos: table, ang: table) From ad8fb50b4251a2ecf7fe87d7120e30f001402eb7 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:38:16 +0100 Subject: [PATCH 036/117] Fix phys_hinge engine entity annotation --- custom/ents.Create.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/custom/ents.Create.lua b/custom/ents.Create.lua index 38bef88c..7e95a857 100644 --- a/custom/ents.Create.lua +++ b/custom/ents.Create.lua @@ -14,6 +14,7 @@ ---| "phys_bone_follower" ---| "phys_constraint" ---| "phys_constraintsystem" +---| "phys_hinge" ---| "phys_keepupright" ---| "phys_lengthconstraint" ---| "phys_magnet" From 41ef4cfd0919245632ef1e848875312d66a72e11 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:49:30 +0100 Subject: [PATCH 037/117] Fix widget axis entity annotations --- custom/class.EngineEntities.lua | 2 ++ custom/ents.Create.lua | 2 ++ 2 files changed, 4 insertions(+) diff --git a/custom/class.EngineEntities.lua b/custom/class.EngineEntities.lua index f83d3ebe..8b94207d 100644 --- a/custom/class.EngineEntities.lua +++ b/custom/class.EngineEntities.lua @@ -22,6 +22,8 @@ ---@class phys_torque : Entity ---@class point_viewcontrol : Entity ---@class ragdoll_motion : Entity +---@class widget_axis_arrow : Entity +---@class widget_axis_disc : Entity ---@class widget_bone : Entity ---@class widget_bones : Entity local EngineEntities = {} diff --git a/custom/ents.Create.lua b/custom/ents.Create.lua index 7e95a857..6d9228f0 100644 --- a/custom/ents.Create.lua +++ b/custom/ents.Create.lua @@ -25,6 +25,8 @@ ---| "phys_torque" ---| "point_viewcontrol" ---| "ragdoll_motion" +---| "widget_axis_arrow" +---| "widget_axis_disc" ---| "widget_bone" ---@overload fun(class: KnownEngineEntityClass): Entity ---@generic T : Entity From 74d90394af1899db697f5fe35ddfe1aab457a7f8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 18:17:52 +0100 Subject: [PATCH 038/117] Fix Derma_Anim return annotation --- custom/Global.Derma_Anim.lua | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 custom/Global.Derma_Anim.lua diff --git a/custom/Global.Derma_Anim.lua b/custom/Global.Derma_Anim.lua new file mode 100644 index 00000000..82abdf31 --- /dev/null +++ b/custom/Global.Derma_Anim.lua @@ -0,0 +1,40 @@ +---Runtime object returned by Derma_Anim. +---@realm client +---@realm menu +---@class DermaAnimation +---@field Name string +---@field Panel Panel +---@field Func fun(pnl: Panel, anim: DermaAnimation, delta: number, data: any) +---@field Running? boolean +---@field Started? boolean +---@field Finished? boolean +---@field Length? number +---@field StartTime? number +---@field EndTime? number +---@field Data? any +local DermaAnimation = {} + +---Runs the animation's frame callback if the animation is active. +function DermaAnimation:Run() end + +---Starts the animation. +---@param length number +---@param data? any +function DermaAnimation:Start(length, data) end + +---Stops the animation. +function DermaAnimation:Stop() end + +---Returns whether the animation is currently active. +---@return boolean? +function DermaAnimation:Active() end + +---Creates a new derma animation. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.Derma_Anim +---@param name string Name of the animation to create. +---@param panel Panel Panel to run the animation on. +---@param func fun(pnl: Panel, anim: DermaAnimation, delta: number, data: any) Function to call to process the animation. +---@return DermaAnimation +function _G.Derma_Anim(name, panel, func) end From 488f6d0947bf297a812d7ce701a73886fc78b5df Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 18:47:00 +0100 Subject: [PATCH 039/117] Fix VGUI panel annotation overrides --- custom/DCategoryList.Add.lua | 7 +++++++ custom/vgui.Create.lua | 1 + 2 files changed, 8 insertions(+) create mode 100644 custom/DCategoryList.Add.lua diff --git a/custom/DCategoryList.Add.lua b/custom/DCategoryList.Add.lua new file mode 100644 index 00000000..5debed2c --- /dev/null +++ b/custom/DCategoryList.Add.lua @@ -0,0 +1,7 @@ +---Adds a DCollapsibleCategory to the list. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DCategoryList:Add +---@param categoryName string The name of the category to add. +---@return (instance) DCollapsibleCategory # The created DCollapsibleCategory. +function DCategoryList:Add(categoryName) end diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua index 3f7d59af..fc078659 100644 --- a/custom/vgui.Create.lua +++ b/custom/vgui.Create.lua @@ -4,6 +4,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.Create ---@generic T: Panel +---@overload fun(classname: string, parent?: Panel, name?: string): Panel # Creates a panel from a dynamic class name. ---@[call_arg("gmod.vgui_panel", "reference")] ---@param classname `T` Classname of the panel to create. --- From 537a8d5617fe96b7fd005c6e6d9f3bb5c98eac26 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 19:32:57 +0100 Subject: [PATCH 040/117] Fix Vector color alias annotations --- custom/class.Vector.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/custom/class.Vector.lua b/custom/class.Vector.lua index 0bf2484a..d0a2bf52 100644 --- a/custom/class.Vector.lua +++ b/custom/class.Vector.lua @@ -3,8 +3,14 @@ --- --- Created by Global.Vector. ---@field x number +---@field X number +---@field r number ---@field y number +---@field Y number +---@field g number ---@field z number +---@field Z number +---@field b number ---@field [1] number ---@field [2] number ---@field [3] number From 8f3b422a3cef59b3983b5afda31b53c8c69ae091 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 12 Jun 2026 23:04:15 +0100 Subject: [PATCH 041/117] Fix workshop menu annotation overrides --- __tests__/cli-generate-lua.spec.ts | 72 ++++++++++++++++++++++++ __tests__/custom-annotations.spec.ts | 38 ++++++++++++- custom/engine.GetAddons.lua | 19 +++++++ custom/engine.GetUserContent.lua | 13 +++++ custom/steamworks.FileInfo.lua | 10 ++++ custom/steamworks.FileUserInfo.lua | 11 ++++ custom/steamworks.GetDownloadedItems.lua | 7 +++ custom/workshopfilebase.FillFileInfo.lua | 35 ++++++++++++ src/api-writer/glua-api-writer.ts | 45 +++++++++++++++ 9 files changed, 249 insertions(+), 1 deletion(-) create mode 100644 custom/engine.GetAddons.lua create mode 100644 custom/engine.GetUserContent.lua create mode 100644 custom/steamworks.FileInfo.lua create mode 100644 custom/steamworks.FileUserInfo.lua create mode 100644 custom/steamworks.GetDownloadedItems.lua create mode 100644 custom/workshopfilebase.FillFileInfo.lua diff --git a/__tests__/cli-generate-lua.spec.ts b/__tests__/cli-generate-lua.spec.ts index 6bb289c6..301f827e 100644 --- a/__tests__/cli-generate-lua.spec.ts +++ b/__tests__/cli-generate-lua.spec.ts @@ -106,6 +106,78 @@ describe('cli-generate-lua', () => { } }); + test('emits custom function overrides for missing wiki pages', () => { + const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-missing-overrides-')); + const outputPath = path.join(tmpRoot, 'output'); + const customOverridesPath = path.join(tmpRoot, 'custom'); + const steamworksDir = path.join(outputPath, 'steamworks'); + + fs.mkdirSync(steamworksDir, { recursive: true }); + fs.mkdirSync(customOverridesPath, { recursive: true }); + + fs.writeFileSync( + path.join(steamworksDir, 'library.json'), + JSON.stringify( + [ + { + type: 'library', + address: 'steamworks', + name: 'steamworks', + description: 'Steamworks related functions.', + realm: 'shared', + url: 'https://wiki.facepunch.com/gmod/steamworks', + }, + ], + null, + 2, + ), + 'utf8', + ); + + fs.writeFileSync( + path.join(customOverridesPath, 'steamworks.GetDownloadedItems.lua'), + [ + '---Returns a list of downloaded UGC item IDs.', + '---@return string[]', + 'function steamworks.GetDownloadedItems() end', + '', + ].join('\n'), + 'utf8', + ); + + fs.writeFileSync( + path.join(customOverridesPath, 'steamworks.FileUserInfo.lua'), + [ + '---Retrieves local file/user data for a Steam Workshop addon.', + '---@param workshopItemID string', + '---@param callback fun(info: SteamworksFileUserInfo)', + 'function steamworks.FileUserInfo(workshopItemID, callback) end', + '', + ].join('\n'), + 'utf8', + ); + + try { + const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + const result = spawnSync( + `${command} run generate-lua -- --output "${outputPath}" --custom-overrides "${customOverridesPath}"`, + [], + { + cwd: process.cwd(), + encoding: 'utf8', + shell: true, + }, + ); + + expect(result.status).toBe(0); + const steamworksLua = fs.readFileSync(path.join(outputPath, 'steamworks.lua'), 'utf8'); + expect(steamworksLua).toContain('function steamworks.GetDownloadedItems() end'); + expect(steamworksLua).toContain('function steamworks.FileUserInfo(workshopItemID, callback) end'); + } finally { + fs.rmSync(tmpRoot, { recursive: true, force: true }); + } + }); + test('applies typed Entity networked getter overrides', () => { const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gluals-generate-lua-')); const outputPath = path.join(tmpRoot, 'output'); diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 9c0c2830..a0c9adf3 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -92,6 +92,16 @@ describe('custom and plugin annotation smoke checks', () => { const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); + const generatedEngine = fs.readFileSync(path.join(process.cwd(), 'output', 'engine.lua'), 'utf8'); + const generatedSteamworks = fs.readFileSync(path.join(process.cwd(), 'output', 'steamworks.lua'), 'utf8'); + const generatedWorkshopFileBase = fs.readFileSync(path.join(process.cwd(), 'output', 'workshopfilebase.lua'), 'utf8'); + + const customEngineGetAddons = fs.readFileSync(path.join(customRoot, 'engine.GetAddons.lua'), 'utf8'); + const customEngineGetUserContent = fs.readFileSync(path.join(customRoot, 'engine.GetUserContent.lua'), 'utf8'); + const customSteamworksGetDownloadedItems = fs.readFileSync(path.join(customRoot, 'steamworks.GetDownloadedItems.lua'), 'utf8'); + const customSteamworksFileUserInfo = fs.readFileSync(path.join(customRoot, 'steamworks.FileUserInfo.lua'), 'utf8'); + const customSteamworksFileInfo = fs.readFileSync(path.join(customRoot, 'steamworks.FileInfo.lua'), 'utf8'); + const customWorkshopfileFillFileInfo = fs.readFileSync(path.join(customRoot, 'workshopfilebase.FillFileInfo.lua'), 'utf8'); expect(globals).toMatch(/---@alias GPlayer Player/); expect(globals).toMatch(/---@class NULL : Entity/); @@ -167,7 +177,6 @@ describe('custom and plugin annotation smoke checks', () => { expect(entsCreate).toMatch(/---@alias KnownEngineEntityClass/); expect(entsCreate).toMatch(/"phys_constraint"/); - expect(entsCreate).not.toMatch(/"phys_hinge"/); expect(entsCreate).not.toMatch(/"widget_bones"/); expect(entsCreate).toMatch(/---@overload fun\(class: KnownEngineEntityClass\): Entity/); expect(entsCreate).toMatch(/---@return \(instance\) T\|NULL/); @@ -257,6 +266,33 @@ describe('custom and plugin annotation smoke checks', () => { expect(generatedCustomClasses).toMatch(/---@field Colours SKINColours/); expect(generatedList).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); expect(generatedStructures).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); + + expect(customEngineGetAddons).toMatch(/---@class \(partial\) EngineAddon/); + expect(customEngineGetAddons).toMatch(/---@field wsid string/); + expect(customEngineGetAddons).toMatch(/---@return EngineAddon\[]/); + expect(generatedEngine).toMatch(/---@class \(partial\) EngineAddon/); + expect(generatedEngine).toMatch(/---@field wsid string/); + + expect(customEngineGetUserContent).toMatch(/---@class \(partial\) EngineUserContent/); + expect(customEngineGetUserContent).toMatch(/---@deprecated Used internally for in-game menus\./); + expect(customEngineGetUserContent).toMatch(/---@realm menu/); + expect(customEngineGetUserContent).toMatch(/---@return EngineUserContent\[]/); + expect(generatedEngine).toMatch(/---@return EngineUserContent\[]/); + + expect(customSteamworksGetDownloadedItems).toMatch(/---@return string\[]/); + expect(generatedSteamworks).toMatch(/---@return string\[]/); + + expect(customSteamworksFileInfo).toMatch(/UGCFileInfo\?/); + expect(generatedSteamworks).toMatch(/UGCFileInfo\?/); + + expect(customSteamworksFileUserInfo).toMatch(/---@class \(partial\) SteamworksFileUserInfo/); + expect(customSteamworksFileUserInfo).toMatch(/---@field error\? number/); + expect(customSteamworksFileUserInfo).toMatch(/---@param callback fun\(info: SteamworksFileUserInfo\)/); + + expect(customWorkshopfileFillFileInfo).toMatch(/---@class \(partial\) WorkshopFileInfoResults/); + expect(customWorkshopfileFillFileInfo).toMatch(/---@param results WorkshopFileInfoResults/); + expect(generatedWorkshopFileBase).toMatch(/---@class \(partial\) WorkshopFileInfoResults/); + expect(generatedWorkshopFileBase).toMatch(/---@param results WorkshopFileInfoResults/); }); test('iterator overrides expose typed generic-for values', () => { diff --git a/custom/engine.GetAddons.lua b/custom/engine.GetAddons.lua new file mode 100644 index 00000000..97f9fdc9 --- /dev/null +++ b/custom/engine.GetAddons.lua @@ -0,0 +1,19 @@ +---Returns a list of addons the player have subscribed to on the workshop. +--- +--- This list will also include "Floating" .gma addons that are mounted by the game, but not the folder addons. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/engine.GetAddons +---@class (partial) EngineAddon +---@field downloaded number The amount of bytes downloaded. +---@field models table List of models included in the addon. +---@field title string The addon title. +---@field file string The path to the mounted .gma file. +---@field mounted boolean Whether the addon is currently mounted. +---@field wsid string The workshop ID for non-local addons. +---@field size number The addon size in bytes. +---@field updated number Unix timestamp of the last update. +---@field tags string Comma-separated tag list. +---@field timeadded number Unix timestamp when the addon was added. +---@return EngineAddon[] # A table of addon entries. +function engine.GetAddons() end diff --git a/custom/engine.GetUserContent.lua b/custom/engine.GetUserContent.lua new file mode 100644 index 00000000..c4c828fc --- /dev/null +++ b/custom/engine.GetUserContent.lua @@ -0,0 +1,13 @@ +---Returns the UGC (demos, saves and dupes) the player have subscribed to on the workshop. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/engine.GetUserContent +---@deprecated Used internally for in-game menus. +---@class (partial) EngineUserContent +---@field title string The addon title. +---@field type string The content type. +---@field tags string Comma-separated tag list. +---@field wsid string The workshop ID for the subscribed content. +---@field timeadded number Unix timestamp when subscribed. +---@return EngineUserContent[] # Table of subscribed UGC rows. +function engine.GetUserContent() end diff --git a/custom/steamworks.FileInfo.lua b/custom/steamworks.FileInfo.lua new file mode 100644 index 00000000..f466f1d8 --- /dev/null +++ b/custom/steamworks.FileInfo.lua @@ -0,0 +1,10 @@ +---Retrieves info about supplied Steam Workshop addon. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/steamworks.FileInfo +---@param workshopItemID string The ID of Steam Workshop item. +---@param resultCallback fun(data: UGCFileInfo?) The function to process retrieved data. +--- +--- Function argument(s): +--- * table `data` - The data about the item, if the request succeeded, `nil` otherwise. See Structures/UGCFileInfo. +function steamworks.FileInfo(workshopItemID, resultCallback) end diff --git a/custom/steamworks.FileUserInfo.lua b/custom/steamworks.FileUserInfo.lua new file mode 100644 index 00000000..55a9a917 --- /dev/null +++ b/custom/steamworks.FileUserInfo.lua @@ -0,0 +1,11 @@ +---Retrieves local file/user data for a Steam Workshop addon. +---@realm client +---@realm menu +---@param workshopItemID string The ID of Steam Workshop item. +---@param callback fun(info: SteamworksFileUserInfo) The function to process the returned info. +---@deprecated Used internally for in-game menus. +function steamworks.FileUserInfo(workshopItemID, callback) end + +---@class (partial) SteamworksFileUserInfo +---@field error? number Error code from steamworks, if any. +local SteamworksFileUserInfo = {} diff --git a/custom/steamworks.GetDownloadedItems.lua b/custom/steamworks.GetDownloadedItems.lua new file mode 100644 index 00000000..02bc0723 --- /dev/null +++ b/custom/steamworks.GetDownloadedItems.lua @@ -0,0 +1,7 @@ +---Returns a list of downloaded UGC item IDs. +--- +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +---@realm client +---@realm menu +---@return string[] # A list of workshop item IDs. +function steamworks.GetDownloadedItems() end diff --git a/custom/workshopfilebase.FillFileInfo.lua b/custom/workshopfilebase.FillFileInfo.lua new file mode 100644 index 00000000..3192a873 --- /dev/null +++ b/custom/workshopfilebase.FillFileInfo.lua @@ -0,0 +1,35 @@ +---@class (partial) WorkshopFileInfoEntry +---@field downloaded number The amount of bytes downloaded. +---@field models table Model table list. +---@field title string The addon title. +---@field file string Local addon file path when available. +---@field mounted boolean Whether the addon is mounted. +---@field wsid string The workshop ID or negative local addon key. +---@field size number Addon file size. +---@field updated number Last update timestamp. +---@field tags string Comma-separated tags. +---@field timeadded number Time the addon was added. +local WorkshopFileInfoEntry = {} + +---@class (partial) WorkshopUserContentEntry +---@field title string The content title. +---@field type string The content type. +---@field tags string Comma-separated tags. +---@field wsid string The workshop ID. +---@field timeadded number Time the content was added. +local WorkshopUserContentEntry = {} + +---@class (partial) WorkshopFileInfoResults +---@field results string[] The results IDs for this page. +---@field otherresults string[] All result IDs before pagination. +---@field totalresults number Total number of matching results. +---@field extraresults table Additional row metadata. +local WorkshopFileInfoResults = {} + +---Updates the set HTML panel with the newly fetched results +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/WorkshopFileBase:FillFileInfo +---@param results WorkshopFileInfoResults The result payload. +---@param isUGC? boolean Skips first x results. +function WorkshopFileBase:FillFileInfo(results, isUGC) end diff --git a/src/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts index a9b0a553..9c86288c 100644 --- a/src/api-writer/glua-api-writer.ts +++ b/src/api-writer/glua-api-writer.ts @@ -516,6 +516,20 @@ export class GluaApiWriter { } public writeToDisk() { + const usedOverrides = new Set(); + const moduleFileByName = new Map(); + + for (const [filePath, pages] of this.files) { + const baseName = filePath.split(/[\\/]/).pop() ?? ''; + if (baseName.endsWith('.lua')) { + moduleFileByName.set(baseName.slice(0, -4), filePath); + } + + pages.forEach(({ page }) => { + usedOverrides.add(safeFileName(page.address, '.')); + }); + } + // Process module files first so that class overrides with corresponding wiki // pages are emitted inline (via writeClassStart) alongside their methods. this.files.forEach((pages: IndexedWikiPage[], filePath: string) => { @@ -526,6 +540,37 @@ export class GluaApiWriter { } }); + const orphanFunctionOverrides = new Map(); + + for (const [pageAddress, override] of this.pageOverrides.entries()) { + if (usedOverrides.has(pageAddress)) continue; + if (pageAddress.startsWith('class.')) continue; + + const moduleMatch = pageAddress.match(/^([^.]+)\./); + if (!moduleMatch) continue; + + const moduleFilePath = moduleFileByName.get(moduleMatch[1]); + if (!moduleFilePath) continue; + + const current = orphanFunctionOverrides.get(moduleFilePath) ?? []; + current.push(override.endsWith('\n') ? override : `${override}\n`); + orphanFunctionOverrides.set(moduleFilePath, current); + } + + for (const [moduleFilePath, overrides] of orphanFunctionOverrides) { + if (overrides.length === 0) continue; + + const joinedOverrides = overrides.join('\n'); + + if (fs.existsSync(moduleFilePath)) { + const existing = fs.readFileSync(moduleFilePath, 'utf-8'); + const separator = existing.endsWith('\n') ? '' : '\n'; + fs.appendFileSync(moduleFilePath, `${separator}\n${joinedOverrides}`); + } else { + fs.writeFileSync(moduleFilePath, ['---@meta', '', ...joinedOverrides.split('\n')].join('\n')); + } + } + // Then, emit any class.* overrides that weren't triggered by wiki pages. // These are truly orphan classes with no corresponding wiki module. const orphanClassOverrides: string[] = []; From 89ce174ab392a18b4b7ab381c83d4a33b9e0dd26 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:10:03 +0100 Subject: [PATCH 042/117] Fix DPropertySheet, DTab, and workshop file shape annotations --- custom/DPropertySheet.GetActiveTab.lua | 5 +++++ custom/DPropertySheet.GetItems.lua | 5 +++++ custom/DPropertySheet.SetActiveTab.lua | 5 +++++ custom/DTab.GetPropertySheet.lua | 6 ++++++ custom/DTab.SetPropertySheet.lua | 5 +++++ custom/DTab.Setup.lua | 5 +++++ custom/class.DModelPanel.lua | 17 +++++++++++++++++ custom/class.DPropertySheet.lua | 3 ++- custom/workshopfilebase.FillFileInfo.lua | 1 + 9 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 custom/DPropertySheet.GetActiveTab.lua create mode 100644 custom/DPropertySheet.GetItems.lua create mode 100644 custom/DPropertySheet.SetActiveTab.lua create mode 100644 custom/DTab.GetPropertySheet.lua create mode 100644 custom/DTab.SetPropertySheet.lua create mode 100644 custom/DTab.Setup.lua create mode 100644 custom/class.DModelPanel.lua diff --git a/custom/DPropertySheet.GetActiveTab.lua b/custom/DPropertySheet.GetActiveTab.lua new file mode 100644 index 00000000..4cf8346a --- /dev/null +++ b/custom/DPropertySheet.GetActiveTab.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DPropertySheet:GetActiveTab +---@return DTab # The active [DTab](https://wiki.facepunch.com/gmod/DTab). +function DPropertySheet:GetActiveTab() end diff --git a/custom/DPropertySheet.GetItems.lua b/custom/DPropertySheet.GetItems.lua new file mode 100644 index 00000000..2ecbf3e7 --- /dev/null +++ b/custom/DPropertySheet.GetItems.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DPropertySheet:GetItems +---@return DPropertySheetSheet[] # All tab entries on this property sheet. +function DPropertySheet:GetItems() end diff --git a/custom/DPropertySheet.SetActiveTab.lua b/custom/DPropertySheet.SetActiveTab.lua new file mode 100644 index 00000000..854a5e84 --- /dev/null +++ b/custom/DPropertySheet.SetActiveTab.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DPropertySheet:SetActiveTab +---@param tab DTab The tab to make active. +function DPropertySheet:SetActiveTab(tab) end diff --git a/custom/DTab.GetPropertySheet.lua b/custom/DTab.GetPropertySheet.lua new file mode 100644 index 00000000..4cb567ef --- /dev/null +++ b/custom/DTab.GetPropertySheet.lua @@ -0,0 +1,6 @@ +---The [DPropertySheet](https://wiki.facepunch.com/gmod/DPropertySheet) this tab belongs to. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTab:GetPropertySheet +---@return DPropertySheet # The property sheet owning this tab. +function DTab:GetPropertySheet() end diff --git a/custom/DTab.SetPropertySheet.lua b/custom/DTab.SetPropertySheet.lua new file mode 100644 index 00000000..4336e7a7 --- /dev/null +++ b/custom/DTab.SetPropertySheet.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTab:SetPropertySheet +---@param pnl DPropertySheet The DPropertySheet to set for this tab. +function DTab:SetPropertySheet(pnl) end diff --git a/custom/DTab.Setup.lua b/custom/DTab.Setup.lua new file mode 100644 index 00000000..9202e172 --- /dev/null +++ b/custom/DTab.Setup.lua @@ -0,0 +1,5 @@ +---@param label string The label shown for the tab. +---@param pnl DPropertySheet The parent sheet to attach this tab to. +---@param contents Panel The tab contents panel. +---@param icon string The icon path. +function DTab:Setup(label, pnl, contents, icon) end diff --git a/custom/class.DModelPanel.lua b/custom/class.DModelPanel.lua new file mode 100644 index 00000000..bdc4f536 --- /dev/null +++ b/custom/class.DModelPanel.lua @@ -0,0 +1,17 @@ +---@class (partial) DModelPanel : DButton +---@field Entity CSEnt? The panel's internal clientside entity. +---@field vCamPos Vector The camera position used for rendering. +---@field aLookAngle Angle The camera look angle. +---@field fFOV number The camera field of view. +---@field vLookatPos? Vector Point the camera is looking at. +---@field colAmbientLight? Color Ambient lighting color. +---@field colColor? Color Color applied to the rendered model. +---@field bAnimated boolean Whether the model entity is animated. +---@field m_fAnimSpeed? number The animation speed. +---@field m_bFirstPerson? boolean Whether first-person controls are enabled. +---@field m_iMoveScale? number Movement scale for first-person controls. +---@field DirectionalLight? table Directional lights indexed by BOX_*. +---@field FarZ? number Far clip plane distance. +---@field Scene? CSEnt Scene instance. +---@field LastPaint? number Time of last paint. +local DModelPanel = {} diff --git a/custom/class.DPropertySheet.lua b/custom/class.DPropertySheet.lua index e15b4506..2154fdb3 100644 --- a/custom/class.DPropertySheet.lua +++ b/custom/class.DPropertySheet.lua @@ -1,4 +1,5 @@ --- A tab oriented control where you can create multiple tabs with items within. Used mainly for organization. ---@class DPropertySheet : Panel ---@field tabScroller DHorizontalScroller The internal horizontal scroller that manages tab positioning. -local DPropertySheet = {} \ No newline at end of file +---@field Items DPropertySheetSheet[] The list of tabs added to this sheet. +local DPropertySheet = {} diff --git a/custom/workshopfilebase.FillFileInfo.lua b/custom/workshopfilebase.FillFileInfo.lua index 3192a873..6cf01dcc 100644 --- a/custom/workshopfilebase.FillFileInfo.lua +++ b/custom/workshopfilebase.FillFileInfo.lua @@ -17,6 +17,7 @@ local WorkshopFileInfoEntry = {} ---@field tags string Comma-separated tags. ---@field wsid string The workshop ID. ---@field timeadded number Time the content was added. +---@field file? string Local addon file path when available. local WorkshopUserContentEntry = {} ---@class (partial) WorkshopFileInfoResults From 920f3dbe64b882efbc66fbc7ee11c935169b1b33 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:16:50 +0100 Subject: [PATCH 043/117] refine DModelPanel field nullability --- custom/class.DModelPanel.lua | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/custom/class.DModelPanel.lua b/custom/class.DModelPanel.lua index bdc4f536..aac5bb65 100644 --- a/custom/class.DModelPanel.lua +++ b/custom/class.DModelPanel.lua @@ -1,17 +1,17 @@ ---@class (partial) DModelPanel : DButton ----@field Entity CSEnt? The panel's internal clientside entity. +---@field Entity CSEnt The panel's internal clientside entity. ---@field vCamPos Vector The camera position used for rendering. ---@field aLookAngle Angle The camera look angle. ---@field fFOV number The camera field of view. ----@field vLookatPos? Vector Point the camera is looking at. ----@field colAmbientLight? Color Ambient lighting color. ----@field colColor? Color Color applied to the rendered model. +---@field vLookatPos Vector Point the camera is looking at. +---@field colAmbientLight Color Ambient lighting color. +---@field colColor Color Color applied to the rendered model. ---@field bAnimated boolean Whether the model entity is animated. ---@field m_fAnimSpeed? number The animation speed. ---@field m_bFirstPerson? boolean Whether first-person controls are enabled. ---@field m_iMoveScale? number Movement scale for first-person controls. ----@field DirectionalLight? table Directional lights indexed by BOX_*. ----@field FarZ? number Far clip plane distance. +---@field DirectionalLight table Directional lights indexed by BOX_*. +---@field FarZ number Far clip plane distance. ---@field Scene? CSEnt Scene instance. ----@field LastPaint? number Time of last paint. +---@field LastPaint number Time of last paint. local DModelPanel = {} From 6e7c3115a447b43e8d94611ce74a6a7cead2886f Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 00:56:23 +0100 Subject: [PATCH 044/117] fix: remove false-positive params from UGCPublishWindow:DoPublish annotation The wiki incorrectly documents DoPublish as taking (wsid, err) parameters. Those parameters actually belong to OnPublishFinished(wsId, err). DoPublish is the button click handler that validates inputs and initiates publishing - it takes zero parameters. The custom override corrects this false-positive annotation mismatch. --- custom/UGCPublishWindow.DoPublish.lua | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 custom/UGCPublishWindow.DoPublish.lua diff --git a/custom/UGCPublishWindow.DoPublish.lua b/custom/UGCPublishWindow.DoPublish.lua new file mode 100644 index 00000000..18540ee6 --- /dev/null +++ b/custom/UGCPublishWindow.DoPublish.lua @@ -0,0 +1,4 @@ +---Publishes the Item or throws an error if the Title or Tags are invalid +---@realm menu +---@source https://wiki.facepunch.com/gmod/UGCPublishWindow:DoPublish +function UGCPublishWindow:DoPublish() end From 5b21fe61a2a99c19089fea0e97d47a1ec9c247fe Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 08:44:54 +0100 Subject: [PATCH 045/117] Fix FormattedTime return overloads --- custom/string.FormattedTime.lua | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 custom/string.FormattedTime.lua diff --git a/custom/string.FormattedTime.lua b/custom/string.FormattedTime.lua new file mode 100644 index 00000000..516093d7 --- /dev/null +++ b/custom/string.FormattedTime.lua @@ -0,0 +1,13 @@ +---Formats the supplied number of seconds to the specified format. +--- +---When no format is supplied, this returns a FormattedTime table instead. +---@realm client +---@realm menu +---@realm server +---@source https://wiki.facepunch.com/gmod/string.FormattedTime +---@overload fun(float: number): FormattedTime +---@overload fun(float: number, format: nil): FormattedTime +---@param float number Number of seconds to format. +---@param format string The format string. If this is omitted, a FormattedTime table is returned instead. +---@return string # The formatted time string. +function string.FormattedTime(float, format) end From 659c9f2d82018613f3e9afc818a9d86f512a4f8a Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 08:55:12 +0100 Subject: [PATCH 046/117] Fix Player drive entity nil annotations --- custom/Player.SetDrivingEntity.lua | 11 +++++++++++ custom/Player.SetViewEntity.lua | 7 +++++++ 2 files changed, 18 insertions(+) create mode 100644 custom/Player.SetDrivingEntity.lua create mode 100644 custom/Player.SetViewEntity.lua diff --git a/custom/Player.SetDrivingEntity.lua b/custom/Player.SetDrivingEntity.lua new file mode 100644 index 00000000..ae4c9a4f --- /dev/null +++ b/custom/Player.SetDrivingEntity.lua @@ -0,0 +1,11 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Sets the driving entity and driving mode, or clears the driving entity when passed `nil`. +--- +--- Use [drive.PlayerStartDriving](https://wiki.facepunch.com/gmod/drive.PlayerStartDriving) instead, see [Entity Driving](https://wiki.facepunch.com/gmod/Entity_Driving). +---@realm shared +---@source https://wiki.facepunch.com/gmod/Player:SetDrivingEntity +---@overload fun(self: Player, drivingEntity: nil) +---@param drivingEntity Entity The entity the player should drive. +---@param drivingMode number The driving mode index. +function Player:SetDrivingEntity(drivingEntity, drivingMode) end diff --git a/custom/Player.SetViewEntity.lua b/custom/Player.SetViewEntity.lua new file mode 100644 index 00000000..6234356e --- /dev/null +++ b/custom/Player.SetViewEntity.lua @@ -0,0 +1,7 @@ +---Attaches the player's view to the position and angles of the specified entity. +--- +--- Passing `nil` clears the player's view entity. +---@realm server +---@source https://wiki.facepunch.com/gmod/Player:SetViewEntity +---@param viewEntity Entity|nil The entity to attach the player view to, or `nil` to clear it. +function Player:SetViewEntity(viewEntity) end From 417f58ce45bbbafa29733fde70a3004a32406360 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 09:39:59 +0100 Subject: [PATCH 047/117] Fix SetOnViewMaterial backup annotations --- custom/DImage.SetOnViewMaterial.lua | 7 +++++++ custom/DImageButton.SetOnViewMaterial.lua | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 custom/DImage.SetOnViewMaterial.lua create mode 100644 custom/DImageButton.SetOnViewMaterial.lua diff --git a/custom/DImage.SetOnViewMaterial.lua b/custom/DImage.SetOnViewMaterial.lua new file mode 100644 index 00000000..73cf944d --- /dev/null +++ b/custom/DImage.SetOnViewMaterial.lua @@ -0,0 +1,7 @@ +---Sets the image from a material path shown when viewed as material. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DImage:SetOnViewMaterial +---@param mat string The material path to use. +---@param backupMat? string Optional fallback material path. +function DImage:SetOnViewMaterial(mat, backupMat) end diff --git a/custom/DImageButton.SetOnViewMaterial.lua b/custom/DImageButton.SetOnViewMaterial.lua new file mode 100644 index 00000000..6ef34cf4 --- /dev/null +++ b/custom/DImageButton.SetOnViewMaterial.lua @@ -0,0 +1,7 @@ +---Sets the image from a material path shown when viewed as material. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DImageButton:SetOnViewMaterial +---@param mat string The material path to use. +---@param backup? string Optional fallback material path. +function DImageButton:SetOnViewMaterial(mat, backup) end From 66c2ea003659fbec6bddfbccc88ef2d923362e14 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 09:53:05 +0100 Subject: [PATCH 048/117] Fix dragdrop receiver annotations --- custom/Panel.Receiver.lua | 8 ++++++++ custom/dragndrop.CallReceiverFunction.lua | 11 +++++++++++ 2 files changed, 19 insertions(+) create mode 100644 custom/Panel.Receiver.lua create mode 100644 custom/dragndrop.CallReceiverFunction.lua diff --git a/custom/Panel.Receiver.lua b/custom/Panel.Receiver.lua new file mode 100644 index 00000000..779efd23 --- /dev/null +++ b/custom/Panel.Receiver.lua @@ -0,0 +1,8 @@ +---Allows the panel to receive drag and drop events. Can be called multiple times with different names to receive multiple different draggable panel events. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:Receiver +---@param name string Name of DnD panels to receive. This is set on the drag'n'drop-able panels via Panel:Droppable. +---@param func fun(pnl: Panel, tbl: table, dropped: boolean, command: any, x: number, y: number) This function is called whenever a panel with valid name is hovering above and dropped on this panel. +---@param menu? table A table of commands to display as a menu if drag'n'drop was performed with a right click. +function Panel:Receiver(name, func, menu) end diff --git a/custom/dragndrop.CallReceiverFunction.lua b/custom/dragndrop.CallReceiverFunction.lua new file mode 100644 index 00000000..0bce84c6 --- /dev/null +++ b/custom/dragndrop.CallReceiverFunction.lua @@ -0,0 +1,11 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +---Calls the receiver function of hovered panel. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/dragndrop.CallReceiverFunction +---@param bDoDrop boolean true if the mouse was released, false if we right clicked. +---@param command? any The command value from the receiver menu, or nil. +---@param mx? number The local to the panel mouse cursor X position when the click happened. +---@param my? number The local to the panel mouse cursor Y position when the click happened. +function dragndrop.CallReceiverFunction(bDoDrop, command, mx, my) end From df3a9791886b584df4ee2c35466525054da50600 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 10:01:20 +0100 Subject: [PATCH 049/117] Fix VGUI optional callback annotations --- custom/DFileBrowser.ShowFolder.lua | 8 ++++++++ custom/DListView.OnClickLine.lua | 7 +++++++ custom/DTextEntry.OnTextChanged.lua | 10 ++++++++++ 3 files changed, 25 insertions(+) create mode 100644 custom/DFileBrowser.ShowFolder.lua create mode 100644 custom/DListView.OnClickLine.lua create mode 100644 custom/DTextEntry.OnTextChanged.lua diff --git a/custom/DFileBrowser.ShowFolder.lua b/custom/DFileBrowser.ShowFolder.lua new file mode 100644 index 00000000..f49569fc --- /dev/null +++ b/custom/DFileBrowser.ShowFolder.lua @@ -0,0 +1,8 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- Builds the file or icon list for the current directory. +--- +--- You should use [DFileBrowser:SetCurrentFolder](https://wiki.facepunch.com/gmod/DFileBrowser:SetCurrentFolder) to change the directory. +---@realm client +---@source https://wiki.facepunch.com/gmod/DFileBrowser:ShowFolder +---@param currentDir? string The directory to populate the list from. +function DFileBrowser:ShowFolder(currentDir) end diff --git a/custom/DListView.OnClickLine.lua b/custom/DListView.OnClickLine.lua new file mode 100644 index 00000000..208aab9d --- /dev/null +++ b/custom/DListView.OnClickLine.lua @@ -0,0 +1,7 @@ +---Called whenever a line is clicked. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView:OnClickLine +---@param line Panel The selected line. +---@param isSelected? boolean Boolean indicating whether the line is selected. +function DListView:OnClickLine(line, isSelected) end diff --git a/custom/DTextEntry.OnTextChanged.lua b/custom/DTextEntry.OnTextChanged.lua new file mode 100644 index 00000000..c9dd38ce --- /dev/null +++ b/custom/DTextEntry.OnTextChanged.lua @@ -0,0 +1,10 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Called internally when the text inside the [DTextEntry](https://wiki.facepunch.com/gmod/DTextEntry) changes. This is an implementation of [TextEntry:OnTextChanged](https://wiki.facepunch.com/gmod/TextEntry:OnTextChanged) +--- +--- You should not override this function. Use [DTextEntry:OnValueChange](https://wiki.facepunch.com/gmod/DTextEntry:OnValueChange) instead. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTextEntry:OnTextChanged +---@param noMenuRemoval? boolean Determines whether to remove the autocomplete menu (false) or not (true). +function DTextEntry:OnTextChanged(noMenuRemoval) end From e2ccee08fa064293d227c76ce443c9343d1225f6 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 10:17:41 +0100 Subject: [PATCH 050/117] Add low-risk runtime field annotations --- custom/class.DListView.lua | 3 +++ custom/class.SKIN.lua | 3 +++ custom/dragndrop.state.lua | 7 +++++++ custom/sql.m_strError.lua | 3 +++ 4 files changed, 16 insertions(+) create mode 100644 custom/class.DListView.lua create mode 100644 custom/dragndrop.state.lua create mode 100644 custom/sql.m_strError.lua diff --git a/custom/class.DListView.lua b/custom/class.DListView.lua new file mode 100644 index 00000000..04a71d1e --- /dev/null +++ b/custom/class.DListView.lua @@ -0,0 +1,3 @@ +---@class DListView : DPanel +---@field Sorted table Lines sorted by the current column/order. +local DListView = {} diff --git a/custom/class.SKIN.lua b/custom/class.SKIN.lua index 63f14b66..840a3481 100644 --- a/custom/class.SKIN.lua +++ b/custom/class.SKIN.lua @@ -71,6 +71,9 @@ --- Active Derma skin table used by derma and GWEN. ---@class SKIN +---@field Name? string Internal skin registry name assigned by derma.DefineSkin. +---@field Description? string Human-readable skin description assigned by derma.DefineSkin. +---@field Base? string Optional base skin name assigned by derma.DefineSkin. ---@field Colours SKINColours ---@field PaintPanel fun(self: SKIN, panel: Panel, w: number, h: number) ---@field PaintShadow fun(self: SKIN, panel: Panel, w: number, h: number) diff --git a/custom/dragndrop.state.lua b/custom/dragndrop.state.lua new file mode 100644 index 00000000..92e8bcb3 --- /dev/null +++ b/custom/dragndrop.state.lua @@ -0,0 +1,7 @@ +---Current local mouse X position tracked by dragndrop while dispatching receivers. +---@type number +dragndrop.m_MouseLocalX = nil + +---Current local mouse Y position tracked by dragndrop while dispatching receivers. +---@type number +dragndrop.m_MouseLocalY = nil diff --git a/custom/sql.m_strError.lua b/custom/sql.m_strError.lua new file mode 100644 index 00000000..1787867a --- /dev/null +++ b/custom/sql.m_strError.lua @@ -0,0 +1,3 @@ +---Last SQL error string, assigned by the engine DLL. +---@type string +sql.m_strError = nil From 79dde6647011ad8ecd870df24fb7360f9df2acc0 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 10:28:24 +0100 Subject: [PATCH 051/117] Add workshop singleton annotations --- custom/steamworks.SetFavorite.lua | 7 ++++ custom/workshopfilebase.dupes.lua | 56 +++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 custom/steamworks.SetFavorite.lua create mode 100644 custom/workshopfilebase.dupes.lua diff --git a/custom/steamworks.SetFavorite.lua b/custom/steamworks.SetFavorite.lua new file mode 100644 index 00000000..c4eca98c --- /dev/null +++ b/custom/steamworks.SetFavorite.lua @@ -0,0 +1,7 @@ +---Sets or clears a Steam Workshop item's favorite state. +--- +---**INTERNAL**: This is used internally by the menu HTML bridge. +---@realm menu +---@param workshopItemID string|number The ID of the Steam Workshop item. +---@param favorite boolean Whether the item should be favorited. +function steamworks.SetFavorite(workshopItemID, favorite) end diff --git a/custom/workshopfilebase.dupes.lua b/custom/workshopfilebase.dupes.lua new file mode 100644 index 00000000..b3aea346 --- /dev/null +++ b/custom/workshopfilebase.dupes.lua @@ -0,0 +1,56 @@ +---@class (partial) DupeWorkshopFileBase : WorkshopFileBase +---@field DownloadAndArm fun(wsid: string|number) Downloads and arms a subscribed dupe from the workshop. +---@field Arm fun(filename: string) Arms a local dupe file for placement. +local DupeWorkshopFileBase = {} + +---@class ws_dupe : DupeWorkshopFileBase +---Sandbox dupes workshop helper used by the menu HTML bridge. [(View Source)](https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L11) +ws_dupe = {} + +---Downloads and arms a subscribed dupe from the workshop. +--- +---**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 +---@param wsid string|number The workshop item ID. +function DupeWorkshopFileBase.DownloadAndArm(wsid) end + +---Downloads and arms a subscribed dupe from the workshop. +--- +---**INTERNAL**: This method is source-backed on the sandbox `ws_dupe` workshop helper. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 +---@param wsid string|number The workshop item ID. +function WorkshopFileBase:DownloadAndArm(wsid) end + +---Arms a local dupe file for placement. +--- +---**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 +---@param filename string The dupe file path. +function DupeWorkshopFileBase.Arm(filename) end + +---Arms a local dupe file for placement. +--- +---**INTERNAL**: This method is source-backed on the sandbox `ws_dupe` workshop helper. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 +---@param filename string The dupe file path. +function WorkshopFileBase:Arm(filename) end + +---Downloads and arms a subscribed dupe from the workshop. +--- +---**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 +---@param wsid string|number The workshop item ID. +function ws_dupe:DownloadAndArm(wsid) end + +---Arms a local dupe file for placement. +--- +---**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 +---@param filename string The dupe file path. +function ws_dupe:Arm(filename) end From bfb02540432a2c84437277cae9d0dda26d86df19 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 20:58:34 +0100 Subject: [PATCH 052/117] Fix DListView line panel column annotations --- custom/DListView_Line.SetColumnText.lua | 8 ++++++++ custom/DListView_Line.SetValue.lua | 8 ++++++++ 2 files changed, 16 insertions(+) create mode 100644 custom/DListView_Line.SetColumnText.lua create mode 100644 custom/DListView_Line.SetValue.lua diff --git a/custom/DListView_Line.SetColumnText.lua b/custom/DListView_Line.SetColumnText.lua new file mode 100644 index 00000000..c0c3b1f3 --- /dev/null +++ b/custom/DListView_Line.SetColumnText.lua @@ -0,0 +1,8 @@ +---Sets the string or panel held in the specified column of a [DListView_Line](https://wiki.facepunch.com/gmod/DListView_Line) panel. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView_Line:SetColumnText +---@param column number The number of the column to write the value to, starts with 1. +---@param value string|Panel Column text, or a panel to parent into the column. +---@return DLabel? label The DLabel in which the text was set when `value` is a string. +function DListView_Line:SetColumnText(column, value) end diff --git a/custom/DListView_Line.SetValue.lua b/custom/DListView_Line.SetValue.lua new file mode 100644 index 00000000..5813d6cc --- /dev/null +++ b/custom/DListView_Line.SetValue.lua @@ -0,0 +1,8 @@ +---Alias of [DListView_Line:SetColumnText](https://wiki.facepunch.com/gmod/DListView_Line:SetColumnText). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView_Line:SetValue +---@param column number The number of the column to write the value to, starts with 1. +---@param value string|Panel Column text, or a panel to parent into the column. +---@return DLabel? label The DLabel in which the text was set when `value` is a string. +function DListView_Line:SetValue(column, value) end From a51677b8b511beead478749661dbc373488a3382 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 13 Jun 2026 23:56:27 +0100 Subject: [PATCH 053/117] Add string.Comma and util.GetSunInfo override annotations --- custom/string.Comma.lua | 8 ++++++++ custom/util.GetSunInfo.lua | 5 +++++ 2 files changed, 13 insertions(+) create mode 100644 custom/string.Comma.lua create mode 100644 custom/util.GetSunInfo.lua diff --git a/custom/string.Comma.lua b/custom/string.Comma.lua new file mode 100644 index 00000000..cfd5967a --- /dev/null +++ b/custom/string.Comma.lua @@ -0,0 +1,8 @@ +---Inserts commas for every third digit of a given number or numeric string. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/string.Comma +---@param value number|string The input number or numeric string to commafy +---@param separator? string An optional string that will be used instead of the default comma. +---@return string # The commafied string +function string.Comma(value, separator) end diff --git a/custom/util.GetSunInfo.lua b/custom/util.GetSunInfo.lua new file mode 100644 index 00000000..8e5c59b8 --- /dev/null +++ b/custom/util.GetSunInfo.lua @@ -0,0 +1,5 @@ +---Gets information about the sun position and obstruction or nil if there is no sun. +---@realm client +---@source https://wiki.facepunch.com/gmod/util.GetSunInfo +---@return SunInfo? # The sun info, or nil if there is no sun. See Structures/SunInfo +function util.GetSunInfo() end From aa46c07bd58c49936bf4f5b028779956e5d5ba2c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 14 Jun 2026 00:17:18 +0100 Subject: [PATCH 054/117] Fix DTree_Node root and right-click annotations --- custom/DTree_Node.GetRoot.lua | 8 ++++++++ custom/dtree.DoRightClick.lua | 6 ++++++ 2 files changed, 14 insertions(+) create mode 100644 custom/DTree_Node.GetRoot.lua create mode 100644 custom/dtree.DoRightClick.lua diff --git a/custom/DTree_Node.GetRoot.lua b/custom/DTree_Node.GetRoot.lua new file mode 100644 index 00000000..106a2b23 --- /dev/null +++ b/custom/DTree_Node.GetRoot.lua @@ -0,0 +1,8 @@ +---Returns the root node, the DTree this node is under. +--- +--- See also [DTree_Node:GetParentNode](https://wiki.facepunch.com/gmod/DTree_Node:GetParentNode). +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:GetRoot +---@return DTree # The root DTree. +function DTree_Node:GetRoot() end diff --git a/custom/dtree.DoRightClick.lua b/custom/dtree.DoRightClick.lua new file mode 100644 index 00000000..9b3a15be --- /dev/null +++ b/custom/dtree.DoRightClick.lua @@ -0,0 +1,6 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree.lua +---@param node DTree_Node The node that was right-clicked. +---@return boolean # Return true to handle the right-click. +function DTree:DoRightClick(node) end From 3b2164dcc95919ba5806ff54acbf91013e932d2f Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 14 Jun 2026 02:39:40 +0100 Subject: [PATCH 055/117] Add Global.LoadPresets override annotation --- custom/Global.LoadPresets.lua | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 custom/Global.LoadPresets.lua diff --git a/custom/Global.LoadPresets.lua b/custom/Global.LoadPresets.lua new file mode 100644 index 00000000..1062c8ce --- /dev/null +++ b/custom/Global.LoadPresets.lua @@ -0,0 +1,9 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Loads all preset settings for the [presets](https://wiki.facepunch.com/gmod/presets) and returns them in a table +---@realm client +---@source https://wiki.facepunch.com/gmod/Global.LoadPresets +---@class GmodPresets: table +--- +---@return GmodPresets # Preset data +function _G.LoadPresets() end From 220027ebf0aebbf0086335ca20307bad6d91d9ab Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 14 Jun 2026 09:06:26 +0100 Subject: [PATCH 056/117] Add DMenu:AddSpacer return override --- custom/DMenu.AddSpacer.lua | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 custom/DMenu.AddSpacer.lua diff --git a/custom/DMenu.AddSpacer.lua b/custom/DMenu.AddSpacer.lua new file mode 100644 index 00000000..0d94fe23 --- /dev/null +++ b/custom/DMenu.AddSpacer.lua @@ -0,0 +1,6 @@ +---Adds a spacer to the DMenu. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DMenu:AddSpacer +---@return (instance) DPanel #The created spacer panel. +function DMenu:AddSpacer() end From a1e248f96e99d0bcb25d24670ce08451fa7a59ea Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 14 Jun 2026 11:11:20 +0100 Subject: [PATCH 057/117] Add drive and DrawBloom annotation overrides --- custom/Global.DrawBloom.lua | 13 +++++++++++++ custom/class.DriveMethod.lua | 21 +++++++++++++++++++++ custom/drive.GetMethod.lua | 6 ++++++ 3 files changed, 40 insertions(+) create mode 100644 custom/Global.DrawBloom.lua create mode 100644 custom/class.DriveMethod.lua create mode 100644 custom/drive.GetMethod.lua diff --git a/custom/Global.DrawBloom.lua b/custom/Global.DrawBloom.lua new file mode 100644 index 00000000..2c64bdbe --- /dev/null +++ b/custom/Global.DrawBloom.lua @@ -0,0 +1,13 @@ +---Draws the bloom post-processing effect. +---@realm client +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/postprocess/bloom.lua +---@param darken number +---@param multiply number +---@param sizex number +---@param sizey number +---@param passes number +---@param color number +---@param colr number +---@param colg number +---@param colb number +function _G.DrawBloom(darken, multiply, sizex, sizey, passes, color, colr, colg, colb) end diff --git a/custom/class.DriveMethod.lua b/custom/class.DriveMethod.lua new file mode 100644 index 00000000..0bf24493 --- /dev/null +++ b/custom/class.DriveMethod.lua @@ -0,0 +1,21 @@ +---@meta + +---Runtime drive mode table returned by drive.GetMethod. +--- +--- Source: https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/drive/drive_base.lua +---@class DriveMethod +---@field Entity Entity Driven entity. +---@field Player Player Driving player. +---@field ModeID number Network string ID of the active drive mode. +---@field StopDriving? boolean Set by DriveMethod:Stop to stop driving after FinishMove. +---@field Init fun(self: DriveMethod, cmd?: CUserCmd) +---@field SetupControls fun(self: DriveMethod, cmd: CUserCmd) +---@field StartMove fun(self: DriveMethod, mv: CMoveData, cmd: CUserCmd) +---@field Move fun(self: DriveMethod, mv: CMoveData) +---@field FinishMove fun(self: DriveMethod, mv: CMoveData) +---@field CalcView fun(self: DriveMethod, view: ViewData) +---@field CalcView_ThirdPerson fun(self: DriveMethod, view: ViewData, dist: number, hullsize: number, entityfilter: Entity) +local DriveMethod = {} + +---Call this in your drive method at any point to stop driving. +function DriveMethod:Stop() end diff --git a/custom/drive.GetMethod.lua b/custom/drive.GetMethod.lua new file mode 100644 index 00000000..1a8cffd1 --- /dev/null +++ b/custom/drive.GetMethod.lua @@ -0,0 +1,6 @@ +---Gets the active drive method table for a player, if the player is currently driving. +---@realm shared +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/modules/drive.lua +---@param ply Player +---@return DriveMethod? +function drive.GetMethod(ply) end From f90a2cd07ec775585bd9c13075c6b51b94dbc668 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Wed, 17 Jun 2026 00:04:32 +0100 Subject: [PATCH 058/117] Add loader annotation overrides --- custom/Global.AddCSLuaFile.lua | 6 ++++++ custom/Global.IncludeCS.lua | 8 ++++++++ custom/Global.include.lua | 8 ++++++++ custom/Global.require.lua | 7 +++++++ custom/file.Find.lua | 12 ++++++++++++ 5 files changed, 41 insertions(+) create mode 100644 custom/Global.AddCSLuaFile.lua create mode 100644 custom/Global.IncludeCS.lua create mode 100644 custom/Global.include.lua create mode 100644 custom/Global.require.lua create mode 100644 custom/file.Find.lua diff --git a/custom/Global.AddCSLuaFile.lua b/custom/Global.AddCSLuaFile.lua new file mode 100644 index 00000000..39735316 --- /dev/null +++ b/custom/Global.AddCSLuaFile.lua @@ -0,0 +1,6 @@ +---Marks a Lua file to be sent to clients. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Global.AddCSLuaFile +---@[call_arg("gmod.load", "addcsluafile")] +---@param fileName? string The file to send. +function AddCSLuaFile(fileName) end \ No newline at end of file diff --git a/custom/Global.IncludeCS.lua b/custom/Global.IncludeCS.lua new file mode 100644 index 00000000..f8fe8292 --- /dev/null +++ b/custom/Global.IncludeCS.lua @@ -0,0 +1,8 @@ +---Includes a Lua file on the client and sends it from the server. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.IncludeCS +---@[call_arg("gmod.load", "includecs")] +---@param fileName string The file to include and send. +---@return ... +function IncludeCS(fileName) end \ No newline at end of file diff --git a/custom/Global.include.lua b/custom/Global.include.lua new file mode 100644 index 00000000..a4109dab --- /dev/null +++ b/custom/Global.include.lua @@ -0,0 +1,8 @@ +---Executes a Lua file. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.include +---@[call_arg("gmod.load", "include")] +---@param fileName string The file to include. +---@return ... +function include(fileName) end \ No newline at end of file diff --git a/custom/Global.require.lua b/custom/Global.require.lua new file mode 100644 index 00000000..98d86d60 --- /dev/null +++ b/custom/Global.require.lua @@ -0,0 +1,7 @@ +---Loads a binary or Lua module. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.require +---@[call_arg("gmod.load", "require")] +---@param moduleName string The module name. +function require(moduleName) end \ No newline at end of file diff --git a/custom/file.Find.lua b/custom/file.Find.lua new file mode 100644 index 00000000..0c80c68e --- /dev/null +++ b/custom/file.Find.lua @@ -0,0 +1,12 @@ +---Returns files and folders matching a wildcard in the requested search path. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/file.Find +---@[call_arg("gmod.file_find", "glob")] +---@param name string The wildcard pattern to search for. +---@[call_arg("gmod.file_find", "search_path")] +---@param path string The search path to look in. +---@param sorting? string The sorting mode to use. +---@return string[] files # Matching file names. +---@return string[] directories # Matching directory names. +function file.Find(name, path, sorting) end \ No newline at end of file From de0b95142f557e50003f087c329251e694280ed8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 20 Jun 2026 21:21:13 +0100 Subject: [PATCH 059/117] Add stable and prerelease annotation streams --- .github/workflows/release-gluals.yml | 75 +++++++++++++++++++++++----- README.md | 46 ++++++++--------- 2 files changed, 83 insertions(+), 38 deletions(-) diff --git a/.github/workflows/release-gluals.yml b/.github/workflows/release-gluals.yml index 689d87cc..1fdd8e6b 100644 --- a/.github/workflows/release-gluals.yml +++ b/.github/workflows/release-gluals.yml @@ -4,14 +4,29 @@ on: push: branches: - main + - beta schedule: - cron: "0 0 1 * *" jobs: release: + name: release (${{ matrix.channel }}) + if: github.event_name != 'push' || github.ref_name == matrix.source_branch permissions: write-all runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - channel: stable + source_branch: main + publish_base_branch: gluals-annotations + plugin_branch_prefix: gluals-annotations-plugin- + - channel: prerelease + source_branch: beta + publish_base_branch: gluals-annotations-prerelease + plugin_branch_prefix: gluals-annotations-prerelease-plugin- concurrency: - group: release-gluals-${{ github.ref }} + group: release-gluals-${{ matrix.source_branch }} cancel-in-progress: false steps: - uses: actions/checkout@v4 @@ -20,6 +35,28 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "22" + - name: Check out source branch content + env: + SOURCE_BRANCH: ${{ matrix.source_branch }} + CHANNEL: ${{ matrix.channel }} + run: | + set -e + + if ! git ls-remote --exit-code --heads origin "$SOURCE_BRANCH" >/dev/null 2>&1; then + echo "::error title=Missing source branch::Channel '$CHANNEL' requires source branch '$SOURCE_BRANCH' on origin. Create/push that branch before running scheduled or manual publishing." + exit 1 + fi + + git fetch --depth=1 origin "refs/heads/$SOURCE_BRANCH:refs/remotes/origin/$SOURCE_BRANCH" + git checkout -B "$SOURCE_BRANCH" "refs/remotes/origin/$SOURCE_BRANCH" + + { + echo "## release-gluals source channel" + echo "- channel: \`$CHANNEL\`" + echo "- source_branch: \`$SOURCE_BRANCH\`" + echo "- publish_base_branch: \`${{ matrix.publish_base_branch }}\`" + echo "- plugin_branch_prefix: \`${{ matrix.plugin_branch_prefix }}\`" + } >> "$GITHUB_STEP_SUMMARY" - name: Decide what to generate id: gate env: @@ -130,7 +167,7 @@ jobs: --indexOutput ./plugin/index.json \ --annotationsOutput ./output \ --pluginBundlesOutput ./output-plugins \ - --branchPrefix gluals-annotations-plugin- \ + --branchPrefix "${{ matrix.plugin_branch_prefix }}" \ --artifactManifest plugin.json \ --version "${{ steps.build_ts.outputs.value }}" \ --generatedAt "${{ steps.build_ts.outputs.value }}" @@ -145,6 +182,8 @@ jobs: id: payloads if: steps.gate.outputs.should_generate == 'true' env: + PUBLISH_BASE_BRANCH: ${{ matrix.publish_base_branch }} + PLUGIN_BRANCH_PREFIX: ${{ matrix.plugin_branch_prefix }} PUBLISH_BASE: ${{ steps.gate.outputs.publish_base }} PUBLISH_ALL_PLUGINS: ${{ steps.gate.outputs.publish_all_plugins }} PLUGINS_TO_PUBLISH: ${{ steps.gate.outputs.plugins_to_publish }} @@ -258,10 +297,10 @@ jobs: changed_plugins="" if [ "$PUBLISH_BASE" = "true" ]; then - if branch_has_changes "gluals-annotations" "$base_dir"; then + if branch_has_changes "$PUBLISH_BASE_BRANCH" "$base_dir"; then changed_base=true else - echo "Base annotations payload matches gluals-annotations." + echo "Base annotations payload matches $PUBLISH_BASE_BRANCH." fi else echo "Skipping base annotations comparison (no relevant source changes)." @@ -276,7 +315,7 @@ jobs: continue fi - plugin_branch="gluals-annotations-plugin-${plugin_id}" + plugin_branch="${PLUGIN_BRANCH_PREFIX}${plugin_id}" if branch_has_changes "$plugin_branch" "$plugin_dir"; then changed_plugins="$changed_plugins $plugin_id" else @@ -325,12 +364,16 @@ jobs: " - name: Tag latest scrape revision if: steps.payloads.outputs.should_publish == 'true' + env: + SOURCE_BRANCH: ${{ matrix.source_branch }} run: | build_tag=$(echo "${{ steps.build_ts.outputs.value }}" | sed 's/:/-/g' | sed 's/T/_/' | sed 's/Z//') - tag="$build_tag" + # Include the source branch in every tag so stable/prerelease matrix jobs + # are unique by construction even when they share the same timestamp. + tag="${build_tag}-${SOURCE_BRANCH}" if git rev-parse -q --verify "refs/tags/$tag" >/dev/null 2>&1; then echo "Tag $tag already exists. Falling back to build run number." - tag="${build_tag}-${GITHUB_RUN_NUMBER}" + tag="${build_tag}-${SOURCE_BRANCH}-${GITHUB_RUN_NUMBER}" fi git tag "$tag" git push origin "refs/tags/$tag" @@ -339,6 +382,8 @@ jobs: env: BASE_DIR: ${{ steps.payloads.outputs.base_dir }} PLUGIN_STAGE_DIR: ${{ steps.payloads.outputs.plugin_stage_dir }} + PUBLISH_BASE_BRANCH: ${{ matrix.publish_base_branch }} + PLUGIN_BRANCH_PREFIX: ${{ matrix.plugin_branch_prefix }} CHANGED_BASE: ${{ steps.payloads.outputs.changed_base }} CHANGED_PLUGINS: ${{ steps.payloads.outputs.changed_plugins }} run: | @@ -352,20 +397,26 @@ jobs: local source_dir="$2" local commit_message="$3" - git checkout --orphan "$branch_name" - git rm -rf . + if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then + git fetch --depth=1 origin "refs/heads/$branch_name:refs/remotes/origin/$branch_name" + git checkout -B "$branch_name" "refs/remotes/origin/$branch_name" + else + git checkout --orphan "$branch_name" + fi + + git rm -rf . || true git clean -fdx cp -R "$source_dir/." . git add -A git commit -m "$commit_message" - git push -f origin "$branch_name" + git push origin "$branch_name" } now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" if [ "$CHANGED_BASE" = "true" ]; then - publish_branch "gluals-annotations" "$BASE_DIR" "Update GLuaLS annotations - $now" + publish_branch "$PUBLISH_BASE_BRANCH" "$BASE_DIR" "Update GLuaLS annotations - $now" else echo "Skipping base annotations branch (generated output unchanged)." fi @@ -377,6 +428,6 @@ jobs: exit 1 fi - plugin_branch="gluals-annotations-plugin-${plugin_id}" + plugin_branch="${PLUGIN_BRANCH_PREFIX}${plugin_id}" publish_branch "$plugin_branch" "$plugin_dir" "Update ${plugin_id} plugin annotations - $now" done diff --git a/README.md b/README.md index 5bce35d1..d2ed4076 100644 --- a/README.md +++ b/README.md @@ -5,14 +5,20 @@ Automatically generates GLuaLS annotations for Garry's Mod API by scraping the [ Plugins are currently WIP and are not used. **Note**: This repository is part of the GMod language server infrastructure. -Annotations are automatically downloaded by the VSCode extension from the `gluals-annotations` branch - manual setup is not required. +Annotations are automatically downloaded by the VSCode extension from generated publish branches - manual setup is not required. -## Workflow +`npm run scrape-wiki` scrapes and normalizes wiki pages, then writes Lua annotations into `output/`. -1. `npm run wiki-check-changed` checks whether upstream wiki content changed since the latest scrape tag. -2. `npm run scrape-wiki` scrapes and normalizes wiki pages, then writes Lua annotations into `output/`. -3. `npm test` validates scraper and writer behavior. -4. CI formats generated output and publishes annotations to the `gluals-annotations` branch for extension consumption. +## Branches + +- `main` is for stable annotations. + - Publishes base annotations to `gluals-annotations` + - Publishes plugin annotations to `gluals-annotations-plugin-` +- `beta` is for pre-release annotations. + - Publishes base annotations to `gluals-annotations-prerelease` + - Publishes plugin annotations to `gluals-annotations-prerelease-plugin-` +- Do not edit the generated output branches by hand. Make changes on `main` or `beta` instead. +- Generated output branches keep their old commits, so users can choose an older annotation commit if needed. ## Development Setup @@ -38,7 +44,7 @@ Run tests: npm test ``` -Build release artifact locally (legacy, not required for branch-based consumption): +Build a release ZIP locally (old workflow, not needed for normal extension downloads): ```bash npm run pack-release @@ -46,32 +52,20 @@ npm run pack-release ## Local Development Testing -For local language server testing, generate annotations and point your workspace library to `./output/`: - -```json -{ - "workspace": { - "library": [ - "./output" - ] - } -} -``` - -**Note**: The VSCode extension automatically downloads production annotations from the `gluals-annotations` branch. The above configuration is only needed for testing local changes during development. +For local language server testing, use the override setting in the VSCode extension to point annotations to your generated local output folder. ## Repository Layout - `src/scrapers/` - GMod wiki scraping and normalization - `src/api-writer/` - EmmyLua/LuaCATS annotation generation -- `plugin/` - framework plugin metadata + gluarc fragments consumed by the VSCode extension -- `custom/` - manual overrides merged during generation -- `output/` - generated annotation files (published to `gluals-annotations` branch) +- `plugin/` - framework plugin data and gluarc files used by the VSCode extension +- `custom/` - manual fixes added during generation +- `output/` - generated annotation files before they are published -## Plugin Metadata Notes +## Plugin notes -The VSCode extension loads plugin metadata from the annotation bundle (`plugin/index.json` + `plugin//plugin.json`). +The VSCode extension loads plugin data from the annotation bundle (`plugin/index.json` + `plugin//plugin.json`). ## Credits -Forked from [luttje/glua-api-snippets](https://github.com/luttje/glua-api-snippets) +Based on [luttje/glua-api-snippets](https://github.com/luttje/glua-api-snippets) From 96cb9cf9a52b1a560d339b1f87abec75c44422e5 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 13:55:19 +0100 Subject: [PATCH 060/117] Fix CI --- .github/workflows/release-gluals.yml | 33 +++++++++++++++++++++++++--- __tests__/release-workflow.spec.ts | 23 +++++++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 __tests__/release-workflow.spec.ts diff --git a/.github/workflows/release-gluals.yml b/.github/workflows/release-gluals.yml index 9aa74f11..0f216941 100644 --- a/.github/workflows/release-gluals.yml +++ b/.github/workflows/release-gluals.yml @@ -63,6 +63,8 @@ jobs: env: EVENT: ${{ github.event_name }} BEFORE_SHA: ${{ github.event.before }} + PUBLISH_BASE_BRANCH: ${{ matrix.publish_base_branch }} + PLUGIN_BRANCH_PREFIX: ${{ matrix.plugin_branch_prefix }} run: | set -e @@ -76,6 +78,29 @@ jobs: exit 0 fi + missing_publish_branch=false + if ! git ls-remote --exit-code --heads origin "$PUBLISH_BASE_BRANCH" >/dev/null 2>&1; then + echo "Publish branch '$PUBLISH_BASE_BRANCH' does not exist yet -> generate all to bootstrap release stream" + missing_publish_branch=true + fi + + for plugin_manifest in plugin/*/plugin.json; do + [ -f "$plugin_manifest" ] || continue + plugin_id="$(basename "$(dirname "$plugin_manifest")")" + plugin_branch="${PLUGIN_BRANCH_PREFIX}${plugin_id}" + if ! git ls-remote --exit-code --heads origin "$plugin_branch" >/dev/null 2>&1; then + echo "Publish branch '$plugin_branch' does not exist yet -> generate all to bootstrap release stream" + missing_publish_branch=true + fi + done + + if $missing_publish_branch; then + echo "should_generate=true" >> "$GITHUB_OUTPUT" + echo "publish_base=true" >> "$GITHUB_OUTPUT" + echo "publish_all_plugins=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + # Push event -> diff against parent. Fail open (generate everything) if we can't. if [ -z "$BEFORE_SHA" ] || [ "$BEFORE_SHA" = "0000000000000000000000000000000000000000" ] \ || ! git cat-file -e "$BEFORE_SHA" 2>/dev/null; then @@ -160,8 +185,7 @@ jobs: run: | npm run generate-lua -- \ --output ./output \ - --customOverrides ./custom \ - --wipeLua + --custom-overrides ./custom npm run generate-plugin-index npm run generate-plugin-artifacts -- \ --pluginRoot ./plugin \ @@ -398,9 +422,12 @@ jobs: local source_dir="$2" local commit_message="$3" + git reset --hard + git clean -fdx + if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then git fetch --depth=1 origin "refs/heads/$branch_name:refs/remotes/origin/$branch_name" - git checkout -B "$branch_name" "refs/remotes/origin/$branch_name" + git checkout --force -B "$branch_name" "refs/remotes/origin/$branch_name" else git checkout --orphan "$branch_name" fi diff --git a/__tests__/release-workflow.spec.ts b/__tests__/release-workflow.spec.ts new file mode 100644 index 00000000..9f96845f --- /dev/null +++ b/__tests__/release-workflow.spec.ts @@ -0,0 +1,23 @@ +import fs from 'fs'; +import path from 'path'; + +describe('release-gluals workflow', () => { + const workflow = fs.readFileSync(path.join(process.cwd(), '.github/workflows/release-gluals.yml'), 'utf8'); + + test('uses supported generate-lua CLI flags', () => { + expect(workflow).toContain('--custom-overrides ./custom'); + expect(workflow).not.toContain('--customOverrides'); + expect(workflow).not.toContain('--wipeLua'); + }); + + test('bootstraps generation when the publish branch is missing', () => { + expect(workflow).toContain('PUBLISH_BASE_BRANCH: ${{ matrix.publish_base_branch }}'); + expect(workflow).toContain('Publish branch'); + expect(workflow).toContain('does not exist yet -> generate all to bootstrap release stream'); + }); + + test('cleans generated worktree changes before switching to publish branches', () => { + expect(workflow).toContain('git reset --hard'); + expect(workflow).toContain('git checkout --force -B "$branch_name"'); + }); +}); From 549f9585904d381da0a39242d4898bfe45292241 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:02:00 +0100 Subject: [PATCH 061/117] Fix CI --- .github/workflows/tests.yml | 1 + __tests__/release-workflow.spec.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1842d7cb..953833bb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -3,6 +3,7 @@ on: push: branches: - main + - beta pull_request: branches: - "**" diff --git a/__tests__/release-workflow.spec.ts b/__tests__/release-workflow.spec.ts index 9f96845f..d2fb0e20 100644 --- a/__tests__/release-workflow.spec.ts +++ b/__tests__/release-workflow.spec.ts @@ -3,6 +3,25 @@ import path from 'path'; describe('release-gluals workflow', () => { const workflow = fs.readFileSync(path.join(process.cwd(), '.github/workflows/release-gluals.yml'), 'utf8'); + const testsWorkflow = fs.readFileSync(path.join(process.cwd(), '.github/workflows/tests.yml'), 'utf8'); + + test('does not keep the old test-plugin release workflow', () => { + expect(fs.existsSync(path.join(process.cwd(), '.github/workflows/release-test.yml'))).toBe(false); + }); + + test('runs tests on both production source branches', () => { + expect(testsWorkflow).toContain('- main'); + expect(testsWorkflow).toContain('- beta'); + }); + + test('publishes stable and prerelease from separate source branches', () => { + expect(workflow).toContain('source_branch: main'); + expect(workflow).toContain('publish_base_branch: gluals-annotations'); + expect(workflow).toContain('plugin_branch_prefix: gluals-annotations-plugin-'); + expect(workflow).toContain('source_branch: beta'); + expect(workflow).toContain('publish_base_branch: gluals-annotations-prerelease'); + expect(workflow).toContain('plugin_branch_prefix: gluals-annotations-prerelease-plugin-'); + }); test('uses supported generate-lua CLI flags', () => { expect(workflow).toContain('--custom-overrides ./custom'); From 2ff0a76279fd28488b61fcca43cac61f14685fe4 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 14:37:45 +0100 Subject: [PATCH 062/117] Add gamemode field annotation overrides --- __tests__/custom-annotations.spec.ts | 9 +++++++++ custom/class.GM.lua | 11 +++++++++++ 2 files changed, 20 insertions(+) create mode 100644 custom/class.GM.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index a0c9adf3..474fc0e1 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -21,6 +21,7 @@ describe('custom and plugin annotation smoke checks', () => { test('new custom class overrides and global alias are present', () => { const customRoot = path.join(process.cwd(), 'custom'); const globals = fs.readFileSync(path.join(customRoot, '_globals.lua'), 'utf8'); + const gm = fs.readFileSync(path.join(customRoot, 'class.GM.lua'), 'utf8'); const dCheckBoxLabel = fs.readFileSync(path.join(customRoot, 'class.DCheckBoxLabel.lua'), 'utf8'); const dHtmlControls = fs.readFileSync(path.join(customRoot, 'class.DHTMLControls.lua'), 'utf8'); const dPanelList = fs.readFileSync(path.join(customRoot, 'class.DPanelList.lua'), 'utf8'); @@ -80,6 +81,7 @@ describe('custom and plugin annotation smoke checks', () => { const serverQueryData = fs.readFileSync(path.join(customRoot, 'ServerQueryData.lua'), 'utf8'); const skin = fs.readFileSync(path.join(customRoot, 'class.SKIN.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); + const generatedGM = fs.readFileSync(path.join(process.cwd(), 'output', 'gm.lua'), 'utf8'); const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); const generatedDButton = fs.readFileSync(path.join(process.cwd(), 'output', 'dbutton.lua'), 'utf8'); @@ -108,6 +110,13 @@ describe('custom and plugin annotation smoke checks', () => { expect(globals).toMatch(/---@alias EntityOrNULL Entity\|NULL/); expect(globals).toMatch(/---@type NULL/); + expect(gm).toMatch(/---@field Name string/); + expect(gm).toMatch(/---@field TeamBased boolean/); + expect(gm).toMatch(/---@field IsSandboxDerived\? boolean/); + expect(generatedGM).toMatch(/---@field Name string/); + expect(generatedGM).toMatch(/---@field TeamBased boolean/); + expect(generatedGM).toMatch(/---@field IsSandboxDerived\? boolean/); + expect(dCheckBoxLabel).toMatch(/---@class DCheckBoxLabel : Panel/); expect(dCheckBoxLabel).toMatch(/---@field Button DCheckBox/); expect(dCheckBoxLabel).toMatch(/---@field Label DLabel/); diff --git a/custom/class.GM.lua b/custom/class.GM.lua new file mode 100644 index 00000000..bb9025c7 --- /dev/null +++ b/custom/class.GM.lua @@ -0,0 +1,11 @@ +--- Source: +--- - garrysmod/gamemodes/base/gamemode/shared.lua +--- - garrysmod/gamemodes/sandbox/gamemode/shared.lua +---@class GM +---@field Name string Gamemode display name. +---@field Author string Gamemode author. +---@field Email string Gamemode contact email. +---@field Website string Gamemode website. +---@field TeamBased boolean Whether the gamemode uses teams. +---@field IsSandboxDerived? boolean True for Sandbox and Sandbox-derived gamemodes. +GM = {} From e7f0477d737034986bcc6dfc94521052f3fae8fa Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 15:30:09 +0100 Subject: [PATCH 063/117] Add remaining base annotation overrides --- __tests__/custom-annotations.spec.ts | 24 +++++++++++++++++ custom/CLuaParticle.SetColor.lua | 8 ++++++ custom/RENDERGROUP.lua | 39 ++++++++++++++++++++++++++++ custom/class.EFFECT.lua | 14 ++++++++++ custom/class.base_ai.lua | 4 +++ custom/class.base_gmodentity.lua | 29 +++++++++++++++++++++ 6 files changed, 118 insertions(+) create mode 100644 custom/CLuaParticle.SetColor.lua create mode 100644 custom/RENDERGROUP.lua create mode 100644 custom/class.EFFECT.lua create mode 100644 custom/class.base_ai.lua create mode 100644 custom/class.base_gmodentity.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 474fc0e1..7c592123 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -76,11 +76,20 @@ describe('custom and plugin annotation smoke checks', () => { const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); + const baseGmodEntity = fs.readFileSync(path.join(customRoot, 'class.base_gmodentity.lua'), 'utf8'); + const baseAi = fs.readFileSync(path.join(customRoot, 'class.base_ai.lua'), 'utf8'); + const effect = fs.readFileSync(path.join(customRoot, 'class.EFFECT.lua'), 'utf8'); + const luaParticleSetColor = fs.readFileSync(path.join(customRoot, 'CLuaParticle.SetColor.lua'), 'utf8'); + const renderGroup = fs.readFileSync(path.join(customRoot, 'RENDERGROUP.lua'), 'utf8'); const skeletonConvertor = fs.readFileSync(path.join(customRoot, 'class.SkeletonConvertor.lua'), 'utf8'); const listSet = fs.readFileSync(path.join(customRoot, 'list.Set.lua'), 'utf8'); const serverQueryData = fs.readFileSync(path.join(customRoot, 'ServerQueryData.lua'), 'utf8'); const skin = fs.readFileSync(path.join(customRoot, 'class.SKIN.lua'), 'utf8'); const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); + const generatedEntity = fs.readFileSync(path.join(process.cwd(), 'output', 'entity.lua'), 'utf8'); + const generatedEffect = fs.readFileSync(path.join(process.cwd(), 'output', 'effect.lua'), 'utf8'); + const generatedLuaParticle = fs.readFileSync(path.join(process.cwd(), 'output', 'cluaparticle.lua'), 'utf8'); + const generatedEnums = fs.readFileSync(path.join(process.cwd(), 'output', 'enums.lua'), 'utf8'); const generatedGM = fs.readFileSync(path.join(process.cwd(), 'output', 'gm.lua'), 'utf8'); const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); @@ -248,6 +257,21 @@ describe('custom and plugin annotation smoke checks', () => { expect(enginePanels).toMatch(/---@class \(partial\) Chromium : HTML/); expect(enginePanels).toMatch(/---@class \(partial\) ModelImage : Panel/); expect(enginePanels).toMatch(/---@class \(partial\) URLLabel : Label/); + expect(baseGmodEntity).toMatch(/---@class base_gmodentity : Entity/); + expect(baseGmodEntity).toMatch(/function base_gmodentity:SetPlayer\(ply\) end/); + expect(baseAi).toMatch(/---@class base_ai : NPC/); + expect(generatedCustomClasses).toMatch(/---@class base_gmodentity : Entity/); + expect(generatedCustomClasses).toMatch(/function base_gmodentity:SetPlayer\(ply\) end/); + expect(generatedCustomClasses).toMatch(/---@class base_ai : NPC/); + expect(effect).toMatch(/---@class EFFECT : Entity/); + expect(effect).toMatch(/---@field Entity Entity/); + expect(generatedEffect).toMatch(/---@class EFFECT/); + expect(generatedEffect).toMatch(/---@field Entity Entity/); + expect(generatedEffect).toMatch(/---@source https:\/\/wiki\.facepunch\.com\/gmod\/EFFECT_Hooks/); + expect(luaParticleSetColor).toMatch(/---@overload fun\(self: CLuaParticle, color: Color\)/); + expect(generatedLuaParticle).toMatch(/---@overload fun\(self: CLuaParticle, color: Color\)/); + expect(renderGroup).toMatch(/RENDERGROUP_NONE = 5/); + expect(generatedEnums).toMatch(/RENDERGROUP_NONE = 5/); expect(skeletonConvertor).toMatch(/---@class ModelEntity/); expect(skeletonConvertor).toMatch(/---@field GetModel fun\(self: ModelEntity\): string/); expect(skeletonConvertor).toMatch(/---@class SkeletonConvertor/); diff --git a/custom/CLuaParticle.SetColor.lua b/custom/CLuaParticle.SetColor.lua new file mode 100644 index 00000000..08bca7d3 --- /dev/null +++ b/custom/CLuaParticle.SetColor.lua @@ -0,0 +1,8 @@ +---Sets the color of the particle. +---@realm client +---@source https://wiki.facepunch.com/gmod/CLuaParticle:SetColor +---@overload fun(self: CLuaParticle, color: Color) +---@param r number The red component. +---@param g number The green component. +---@param b number The blue component. +function CLuaParticle:SetColor(r, g, b) end diff --git a/custom/RENDERGROUP.lua b/custom/RENDERGROUP.lua new file mode 100644 index 00000000..4042b269 --- /dev/null +++ b/custom/RENDERGROUP.lua @@ -0,0 +1,39 @@ +---Enumerations used by `ClientsideModel`, `ENT.RenderGroup`, and `Entity:GetRenderGroup`. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Enums/RENDERGROUP +---@readonly +RENDERGROUP_STATIC_HUGE = 0 +---@readonly +RENDERGROUP_OPAQUE_HUGE = 1 +---@readonly +RENDERGROUP_NONE = 5 +---@readonly +RENDERGROUP_STATIC = 6 +---@readonly +RENDERGROUP_OPAQUE = 7 +---@readonly +RENDERGROUP_TRANSLUCENT = 8 +---@readonly +RENDERGROUP_BOTH = 9 +---@readonly +RENDERGROUP_VIEWMODEL = 10 +---@readonly +RENDERGROUP_VIEWMODEL_TRANSLUCENT = 11 +---@readonly +RENDERGROUP_OPAQUE_BRUSH = 12 +---@readonly +RENDERGROUP_OTHER = 13 + +---@alias RENDERGROUP +---| number # Raw numeric enum value +---| 0 # RENDERGROUP_STATIC_HUGE +---| 1 # RENDERGROUP_OPAQUE_HUGE +---| 5 # RENDERGROUP_NONE +---| 6 # RENDERGROUP_STATIC +---| 7 # RENDERGROUP_OPAQUE +---| 8 # RENDERGROUP_TRANSLUCENT +---| 9 # RENDERGROUP_BOTH +---| 10 # RENDERGROUP_VIEWMODEL +---| 11 # RENDERGROUP_VIEWMODEL_TRANSLUCENT +---| 12 # RENDERGROUP_OPAQUE_BRUSH +---| 13 # RENDERGROUP_OTHER diff --git a/custom/class.EFFECT.lua b/custom/class.EFFECT.lua new file mode 100644 index 00000000..34fffdd8 --- /dev/null +++ b/custom/class.EFFECT.lua @@ -0,0 +1,14 @@ +---Hooks used inside a Lua effect. +--- +---Lua effects are stored in either the `/lua/effects` directory or in a gamemode +---under `/gamemodes/*/entities/effects`. Effects are entities with the classname +---`class CLuaEffect`, so Entity functions are usable on them through `self`. +--- +---Garry's Mod also provides the backing clientside effect entity on `self.Entity` +---for legacy scripted effects that render or move an entity model. +---@source https://wiki.facepunch.com/gmod/EFFECT_Hooks +---@source garrysmod/gamemodes/base/entities/effects/base.lua +---@source garrysmod/gamemodes/sandbox/entities/effects/balloon_pop.lua +---@class EFFECT : Entity +---@field Entity Entity The backing effect entity. +EFFECT = {} diff --git a/custom/class.base_ai.lua b/custom/class.base_ai.lua new file mode 100644 index 00000000..d579ccf2 --- /dev/null +++ b/custom/class.base_ai.lua @@ -0,0 +1,4 @@ +---Base scripted AI entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_ai/init.lua +---@class base_ai : NPC +local base_ai = {} diff --git a/custom/class.base_gmodentity.lua b/custom/class.base_gmodentity.lua new file mode 100644 index 00000000..c2403f5f --- /dev/null +++ b/custom/class.base_gmodentity.lua @@ -0,0 +1,29 @@ +---Sandbox scripted entity base that stores creator/player ownership metadata. +---@source garrysmod/gamemodes/sandbox/entities/entities/base_gmodentity.lua +---@class base_gmodentity : Entity +local base_gmodentity = {} + +---Sets the owning player for Sandbox-derived entities. +---@realm shared +---@param ply? Player|NULL The owning player. +function base_gmodentity:SetPlayer(ply) end + +---Returns the owning player for Sandbox-derived entities. +---@realm shared +---@return Player|NULL +function base_gmodentity:GetPlayer() end + +---Returns the owning player's unique ID for Sandbox-derived entities. +---@realm shared +---@return number +function base_gmodentity:GetPlayerIndex() end + +---Returns the owning player's SteamID64 for Sandbox-derived entities. +---@realm shared +---@return string +function base_gmodentity:GetPlayerSteamID() end + +---Returns the owning player's display name for Sandbox-derived entities. +---@realm shared +---@return string +function base_gmodentity:GetPlayerName() end From dca8760d983070252d836daf664dd73b3f01b5eb Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 16:15:10 +0100 Subject: [PATCH 064/117] Add Derma field annotation overrides --- __tests__/custom-annotations.spec.ts | 81 ++++++++++++++++++++++++++++ custom/class.DColorCube.lua | 5 ++ custom/class.DColorMixer.lua | 21 ++++++++ custom/class.DComboBox.lua | 10 ++++ custom/class.DFileBrowser.lua | 12 +++++ custom/class.DHScrollBar.lua | 14 +++++ custom/class.DHTMLControls.lua | 16 ++++-- custom/class.DHorizontalScroller.lua | 8 +++ custom/class.DListView.lua | 4 ++ custom/class.DMenuBar.lua | 3 ++ custom/class.DMenuOption.lua | 5 ++ custom/class.DModelSelectMulti.lua | 3 ++ custom/class.DNotify.lua | 3 ++ custom/class.DPanelList.lua | 8 ++- custom/class.DPanelSelect.lua | 4 ++ custom/class.DProperties.lua | 4 ++ custom/class.DTree.lua | 4 ++ custom/class.DTree_Node.lua | 10 ++++ custom/class.DVScrollBar.lua | 14 +++++ 19 files changed, 224 insertions(+), 5 deletions(-) create mode 100644 custom/class.DColorCube.lua create mode 100644 custom/class.DColorMixer.lua create mode 100644 custom/class.DComboBox.lua create mode 100644 custom/class.DHScrollBar.lua create mode 100644 custom/class.DHorizontalScroller.lua create mode 100644 custom/class.DMenuBar.lua create mode 100644 custom/class.DMenuOption.lua create mode 100644 custom/class.DModelSelectMulti.lua create mode 100644 custom/class.DNotify.lua create mode 100644 custom/class.DPanelSelect.lua create mode 100644 custom/class.DProperties.lua create mode 100644 custom/class.DTree.lua create mode 100644 custom/class.DTree_Node.lua create mode 100644 custom/class.DVScrollBar.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 7c592123..40922e9c 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -23,8 +23,24 @@ describe('custom and plugin annotation smoke checks', () => { const globals = fs.readFileSync(path.join(customRoot, '_globals.lua'), 'utf8'); const gm = fs.readFileSync(path.join(customRoot, 'class.GM.lua'), 'utf8'); const dCheckBoxLabel = fs.readFileSync(path.join(customRoot, 'class.DCheckBoxLabel.lua'), 'utf8'); + const dColorCube = fs.readFileSync(path.join(customRoot, 'class.DColorCube.lua'), 'utf8'); + const dColorMixer = fs.readFileSync(path.join(customRoot, 'class.DColorMixer.lua'), 'utf8'); + const dComboBox = fs.readFileSync(path.join(customRoot, 'class.DComboBox.lua'), 'utf8'); + const dFileBrowser = fs.readFileSync(path.join(customRoot, 'class.DFileBrowser.lua'), 'utf8'); const dHtmlControls = fs.readFileSync(path.join(customRoot, 'class.DHTMLControls.lua'), 'utf8'); + const dHorizontalScroller = fs.readFileSync(path.join(customRoot, 'class.DHorizontalScroller.lua'), 'utf8'); + const dhScrollBar = fs.readFileSync(path.join(customRoot, 'class.DHScrollBar.lua'), 'utf8'); + const dListView = fs.readFileSync(path.join(customRoot, 'class.DListView.lua'), 'utf8'); + const dMenuBar = fs.readFileSync(path.join(customRoot, 'class.DMenuBar.lua'), 'utf8'); + const dMenuOption = fs.readFileSync(path.join(customRoot, 'class.DMenuOption.lua'), 'utf8'); + const dModelSelectMulti = fs.readFileSync(path.join(customRoot, 'class.DModelSelectMulti.lua'), 'utf8'); + const dNotify = fs.readFileSync(path.join(customRoot, 'class.DNotify.lua'), 'utf8'); const dPanelList = fs.readFileSync(path.join(customRoot, 'class.DPanelList.lua'), 'utf8'); + const dPanelSelect = fs.readFileSync(path.join(customRoot, 'class.DPanelSelect.lua'), 'utf8'); + const dProperties = fs.readFileSync(path.join(customRoot, 'class.DProperties.lua'), 'utf8'); + const dTree = fs.readFileSync(path.join(customRoot, 'class.DTree.lua'), 'utf8'); + const dTreeNode = fs.readFileSync(path.join(customRoot, 'class.DTree_Node.lua'), 'utf8'); + const dvScrollBar = fs.readFileSync(path.join(customRoot, 'class.DVScrollBar.lua'), 'utf8'); const dMenuAddPanel = fs.readFileSync(path.join(customRoot, 'DMenu.AddPanel.lua'), 'utf8'); const dCheckBoxSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetValue.lua'), 'utf8'); const dCheckBoxSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetChecked.lua'), 'utf8'); @@ -92,13 +108,30 @@ describe('custom and plugin annotation smoke checks', () => { const generatedEnums = fs.readFileSync(path.join(process.cwd(), 'output', 'enums.lua'), 'utf8'); const generatedGM = fs.readFileSync(path.join(process.cwd(), 'output', 'gm.lua'), 'utf8'); const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); + const generatedDColorCube = fs.readFileSync(path.join(process.cwd(), 'output', 'dcolorcube.lua'), 'utf8'); + const generatedDColorMixer = fs.readFileSync(path.join(process.cwd(), 'output', 'dcolormixer.lua'), 'utf8'); + const generatedDComboBox = fs.readFileSync(path.join(process.cwd(), 'output', 'dcombobox.lua'), 'utf8'); + const generatedDFileBrowser = fs.readFileSync(path.join(process.cwd(), 'output', 'dfilebrowser.lua'), 'utf8'); + const generatedDHTMLControls = fs.readFileSync(path.join(process.cwd(), 'output', 'dhtmlcontrols.lua'), 'utf8'); + const generatedDHorizontalScroller = fs.readFileSync(path.join(process.cwd(), 'output', 'dhorizontalscroller.lua'), 'utf8'); + const generatedDHScrollBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dhscrollbar.lua'), 'utf8'); const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); + const generatedDListView = fs.readFileSync(path.join(process.cwd(), 'output', 'dlistview.lua'), 'utf8'); + const generatedDMenuBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenubar.lua'), 'utf8'); + const generatedDMenuOption = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenuoption.lua'), 'utf8'); + const generatedDModelSelectMulti = fs.readFileSync(path.join(process.cwd(), 'output', 'dmodelselectmulti.lua'), 'utf8'); + const generatedDNotify = fs.readFileSync(path.join(process.cwd(), 'output', 'dnotify.lua'), 'utf8'); + const generatedDPanelList = fs.readFileSync(path.join(process.cwd(), 'output', 'dpanellist.lua'), 'utf8'); + const generatedDPanelSelect = fs.readFileSync(path.join(process.cwd(), 'output', 'dpanelselect.lua'), 'utf8'); + const generatedDProperties = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperties.lua'), 'utf8'); const generatedDButton = fs.readFileSync(path.join(process.cwd(), 'output', 'dbutton.lua'), 'utf8'); const generatedDLabel = fs.readFileSync(path.join(process.cwd(), 'output', 'dlabel.lua'), 'utf8'); const generatedDMenu = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenu.lua'), 'utf8'); const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); const generatedDSlider = fs.readFileSync(path.join(process.cwd(), 'output', 'dslider.lua'), 'utf8'); const generatedDTreeNode = fs.readFileSync(path.join(process.cwd(), 'output', 'dtree_node.lua'), 'utf8'); + const generatedDTree = fs.readFileSync(path.join(process.cwd(), 'output', 'dtree.lua'), 'utf8'); + const generatedDVScrollBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dvscrollbar.lua'), 'utf8'); const generatedPanel = fs.readFileSync(path.join(process.cwd(), 'output', 'panel.lua'), 'utf8'); const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); @@ -130,11 +163,38 @@ describe('custom and plugin annotation smoke checks', () => { expect(dCheckBoxLabel).toMatch(/---@field Button DCheckBox/); expect(dCheckBoxLabel).toMatch(/---@field Label DLabel/); + expect(dColorCube).toMatch(/---@field BGSaturation DImage/); + expect(dColorMixer).toMatch(/---@field Palette DColorPalette/); + expect(dColorMixer).toMatch(/---@field txtR DNumberWang/); + expect(dColorMixer).toMatch(/---@field m_bPalette\? boolean/); + expect(dColorMixer).toMatch(/---@field m_ConVarA\? string/); + expect(dComboBox).toMatch(/---@field Choices table/); + expect(dComboBox).toMatch(/---@field Menu\? DMenu/); + expect(dFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); + expect(dFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); + expect(dFileBrowser).toMatch(/---@field m_strPath string/); + expect(dFileBrowser).toMatch(/---@field m_bModels\? boolean/); + expect(dFileBrowser).toMatch(/---@field m_bOpen\? boolean/); expect(dHtmlControls).toMatch(/---@class DHTMLControls : Panel/); expect(dHtmlControls).toMatch(/---@field AddressBar DTextEntry/); + expect(dHtmlControls).toMatch(/---@field HTML\? DHTML/); + expect(dHorizontalScroller).toMatch(/---@field Panels Panel\[]/); + expect(dhScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); + expect(dListView).toMatch(/---@field Columns DListView_Column\[]/); + expect(dListView).toMatch(/---@field pnlCanvas Panel/); + expect(dMenuBar).toMatch(/---@field Menus table/); + expect(dMenuOption).toMatch(/---@field SubMenu\? DMenu/); + expect(dModelSelectMulti).toMatch(/---@field ModelPanels table/); + expect(dNotify).toMatch(/---@field Items table/); expect(dPanelList).toMatch(/---@class DPanelList : DPanel/); expect(dPanelList).toMatch(/---@field Items Panel\[]/); + expect(dPanelList).toMatch(/---@field pnlCanvas DPanel/); + expect(dPanelSelect).toMatch(/---@field SelectedPanel\? Panel/); + expect(dProperties).toMatch(/---@field Categories table/); + expect(dTree).toMatch(/---@field RootNode DTree_Node/); + expect(dTreeNode).toMatch(/---@field ChildNodes\? DListLayout/); + expect(dvScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); expect(dMenuAddPanel).toMatch(/---@param pnl T The panel that you want to add\./); expect(dCheckBoxSetValue).toMatch(/---@param checked any/); expect(dCheckBoxSetChecked).toMatch(/---@param checked any/); @@ -154,6 +214,27 @@ describe('custom and plugin annotation smoke checks', () => { expect(dTreeNodeSetShowFiles).toMatch(/---@param showFiles\? boolean/); expect(dTreeNodeSetWildCard).toMatch(/---@param wildcard\? string/); expect(generatedDImage).toMatch(/---@param mat\? string/); + expect(generatedDColorCube).toMatch(/---@field BGSaturation DImage/); + expect(generatedDColorMixer).toMatch(/---@field Palette DColorPalette/); + expect(generatedDColorMixer).toMatch(/---@field m_bPalette\? boolean/); + expect(generatedDComboBox).toMatch(/---@field Choices table/); + expect(generatedDFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); + expect(generatedDFileBrowser).toMatch(/---@field m_bModels\? boolean/); + expect(generatedDFileBrowser).toMatch(/---@field m_bOpen\? boolean/); + expect(generatedDHTMLControls).toMatch(/---@field HTML\? DHTML/); + expect(generatedDHorizontalScroller).toMatch(/---@field Panels Panel\[]/); + expect(generatedDHScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); + expect(generatedDListView).toMatch(/---@field Columns DListView_Column\[]/); + expect(generatedDMenuBar).toMatch(/---@field Menus table/); + expect(generatedDMenuOption).toMatch(/---@field SubMenu\? DMenu/); + expect(generatedDModelSelectMulti).toMatch(/---@field ModelPanels table/); + expect(generatedDNotify).toMatch(/---@field Items table/); + expect(generatedDPanelList).toMatch(/---@field pnlCanvas DPanel/); + expect(generatedDPanelSelect).toMatch(/---@field SelectedPanel\? Panel/); + expect(generatedDProperties).toMatch(/---@field Categories table/); + expect(generatedDTree).toMatch(/---@field RootNode DTree_Node/); + expect(generatedDTreeNode).toMatch(/---@field ChildNodes\? DListLayout/); + expect(generatedDVScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); expect(generatedDButton).toMatch(/---@param skin SKIN/); expect(generatedDLabel).toMatch(/---@param skin SKIN/); expect(generatedDMenu).toMatch(/---@param item\? Panel/); diff --git a/custom/class.DColorCube.lua b/custom/class.DColorCube.lua new file mode 100644 index 00000000..a00c7977 --- /dev/null +++ b/custom/class.DColorCube.lua @@ -0,0 +1,5 @@ +---@class DColorCube : DSlider +---@field BGSaturation DImage +---@field BGValue DImage +---@field m_BaseRGB Color +local DColorCube = {} diff --git a/custom/class.DColorMixer.lua b/custom/class.DColorMixer.lua new file mode 100644 index 00000000..20f4e34a --- /dev/null +++ b/custom/class.DColorMixer.lua @@ -0,0 +1,21 @@ +---@class DColorMixer : DPanel +---@field Palette DColorPalette +---@field label DLabel +---@field WangsPanel Panel +---@field txtR DNumberWang +---@field txtG DNumberWang +---@field txtB DNumberWang +---@field txtA DNumberWang +---@field HSV DColorCube +---@field RGB DRGBPicker +---@field Alpha DAlphaBar +---@field NextConVarCheck number +---@field m_bPalette? boolean +---@field m_bAlpha boolean +---@field m_bWangsPanel boolean +---@field m_ConVarR? string +---@field m_ConVarG? string +---@field m_ConVarB? string +---@field m_ConVarA? string +---@field m_Color Color +local DColorMixer = {} diff --git a/custom/class.DComboBox.lua b/custom/class.DComboBox.lua new file mode 100644 index 00000000..d8747ecd --- /dev/null +++ b/custom/class.DComboBox.lua @@ -0,0 +1,10 @@ +---@class DComboBox : DButton +---@field DropButton DPanel +---@field Choices table +---@field Data table +---@field ChoiceIcons table +---@field Spacers table +---@field selected? integer +---@field Menu? DMenu +---@field m_strConVarValue? string +local DComboBox = {} diff --git a/custom/class.DFileBrowser.lua b/custom/class.DFileBrowser.lua index 42afa1ca..d02b5195 100644 --- a/custom/class.DFileBrowser.lua +++ b/custom/class.DFileBrowser.lua @@ -1,4 +1,16 @@ ---@class DFileBrowser : DPanel ---@field Divider DHorizontalDivider The horizontal divider panel splitting the tree and file list. ---@field Tree DTree The tree view panel for directory navigation. +---@field FolderNode? DTree_Node The root folder node created during setup. +---@field Files? DIconBrowser|DListView The active file list or model icon browser. +---@field FileHeader? Panel The list-view column header for file paths. +---@field bSetup? boolean Whether tree and file panels have been initialized. +---@field m_strName? string +---@field m_strBaseFolder? string +---@field m_strPath string +---@field m_strSearch? string +---@field m_strFilter? string +---@field m_bModels? boolean +---@field m_strCurrentFolder? string +---@field m_bOpen? boolean local DFileBrowser = {} diff --git a/custom/class.DHScrollBar.lua b/custom/class.DHScrollBar.lua new file mode 100644 index 00000000..62128d92 --- /dev/null +++ b/custom/class.DHScrollBar.lua @@ -0,0 +1,14 @@ +---@class DHScrollBar : Panel +---@field Offset number +---@field Scroll number +---@field CanvasSize number +---@field BarSize number +---@field btnLeft DButton +---@field btnRight DButton +---@field btnGrip DScrollBarGrip +---@field HasChanged? boolean +---@field Enabled? boolean +---@field Dragging? boolean +---@field DraggingCanvas? any +---@field HoldPos? number +local DHScrollBar = {} diff --git a/custom/class.DHTMLControls.lua b/custom/class.DHTMLControls.lua index d4536dda..f63a842f 100644 --- a/custom/class.DHTMLControls.lua +++ b/custom/class.DHTMLControls.lua @@ -1,7 +1,15 @@ ---@class DHTMLControls : Panel ---@field AddressBar DTextEntry ----@field BackButton DButton ----@field ForwardButton DButton ----@field RefreshButton DButton ----@field StopButton DButton +---@field BackButton DImageButton +---@field ForwardButton DImageButton +---@field RefreshButton DImageButton +---@field HomeButton DImageButton +---@field StopButton DImageButton +---@field History table +---@field Cur integer +---@field Navigating? boolean +---@field BorderSize number +---@field BackgroundColor Color +---@field HomeURL string +---@field HTML? DHTML local DHTMLControls = {} diff --git a/custom/class.DHorizontalScroller.lua b/custom/class.DHorizontalScroller.lua new file mode 100644 index 00000000..716f1e8b --- /dev/null +++ b/custom/class.DHorizontalScroller.lua @@ -0,0 +1,8 @@ +---@class DHorizontalScroller : Panel +---@field Panels Panel[] +---@field OffsetX number +---@field FrameTime number +---@field pnlCanvas DDragBase +---@field btnLeft DButton +---@field btnRight DButton +local DHorizontalScroller = {} diff --git a/custom/class.DListView.lua b/custom/class.DListView.lua index 04a71d1e..5aa62f69 100644 --- a/custom/class.DListView.lua +++ b/custom/class.DListView.lua @@ -1,3 +1,7 @@ ---@class DListView : DPanel +---@field Columns DListView_Column[] +---@field Lines DListView_Line[] ---@field Sorted table Lines sorted by the current column/order. +---@field pnlCanvas Panel +---@field VBar? DVScrollBar local DListView = {} diff --git a/custom/class.DMenuBar.lua b/custom/class.DMenuBar.lua new file mode 100644 index 00000000..e11a150c --- /dev/null +++ b/custom/class.DMenuBar.lua @@ -0,0 +1,3 @@ +---@class DMenuBar : DPanel +---@field Menus table +local DMenuBar = {} diff --git a/custom/class.DMenuOption.lua b/custom/class.DMenuOption.lua new file mode 100644 index 00000000..c76bb6ba --- /dev/null +++ b/custom/class.DMenuOption.lua @@ -0,0 +1,5 @@ +---@class DMenuOption : DButton +---@field SubMenu? DMenu +---@field SubMenuArrow? Panel +---@field m_MenuClicking? boolean +local DMenuOption = {} diff --git a/custom/class.DModelSelectMulti.lua b/custom/class.DModelSelectMulti.lua new file mode 100644 index 00000000..a22bb270 --- /dev/null +++ b/custom/class.DModelSelectMulti.lua @@ -0,0 +1,3 @@ +---@class DModelSelectMulti : DPropertySheet +---@field ModelPanels table +local DModelSelectMulti = {} diff --git a/custom/class.DNotify.lua b/custom/class.DNotify.lua new file mode 100644 index 00000000..72adb258 --- /dev/null +++ b/custom/class.DNotify.lua @@ -0,0 +1,3 @@ +---@class DNotify : Panel +---@field Items table +local DNotify = {} diff --git a/custom/class.DPanelList.lua b/custom/class.DPanelList.lua index 92a3a27b..c563eb04 100644 --- a/custom/class.DPanelList.lua +++ b/custom/class.DPanelList.lua @@ -1,4 +1,10 @@ ---@class DPanelList : DPanel +---@field pnlCanvas DPanel ---@field Items Panel[] ----@field VBar DVScrollBar +---@field YOffset number +---@field m_fAnimTime number +---@field m_fAnimEase number +---@field m_iBuilds integer +---@field Horizontal boolean +---@field VBar? DVScrollBar local DPanelList = {} diff --git a/custom/class.DPanelSelect.lua b/custom/class.DPanelSelect.lua new file mode 100644 index 00000000..b8fc6042 --- /dev/null +++ b/custom/class.DPanelSelect.lua @@ -0,0 +1,4 @@ +---@class DPanelSelect : DPanelList +---@field SelectedPanel? Panel +---@field OldSelectedPaintOver? function +local DPanelSelect = {} diff --git a/custom/class.DProperties.lua b/custom/class.DProperties.lua new file mode 100644 index 00000000..e7c9a5b0 --- /dev/null +++ b/custom/class.DProperties.lua @@ -0,0 +1,4 @@ +---@class DProperties : Panel +---@field Categories table +---@field Canvas? DScrollPanel +local DProperties = {} diff --git a/custom/class.DTree.lua b/custom/class.DTree.lua new file mode 100644 index 00000000..6c62ce1d --- /dev/null +++ b/custom/class.DTree.lua @@ -0,0 +1,4 @@ +---@class DTree : DScrollPanel +---@field RootNode DTree_Node +---@field m_pSelectedItem? DTree_Node +local DTree = {} diff --git a/custom/class.DTree_Node.lua b/custom/class.DTree_Node.lua new file mode 100644 index 00000000..437e7d53 --- /dev/null +++ b/custom/class.DTree_Node.lua @@ -0,0 +1,10 @@ +---@class DTree_Node : DPanel +---@field Label DTree_Node_Button +---@field Expander DExpandButton +---@field Icon DImage +---@field animSlide table +---@field fLastClick number +---@field m_pRoot? DTree +---@field m_pParentNode? DTree|DTree_Node +---@field ChildNodes? DListLayout +local DTree_Node = {} diff --git a/custom/class.DVScrollBar.lua b/custom/class.DVScrollBar.lua new file mode 100644 index 00000000..1a43236c --- /dev/null +++ b/custom/class.DVScrollBar.lua @@ -0,0 +1,14 @@ +---@class DVScrollBar : Panel +---@field Offset number +---@field Scroll number +---@field CanvasSize number +---@field BarSize number +---@field btnUp DButton +---@field btnDown DButton +---@field btnGrip DScrollBarGrip +---@field HasChanged? boolean +---@field Enabled? boolean +---@field Dragging? boolean +---@field DraggingCanvas? any +---@field HoldPos? number +local DVScrollBar = {} From e86bc149521fa5a6b824e0630aa195206ed1e7a0 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 21 Jun 2026 19:48:33 +0100 Subject: [PATCH 065/117] Add shipped Lua false-positive annotation overrides --- __tests__/custom-annotations.spec.ts | 4 +- custom/ContentHeader.OpenMenu.lua | 4 ++ custom/DDragBase.DropAction_Copy.lua | 12 +++++ custom/DDragBase.DropAction_Normal.lua | 12 +++++ custom/DDragBase.DropAction_Simple.lua | 12 +++++ custom/DForm.TextEntry.lua | 7 +++ custom/DHorizontalDivider.SetLeft.lua | 5 ++ custom/DHorizontalDivider.SetRight.lua | 5 ++ custom/DListView.GetLine.lua | 7 +++ custom/DListView.GetSelectedLine.lua | 9 ++++ custom/DListView_Column.SetWidth.lua | 7 +++ custom/DTree_Node.AnimSlide.lua | 10 ++++ custom/Global.TauntCamera.lua | 5 ++ custom/PANEL.OnDrop.lua | 9 ++++ custom/PropSelect.AddModel.lua | 6 +++ custom/TOOL.LeftClick.lua | 8 +++ custom/Tool.GetOwner.lua | 7 +++ custom/Tool.GetWeapon.lua | 8 +++ custom/Weapon.GetToolObject.lua | 6 +++ custom/_globals.lua | 4 +- custom/class.ContentContainer.lua | 6 ++- custom/class.ContentSidebar.lua | 16 ++++++ custom/class.DListView.lua | 2 +- custom/class.DNotify.lua | 5 +- custom/class.DNumSlider.lua | 4 +- custom/class.DPropertySheet.lua | 1 + custom/class.DTree_Node.lua | 6 ++- custom/class.DermaAnimation.lua | 21 ++++++++ custom/class.PlayerClass.lua | 34 +++++++++++++ custom/class.SKIN.lua | 36 ++++++++++++++ custom/class.SWEP.lua | 11 +++++ custom/class.SpawnMenu.lua | 10 ++++ custom/class.TOOL.lua | 15 ------ custom/class.TauntCamera.lua | 25 ++++++++++ custom/class.Tool.lua | 66 +++++++++++++++++++++++++ custom/class.Weapon.lua | 15 ++++++ custom/class.env_fog_controller.lua | 2 + custom/class.env_projectedtexture.lua | 2 + custom/class.env_sun.lua | 2 + custom/class.npc_manhack.lua | 2 + custom/class.npc_rollermine.lua | 2 + custom/class.prop_physics.lua | 2 + custom/constraint.Elastic.lua | 21 ++++++++ custom/constraint.Weld.lua | 14 ++++++ custom/construct.SetPhysProp.lua | 9 ++++ custom/player_manager.RegisterClass.lua | 7 +++ custom/structures.TextData.lua | 11 +++++ custom/structures.TextureData.lua | 16 ++++++ 48 files changed, 487 insertions(+), 23 deletions(-) create mode 100644 custom/ContentHeader.OpenMenu.lua create mode 100644 custom/DDragBase.DropAction_Copy.lua create mode 100644 custom/DDragBase.DropAction_Normal.lua create mode 100644 custom/DDragBase.DropAction_Simple.lua create mode 100644 custom/DForm.TextEntry.lua create mode 100644 custom/DHorizontalDivider.SetLeft.lua create mode 100644 custom/DHorizontalDivider.SetRight.lua create mode 100644 custom/DListView.GetLine.lua create mode 100644 custom/DListView.GetSelectedLine.lua create mode 100644 custom/DListView_Column.SetWidth.lua create mode 100644 custom/DTree_Node.AnimSlide.lua create mode 100644 custom/Global.TauntCamera.lua create mode 100644 custom/PANEL.OnDrop.lua create mode 100644 custom/PropSelect.AddModel.lua create mode 100644 custom/TOOL.LeftClick.lua create mode 100644 custom/Tool.GetOwner.lua create mode 100644 custom/Tool.GetWeapon.lua create mode 100644 custom/Weapon.GetToolObject.lua create mode 100644 custom/class.ContentSidebar.lua create mode 100644 custom/class.DermaAnimation.lua create mode 100644 custom/class.PlayerClass.lua create mode 100644 custom/class.SpawnMenu.lua delete mode 100644 custom/class.TOOL.lua create mode 100644 custom/class.TauntCamera.lua create mode 100644 custom/class.Tool.lua create mode 100644 custom/class.Weapon.lua create mode 100644 custom/class.env_fog_controller.lua create mode 100644 custom/class.env_projectedtexture.lua create mode 100644 custom/class.env_sun.lua create mode 100644 custom/class.npc_manhack.lua create mode 100644 custom/class.npc_rollermine.lua create mode 100644 custom/class.prop_physics.lua create mode 100644 custom/constraint.Elastic.lua create mode 100644 custom/constraint.Weld.lua create mode 100644 custom/construct.SetPhysProp.lua create mode 100644 custom/player_manager.RegisterClass.lua create mode 100644 custom/structures.TextData.lua create mode 100644 custom/structures.TextureData.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 40922e9c..fcbf4dde 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -308,7 +308,9 @@ describe('custom and plugin annotation smoke checks', () => { expect(tableCopy).toMatch(/---@param originalTable T/); expect(tableCopy).toMatch(/---@return T/); - expect(contentContainer).toMatch(/---@class ContentContainer : DIconLayout/); + // ContentContainer is registered as `vgui.Register("ContentContainer", PANEL, "DScrollPanel")` + // in contentcontainer.lua, so its base class is DScrollPanel (not DIconLayout). + expect(contentContainer).toMatch(/---@class ContentContainer : DScrollPanel/); expect(contentContainer).toMatch(/function ContentContainer:SetTriggerSpawnlistChange\(trigger\) end/); expect(propVehiclePrisonerPod).toMatch(/---@class prop_vehicle_prisoner_pod : Vehicle/); diff --git a/custom/ContentHeader.OpenMenu.lua b/custom/ContentHeader.OpenMenu.lua new file mode 100644 index 00000000..801f24ac --- /dev/null +++ b/custom/ContentHeader.OpenMenu.lua @@ -0,0 +1,4 @@ +---Creates a DermaMenu with a delete option and opens it. Called internally on right-click. +---@realm client +---@source https://wiki.facepunch.com/gmod/ContentHeader:OpenMenu +function ContentHeader:OpenMenu() end diff --git a/custom/DDragBase.DropAction_Copy.lua b/custom/DDragBase.DropAction_Copy.lua new file mode 100644 index 00000000..fa686ca1 --- /dev/null +++ b/custom/DDragBase.DropAction_Copy.lua @@ -0,0 +1,12 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Internal function used in DDragBase:MakeDroppable. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DDragBase:DropAction_Copy +---@param drops Panel[] The list of panels being dropped. +---@param bDoDrop boolean Whether this is an actual drop or just a hover preview. +---@param command string The drop command string. +---@param x number Cursor X position. +---@param y number Cursor Y position. +function DDragBase:DropAction_Copy(drops, bDoDrop, command, x, y) end diff --git a/custom/DDragBase.DropAction_Normal.lua b/custom/DDragBase.DropAction_Normal.lua new file mode 100644 index 00000000..a15d4c83 --- /dev/null +++ b/custom/DDragBase.DropAction_Normal.lua @@ -0,0 +1,12 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Internal function used in DDragBase:MakeDroppable. Handles the normal drop action with positional drop targeting. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DDragBase:DropAction_Normal +---@param drops Panel[] The list of panels being dropped. +---@param bDoDrop boolean Whether this is an actual drop or just a hover preview. +---@param command string The drop command string ("copy", "move", etc.) +---@param x number Cursor X position relative to the panel. +---@param y number Cursor Y position relative to the panel. +function DDragBase:DropAction_Normal(drops, bDoDrop, command, x, y) end diff --git a/custom/DDragBase.DropAction_Simple.lua b/custom/DDragBase.DropAction_Simple.lua new file mode 100644 index 00000000..ab4154b6 --- /dev/null +++ b/custom/DDragBase.DropAction_Simple.lua @@ -0,0 +1,12 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Internal function used in DDragBase:DropAction_Normal. Handles dropping without positional targeting. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DDragBase:DropAction_Simple +---@param drops Panel[] The list of panels being dropped. +---@param bDoDrop boolean Whether this is an actual drop or just a hover preview. +---@param command string The drop command string. +---@param x number Cursor X position. +---@param y number Cursor Y position. +function DDragBase:DropAction_Simple(drops, bDoDrop, command, x, y) end diff --git a/custom/DForm.TextEntry.lua b/custom/DForm.TextEntry.lua new file mode 100644 index 00000000..ea9c0320 --- /dev/null +++ b/custom/DForm.TextEntry.lua @@ -0,0 +1,7 @@ +---Adds a [DTextEntry](https://wiki.facepunch.com/gmod/DTextEntry) to a [DForm](https://wiki.facepunch.com/gmod/DForm) +---@realm client +---@source https://wiki.facepunch.com/gmod/DForm:TextEntry +---@param label string The label for the text entry. +---@param convar? string The convar to link the text entry to. +---@return DTextEntry # The created DTextEntry +function DForm:TextEntry(label, convar) end diff --git a/custom/DHorizontalDivider.SetLeft.lua b/custom/DHorizontalDivider.SetLeft.lua new file mode 100644 index 00000000..bcff8c99 --- /dev/null +++ b/custom/DHorizontalDivider.SetLeft.lua @@ -0,0 +1,5 @@ +---Sets the left side content of the [DHorizontalDivider](https://wiki.facepunch.com/gmod/DHorizontalDivider). +---@realm client +---@source https://wiki.facepunch.com/gmod/DHorizontalDivider:SetLeft +---@param pnl Panel? The panel to set as the left side, or nil to detach. +function DHorizontalDivider:SetLeft(pnl) end diff --git a/custom/DHorizontalDivider.SetRight.lua b/custom/DHorizontalDivider.SetRight.lua new file mode 100644 index 00000000..53e1f19d --- /dev/null +++ b/custom/DHorizontalDivider.SetRight.lua @@ -0,0 +1,5 @@ +---Sets the right side content of the [DHorizontalDivider](https://wiki.facepunch.com/gmod/DHorizontalDivider). +---@realm client +---@source https://wiki.facepunch.com/gmod/DHorizontalDivider:SetRight +---@param pnl Panel? The panel to set as the right side, or nil to detach. +function DHorizontalDivider:SetRight(pnl) end diff --git a/custom/DListView.GetLine.lua b/custom/DListView.GetLine.lua new file mode 100644 index 00000000..2a89963f --- /dev/null +++ b/custom/DListView.GetLine.lua @@ -0,0 +1,7 @@ +---Gets the DListView_Line at the given index. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView:GetLine +---@param id number The index of the line to get. +---@return DListView_Line # The DListView_Line at the given index. +function DListView:GetLine(id) end diff --git a/custom/DListView.GetSelectedLine.lua b/custom/DListView.GetSelectedLine.lua new file mode 100644 index 00000000..1c1b90d7 --- /dev/null +++ b/custom/DListView.GetSelectedLine.lua @@ -0,0 +1,9 @@ +---Gets the currently selected DListView_Line index. +--- +--- If [DListView:SetMultiSelect](https://wiki.facepunch.com/gmod/DListView:SetMultiSelect) is set to true, only the first line of all selected lines will be returned. Use [DListView:GetSelected](https://wiki.facepunch.com/gmod/DListView:GetSelected) instead to get all of the selected lines. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView:GetSelectedLine +---@return number # The index of the currently selected line. +---@return DListView_Line # The currently selected DListView_Line. +function DListView:GetSelectedLine() end diff --git a/custom/DListView_Column.SetWidth.lua b/custom/DListView_Column.SetWidth.lua new file mode 100644 index 00000000..b020d646 --- /dev/null +++ b/custom/DListView_Column.SetWidth.lua @@ -0,0 +1,7 @@ +---Sets the width of the column, clamped between the column's min and max width. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DListView_Column:SetWidth +---@param width number The desired column width in pixels. +---@return number # The actual width the column was set to (clamped and ceiled). +function DListView_Column:SetWidth(width) end diff --git a/custom/DTree_Node.AnimSlide.lua b/custom/DTree_Node.AnimSlide.lua new file mode 100644 index 00000000..70403119 --- /dev/null +++ b/custom/DTree_Node.AnimSlide.lua @@ -0,0 +1,10 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Internal function that handles the expand/collapse animations. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:AnimSlide +---@param anim DermaAnimation The running animation object. +---@param delta number The animation progress delta (0..1). +---@param data table User data passed to the animation. +function DTree_Node:AnimSlide(anim, delta, data) end diff --git a/custom/Global.TauntCamera.lua b/custom/Global.TauntCamera.lua new file mode 100644 index 00000000..1fb1637a --- /dev/null +++ b/custom/Global.TauntCamera.lua @@ -0,0 +1,5 @@ +---Returns a new [TauntCamera](https://wiki.facepunch.com/gmod/TauntCamera) object used by player classes to drive a third-person taunt view. +---@realm client +---@source https://wiki.facepunch.com/gmod/Global.TauntCamera +---@return TauntCamera # The created taunt camera object. +function _G.TauntCamera() end diff --git a/custom/PANEL.OnDrop.lua b/custom/PANEL.OnDrop.lua new file mode 100644 index 00000000..83c08753 --- /dev/null +++ b/custom/PANEL.OnDrop.lua @@ -0,0 +1,9 @@ +---We're being dropped on something +--- We can create a new panel here and return it, so that instead of dropping us - it drops the new panel instead! We remain where we are! +--- Only works for panels derived from [DDragBase](https://wiki.facepunch.com/gmod/DDragBase). +---@hook OnDrop +---@realm client +---@source https://wiki.facepunch.com/gmod/PANEL:OnDrop +---@param target Panel The panel being dropped onto. +---@return Panel # The panel to drop instead of us. By default you should return self. +function Panel:OnDrop(target) end diff --git a/custom/PropSelect.AddModel.lua b/custom/PropSelect.AddModel.lua new file mode 100644 index 00000000..ffb9dc4c --- /dev/null +++ b/custom/PropSelect.AddModel.lua @@ -0,0 +1,6 @@ +---Adds a new model to the selection list. +---@realm client +---@source https://wiki.facepunch.com/gmod/PropSelect:AddModel +---@param model string Model path, **including** `models/` and `.mdl`. +---@param convars? table A list of convar names (as keys) and their values to set when the user selects this model. May be nil or non-table (validated internally). +function PropSelect:AddModel(model, convars) end diff --git a/custom/TOOL.LeftClick.lua b/custom/TOOL.LeftClick.lua new file mode 100644 index 00000000..b7654898 --- /dev/null +++ b/custom/TOOL.LeftClick.lua @@ -0,0 +1,8 @@ +---Called when the user left clicks with the tool. +---@hook LeftClick +---@realm shared +---@source https://wiki.facepunch.com/gmod/TOOL:LeftClick +---@param tr TraceResult A trace from user's eyes to wherever they aim at. See Structures/TraceResult +---@overload fun(self: Tool, tr: TraceResult, fromRight: boolean): boolean +---@return boolean # Return `true` to draw the tool gun beam and play fire animations, `false` otherwise. +function Tool:LeftClick(tr) end diff --git a/custom/Tool.GetOwner.lua b/custom/Tool.GetOwner.lua new file mode 100644 index 00000000..95619c67 --- /dev/null +++ b/custom/Tool.GetOwner.lua @@ -0,0 +1,7 @@ +---Returns the owner of this tool. +--- At runtime this is always a valid player when the tool is active; +--- this override removes spurious nil-return diagnostics. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Tool:GetOwner +---@return Player # The player using the tool. Always valid when called from tool callbacks. +function Tool:GetOwner() end diff --git a/custom/Tool.GetWeapon.lua b/custom/Tool.GetWeapon.lua new file mode 100644 index 00000000..a28d8d04 --- /dev/null +++ b/custom/Tool.GetWeapon.lua @@ -0,0 +1,8 @@ +---Returns the Tool Gun (`gmod_tool`) Scripted Weapon. +--- At runtime this is always set after tool initialization; this override +--- removes the spurious nil-return diagnostic that the LS infers from +--- ToolObj:Create() initialising SWEP to nil. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Tool:GetWeapon +---@return Weapon # The tool gun weapon (`gmod_tool`). Always valid after Init. +function Tool:GetWeapon() end diff --git a/custom/Weapon.GetToolObject.lua b/custom/Weapon.GetToolObject.lua new file mode 100644 index 00000000..b00832a5 --- /dev/null +++ b/custom/Weapon.GetToolObject.lua @@ -0,0 +1,6 @@ +---Returns the tool object associated with the current or specified tool mode. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Weapon:GetToolObject +---@param tool? string The tool mode to retrieve. Defaults to the currently active tool mode. +---@return Tool? # The Tool object for the given mode, or `nil`/`false` if the mode has no tool object. +function Weapon:GetToolObject(tool) end diff --git a/custom/_globals.lua b/custom/_globals.lua index c9147d14..49ddc46d 100644 --- a/custom/_globals.lua +++ b/custom/_globals.lua @@ -83,7 +83,7 @@ MAX_PLAYER_BITS = nil ---The active env_skypaint entity. [(View Source)](https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/base/entities/entities/env_skypaint.lua#L131) g_SkyPaint = nil ----@type PANEL +---@type Panel ---Base panel used for context menus. [(View Source)](https://github.com/garrynewman/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/contextmenu.lua#L143) g_ContextMenu = nil @@ -91,7 +91,7 @@ g_ContextMenu = nil ---Base panel for displaying incoming/outgoing voice messages. [(View Source)](https://github.com/garrynewman/garrysmod/blob/master/garrysmod/gamemodes/base/gamemode/cl_voice.lua#L135) g_VoicePanelList = nil ----@type PANEL +---@type SpawnMenu ---Base panel for the spawn menu. [(View Source)](https://github.com/garrynewman/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/spawnmenu.lua#L207) g_SpawnMenu = nil diff --git a/custom/class.ContentContainer.lua b/custom/class.ContentContainer.lua index f5c2bd0f..c3087fac 100644 --- a/custom/class.ContentContainer.lua +++ b/custom/class.ContentContainer.lua @@ -1,4 +1,8 @@ ----@class ContentContainer : DIconLayout +---@class ContentContainer : DScrollPanel +---@field IconList DTileLayout The tile layout panel that holds content icons, created in Init. +---@field m_pControllerPanel? Panel The controller panel (AccessorFunc-backed). +---@field m_strCategoryName? string The category name for this content container (AccessorFunc-backed). +---@field m_bTriggerSpawnlistChange boolean Whether modifications trigger the SpawnlistContentChanged hook (AccessorFunc-backed). local ContentContainer = {} ---@param trigger boolean diff --git a/custom/class.ContentSidebar.lua b/custom/class.ContentSidebar.lua new file mode 100644 index 00000000..0bfe498f --- /dev/null +++ b/custom/class.ContentSidebar.lua @@ -0,0 +1,16 @@ +---@class ContentSidebar : DPanel +---@field Tree DTree The tree panel listing spawnlist categories and nodes. +---@field Search? Panel The search panel, present after EnableSearch() is called. +---@field Toolbox? ContentSidebarToolbox The toolbox drawer, present after EnableModify() is called. +local ContentSidebar = {} + +---Enables search functionality on this sidebar. +---@param stype? string The search type identifier passed to the search panel. +---@param hookname? string The hook name to populate content. Defaults to "PopulateContent". +function ContentSidebar:EnableSearch(stype, hookname) end + +---Creates and attaches the save/revert notification bar. +function ContentSidebar:CreateSaveNotification() end + +---Enables full modify mode: calls EnableSearch(), CreateSaveNotification(), and adds the toolbox drawer. +function ContentSidebar:EnableModify() end diff --git a/custom/class.DListView.lua b/custom/class.DListView.lua index 5aa62f69..522139f2 100644 --- a/custom/class.DListView.lua +++ b/custom/class.DListView.lua @@ -1,7 +1,7 @@ ---@class DListView : DPanel ---@field Columns DListView_Column[] ---@field Lines DListView_Line[] ----@field Sorted table Lines sorted by the current column/order. +---@field Sorted DListView_Line[] Lines sorted by the current column/order. ---@field pnlCanvas Panel ---@field VBar? DVScrollBar local DListView = {} diff --git a/custom/class.DNotify.lua b/custom/class.DNotify.lua index 72adb258..c2dac523 100644 --- a/custom/class.DNotify.lua +++ b/custom/class.DNotify.lua @@ -1,3 +1,6 @@ ---@class DNotify : Panel ----@field Items table +---@field Items table The list of active notification panels. +---@field Spacing number Spacing between notification items (AccessorFunc-backed). +---@field Alignment integer Alignment of notification items within the panel (AccessorFunc-backed). +---@field m_fLifeLength number Default lifetime in seconds for new items (AccessorFunc-backed via SetLife/GetLife). local DNotify = {} diff --git a/custom/class.DNumSlider.lua b/custom/class.DNumSlider.lua index 265c0ac4..3646148a 100644 --- a/custom/class.DNumSlider.lua +++ b/custom/class.DNumSlider.lua @@ -2,5 +2,7 @@ ---@field Label DLabel The label panel for the slider. ---@field TextArea DTextEntry The text entry panel for the slider value. ---@field Slider DSlider The slider knob panel. ----@field Scratch DNumberScratch The number scratch panel. +---@field Scratch DNumberScratch The number scratch panel attached to the label. +---@field Wang DNumberScratch Alias for Scratch; the DNumberScratch overlay on the label. +---@field m_fDefaultValue? number The default value used by ResetToDefaultValue (AccessorFunc-backed). local DNumSlider = {} diff --git a/custom/class.DPropertySheet.lua b/custom/class.DPropertySheet.lua index 2154fdb3..80f6c8ad 100644 --- a/custom/class.DPropertySheet.lua +++ b/custom/class.DPropertySheet.lua @@ -1,5 +1,6 @@ --- A tab oriented control where you can create multiple tabs with items within. Used mainly for organization. ---@class DPropertySheet : Panel ---@field tabScroller DHorizontalScroller The internal horizontal scroller that manages tab positioning. +---@field animFade DermaAnimation The fade animation used when switching tabs, created in Init via Derma_Anim. ---@field Items DPropertySheetSheet[] The list of tabs added to this sheet. local DPropertySheet = {} diff --git a/custom/class.DTree_Node.lua b/custom/class.DTree_Node.lua index 437e7d53..4ab8dc9b 100644 --- a/custom/class.DTree_Node.lua +++ b/custom/class.DTree_Node.lua @@ -2,9 +2,13 @@ ---@field Label DTree_Node_Button ---@field Expander DExpandButton ---@field Icon DImage ----@field animSlide table +---@field animSlide DermaAnimation The sliding expand/collapse animation, created in Init via Derma_Anim. ---@field fLastClick number ---@field m_pRoot? DTree ---@field m_pParentNode? DTree|DTree_Node ---@field ChildNodes? DListLayout +---@field PropPanel? ContentContainer Content panel for this category node, set by sandbox content hooks. +---@field SMContentPanel? Panel Content container used by the custom spawnlist node (custom.lua). +---@field CustomSpawnlist? boolean Whether this is a custom user spawnlist node. +---@field AddonSpawnlist? boolean Whether this is an addon-provided spawnlist node. local DTree_Node = {} diff --git a/custom/class.DermaAnimation.lua b/custom/class.DermaAnimation.lua new file mode 100644 index 00000000..8a0eb7e1 --- /dev/null +++ b/custom/class.DermaAnimation.lua @@ -0,0 +1,21 @@ +--- Animation object returned by Derma_Anim(). Drives a timed animation callback on a panel. +---@class DermaAnimation +---@field Name string The name assigned to this animation. +---@field Panel Panel The panel this animation belongs to. +---@field Func fun(panel: Panel, anim: DermaAnimation, delta: number, data: any) The animation callback. +---@field Data? any User data passed to the callback each tick. +---@field Running? boolean Whether the animation is currently running. +---@field Started? boolean Set true on the first tick; cleared after first call. +---@field Finished? boolean Set true on the final tick. +---@field Length number Total duration in seconds. +---@field StartTime? number SysTime() when the animation began. +---@field EndTime? number SysTime() when the animation will end. +local DermaAnimation = {} + +function DermaAnimation:Run() end +---@param length number +---@param data? any +function DermaAnimation:Start(length, data) end +function DermaAnimation:Stop() end +---@return boolean +function DermaAnimation:Active() end diff --git a/custom/class.PlayerClass.lua b/custom/class.PlayerClass.lua new file mode 100644 index 00000000..15cf5372 --- /dev/null +++ b/custom/class.PlayerClass.lua @@ -0,0 +1,34 @@ +--- +--- The **PLAYER** table is the structure used to define a custom player class +--- via [player_manager.RegisterClass](https://wiki.facepunch.com/gmod/player_manager.RegisterClass). +--- Player class methods receive the authoring table as `self`, with the driven +--- [Player](https://wiki.facepunch.com/gmod/Player) entity available as `self.Player`. +--- +--- The fields below mirror the shipped `player_default` class +--- (`garrysmod/gamemodes/base/gamemode/player_class/player_default.lua`); the +--- `Player`, `ClassID` and `Func` fields are injected at runtime by +--- `player_manager.lua`'s `LookupPlayerClass`. All fields are optional because a +--- player class only authors the subset it wants to override. +--- +---@class PlayerClass +---@field Player Player The Player entity this class instance is driving. Injected at runtime by player_manager. Always present inside class methods. +---@field ClassID? number Network string ID of the active player class. Injected at runtime by player_manager. +---@field Func? fun() Internal no-op placeholder. Injected at runtime by player_manager. +---@field DisplayName? string Human-readable display name for this player class. +---@field SlowWalkSpeed? number Movement speed when slow-walking (+WALK). Default: 200. +---@field WalkSpeed? number Movement speed when walking (not running). Default: 400. +---@field RunSpeed? number Movement speed when running. Default: 600. +---@field CrouchedWalkSpeed? number Multiplier applied to move speed while crouching. Default: 0.3. +---@field DuckSpeed? number Speed of transition from standing to crouching. Default: 0.3. +---@field UnDuckSpeed? number Speed of transition from crouching to standing. Default: 0.3. +---@field JumpPower? number Vertical impulse strength on jump. Default: 200. +---@field CanUseFlashlight? boolean Whether the player can use the flashlight. Default: true. +---@field MaxHealth? number Maximum health the player can have. Default: 100. +---@field MaxArmor? number Maximum armor the player can have. Default: 100. +---@field StartHealth? number Health given to the player on spawn. Default: 100. +---@field StartArmor? number Armor given to the player on spawn. Default: 0. +---@field DropWeaponOnDie? boolean Whether to drop the active weapon on death. Default: false. +---@field TeammateNoCollide? boolean Whether teammates pass through each other. Default: true. +---@field AvoidPlayers? boolean Whether the player auto-swerves around others. Default: true. +---@field UseVMHands? boolean Whether to use viewmodel hands. Default: true. +PlayerClass = {} diff --git a/custom/class.SKIN.lua b/custom/class.SKIN.lua index 840a3481..178631e2 100644 --- a/custom/class.SKIN.lua +++ b/custom/class.SKIN.lua @@ -69,12 +69,48 @@ ---@field Category SKINColoursCategory ---@field TooltipText Color +---@class SKINTexScroller +---@field TrackV fun(x: number, y: number, w: number, h: number) Vertical scrollbar track texture. +---@field ButtonV_Normal fun(x: number, y: number, w: number, h: number) Vertical scroll grip, normal state. +---@field ButtonV_Hover fun(x: number, y: number, w: number, h: number) Vertical scroll grip, hovered. +---@field ButtonV_Down fun(x: number, y: number, w: number, h: number) Vertical scroll grip, pressed. +---@field ButtonV_Disabled fun(x: number, y: number, w: number, h: number) Vertical scroll grip, disabled. +---@field TrackH fun(x: number, y: number, w: number, h: number) Horizontal scrollbar track texture. +---@field ButtonH_Normal fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, normal state. +---@field ButtonH_Hover fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, hovered. +---@field ButtonH_Down fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, pressed. +---@field ButtonH_Disabled fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, disabled. +---@field LeftButton_Normal fun(x: number, y: number, w: number, h: number) Left scroll arrow, normal. +---@field LeftButton_Hover fun(x: number, y: number, w: number, h: number) Left scroll arrow, hovered. +---@field LeftButton_Down fun(x: number, y: number, w: number, h: number) Left scroll arrow, pressed. +---@field LeftButton_Disabled fun(x: number, y: number, w: number, h: number) Left scroll arrow, disabled. +---@field LeftButton_Dead fun(x: number, y: number, w: number, h: number) Left scroll arrow, dead/inactive (alias used by PaintButtonLeft). +---@field UpButton_Normal fun(x: number, y: number, w: number, h: number) Up scroll arrow, normal. +---@field UpButton_Hover fun(x: number, y: number, w: number, h: number) Up scroll arrow, hovered. +---@field UpButton_Down fun(x: number, y: number, w: number, h: number) Up scroll arrow, pressed. +---@field UpButton_Disabled fun(x: number, y: number, w: number, h: number) Up scroll arrow, disabled. +---@field UpButton_Dead fun(x: number, y: number, w: number, h: number) Up scroll arrow, dead/inactive (alias used by PaintButtonUp). +---@field RightButton_Normal fun(x: number, y: number, w: number, h: number) Right scroll arrow, normal. +---@field RightButton_Hover fun(x: number, y: number, w: number, h: number) Right scroll arrow, hovered. +---@field RightButton_Down fun(x: number, y: number, w: number, h: number) Right scroll arrow, pressed. +---@field RightButton_Disabled fun(x: number, y: number, w: number, h: number) Right scroll arrow, disabled. +---@field RightButton_Dead fun(x: number, y: number, w: number, h: number) Right scroll arrow, dead/inactive (alias used by PaintButtonRight). +---@field DownButton_Normal fun(x: number, y: number, w: number, h: number) Down scroll arrow, normal. +---@field DownButton_Hover fun(x: number, y: number, w: number, h: number) Down scroll arrow, hovered. +---@field DownButton_Down fun(x: number, y: number, w: number, h: number) Down scroll arrow, pressed. +---@field DownButton_Disabled fun(x: number, y: number, w: number, h: number) Down scroll arrow, disabled. +---@field DownButton_Dead fun(x: number, y: number, w: number, h: number) Down scroll arrow, dead/inactive (alias used by PaintButtonDown). + +---@class SKINTex +---@field Scroller SKINTexScroller + --- Active Derma skin table used by derma and GWEN. ---@class SKIN ---@field Name? string Internal skin registry name assigned by derma.DefineSkin. ---@field Description? string Human-readable skin description assigned by derma.DefineSkin. ---@field Base? string Optional base skin name assigned by derma.DefineSkin. ---@field Colours SKINColours +---@field tex SKINTex ---@field PaintPanel fun(self: SKIN, panel: Panel, w: number, h: number) ---@field PaintShadow fun(self: SKIN, panel: Panel, w: number, h: number) ---@field PaintFrame fun(self: SKIN, panel: Panel, w: number, h: number) diff --git a/custom/class.SWEP.lua b/custom/class.SWEP.lua index e812821d..5c1fe0cc 100644 --- a/custom/class.SWEP.lua +++ b/custom/class.SWEP.lua @@ -1,2 +1,13 @@ ---@class SWEP : WEAPON +---@field Tool? table Map of tool mode name → instantiated tool object. Set by gmod_tool SWEP. +---@field Mode? string Currently active tool mode name (e.g. "weld"). Set in SWEP:Think by gmod_tool. +---@field current_mode? string The tool mode active this frame. +---@field last_mode? string The tool mode active the previous frame. +---@field m_uHolsterFrame? number Frame number on which the weapon was holstered (used to skip the extra Think call). +---@field Icons? table Cache of loaded icon materials keyed by path. Set by gmod_tool SWEP DrawHUD. +---@field ToolNameHeight? number Height of the tool name HUD element. Used by gmod_tool SWEP. +---@field InfoBoxHeight? number Height of the tool info box HUD element. Used by gmod_tool SWEP. +---@field Gradient? number Texture ID of the gradient texture used for the HUD background. +---@field InfoIcon? number Texture ID of the info icon used for the HUD. +---@field WepSelectIcon? number Texture ID of the weapon select icon. SWEP = {} diff --git a/custom/class.SpawnMenu.lua b/custom/class.SpawnMenu.lua new file mode 100644 index 00000000..2b3f9369 --- /dev/null +++ b/custom/class.SpawnMenu.lua @@ -0,0 +1,10 @@ +---@class SpawnMenu : EditablePanel +---@field HorizontalDivider DHorizontalDivider The central horizontal divider panel. +---@field ToolMenu ToolMenu The right-side tool menu panel. +---@field CreateMenu CreationMenu The left-side creation/content menu panel. +---@field ToolToggle DImageButton The button that toggles the tool menu visibility. +---@field m_bHangOpen boolean Whether the spawn menu stays open (hang-open mode). +---@field CustomizableSpawnlistNode? any Injected reference to the customizable spawnlist node (optional). +---@field SearchPropPanel? Panel Injected reference to the search prop panel (optional). +---@field StartupTool? Panel The tool item panel to select and activate on first open (set by toolpanel.lua). +local SpawnMenu = {} diff --git a/custom/class.TOOL.lua b/custom/class.TOOL.lua deleted file mode 100644 index 570a298f..00000000 --- a/custom/class.TOOL.lua +++ /dev/null @@ -1,15 +0,0 @@ ---- ---- The **TOOL** table is used in Sandbox tool creation. You can find a list of callbacks on the page and a list of methods on the page. Do note that some of the fields below have no effect on server-side operations. ---- ---- The tool information box drawn on the HUD while your tool is selected has 2 values that are set by [language.Add](https://wiki.facepunch.com/gmod/language.Add). ---- * `tool.[tool mode].name` - The tool name (Note this is NOT the same as TOOL.Name) ---- * `tool.[tool mode].desc` - The tool description ---- ---- Ensure that all tool file names are entirely lowercase. Including capital letters can lead to unintended behavior. ---- ----@class Tool ----@field BuildCPanel fun(panel: ControlPanel) Called to populate the tool's control panel. Override to add your controls. -Tool = Tool or {} - ----@class TOOL : Tool -TOOL = {} diff --git a/custom/class.TauntCamera.lua b/custom/class.TauntCamera.lua new file mode 100644 index 00000000..8828369d --- /dev/null +++ b/custom/class.TauntCamera.lua @@ -0,0 +1,25 @@ +--- A taunt camera object returned by [TauntCamera](https://wiki.facepunch.com/gmod/Global.TauntCamera). +--- Used by player classes to drive a third-person taunt view. +--- Source: garrysmod/gamemodes/base/gamemode/player_class/taunt_camera.lua +---@class TauntCamera +local TauntCamera = {} + +---Returns whether the local player should be drawn while the taunt camera is active. +---@param ply Player The player the camera is following. +---@param on boolean Whether the taunt camera is currently active. +---@return boolean # True if the local player should be drawn. +function TauntCamera:ShouldDrawLocalPlayer(ply, on) end + +---Adjusts the player's view for the taunt camera. +---@param view table The view table (see Structures/CamData). +---@param ply Player The player the camera is following. +---@param on boolean Whether the taunt camera is currently active. +---@return boolean # True if the view was modified. +function TauntCamera:CalcView(view, ply, on) end + +---Processes the player's movement command for the taunt camera. +---@param cmd CUserCmd The movement command to adjust. +---@param ply Player The player the camera is following. +---@param on boolean Whether the taunt camera is currently active. +---@return boolean # True if the command was handled. +function TauntCamera:CreateMove(cmd, ply, on) end diff --git a/custom/class.Tool.lua b/custom/class.Tool.lua new file mode 100644 index 00000000..dcaddb1d --- /dev/null +++ b/custom/class.Tool.lua @@ -0,0 +1,66 @@ +--- +--- The **TOOL** table is used in Sandbox tool creation. You can find a list of callbacks on the page and a list of methods on the page. Do note that some of the fields below have no effect on server-side operations. +--- +--- The tool information box drawn on the HUD while your tool is selected has 2 values that are set by [language.Add](https://wiki.facepunch.com/gmod/language.Add). +--- * `tool.[tool mode].name` - The tool name (Note this is NOT the same as TOOL.Name) +--- * `tool.[tool mode].desc` - The tool description +--- +--- Ensure that all tool file names are entirely lowercase. Including capital letters can lead to unintended behavior. + +--- One slot in the tool's object array (set via Tool:SetObject). +---@class ToolObjectSlot +---@field Ent Entity The entity stored in this slot. +---@field Phys PhysObj|nil The physics object for this slot (nil for world entity). +---@field Bone number The physics bone index. +---@field Pos Vector The local-space hit position (world-space for world entity). +---@field Normal Vector The local-space hit normal (world-space for world entity). + +--- The Objects array on a tool. Named class with a non-nil index operator so that +--- direct `self.Objects[i]` accesses inside tool methods (GetPos, GetEnt, SetObject, etc.) +--- do not generate spurious unchecked-nil-access diagnostics. +--- Callers must guarantee the index is valid before calling any getter. +---@class ToolObjects +---@operator index(integer): ToolObjectSlot + +---@class Tool +---@field Mode string The tool mode string (e.g. "weld", "balloon"). +---@field SWEP Weapon The weapon entity this tool belongs to. +---@field Weapon Weapon Alias for SWEP; the weapon entity this tool belongs to. +---@field Owner Player The player who owns this tool. +---@field Objects ToolObjects Array of stored constraint objects indexed 1-based. +---@field Stage number The current stage of the tool. +---@field Message string The current message/hint string. +---@field LastMessage number CurTime of the last displayed message. +---@field AllowedCVar ConVar ConVar controlling whether this tool is allowed (toolmode_allow_). +---@field ClientConVar table Default client convar name → value pairs. +---@field ServerConVar table Default server convar name → value pairs. +---@field ClientConVars table Instantiated client ConVar objects keyed by name. +---@field ServerConVars table Instantiated server ConVar objects keyed by name. +---@field GhostEntity Entity|nil The current ghost entity, or nil if none. +---@field GhostEntities table? Legacy ghost entity table (unused in base code). +---@field GhostOffset table? Legacy ghost offset table (unused in base code). +---@field BuildCPanel fun(panel: ControlPanel) Called to populate the tool's control panel. Override to add your controls. +---@field Information (string | {name: string, stage: number?, op: number?, icon: string?, icon2: string?})[]? Array of stage-information descriptors. Each element is either a plain string key or a table descriptor with optional stage/op/icon fields. +---@field AddToMenu? boolean Whether to add this tool to the spawn menu tool list. Default true. +---@field Category? string The tool category in the spawn menu (e.g. "Construction"). Default "New Category". +---@field Tab? string The spawn menu tab to place the tool in. Default "Main". +---@field Name? string Display name of the tool shown in the spawn menu. +---@field Command? string The console command to switch to this tool. Default "gmod_tool ". +---@field ConfigName? string The name used for convar config storage. Default is the tool mode. +---@field LeftClickAutomatic? boolean If true, LeftClick fires continuously while held. +---@field RightClickAutomatic? boolean If true, RightClick fires continuously while held. +---@field RequiresTraceHit? boolean If true, tool only fires when the trace hits something. +---@field Init? fun(self: Tool) Called on tool initialization after Create(). +Tool = Tool or {} + +---Returns the Tool Gun (`gmod_tool`) Scripted Weapon. Never nil at runtime after Init. +---@return Weapon # The tool gun weapon. (`gmod_tool`) +function Tool:GetWeapon() end + +---Initializes a ghost entity from the given entity's model/pos/angles. +--- This is the plural-named alias called from SWEP:StartGhostEntities; behaviour is identical to Tool:StartGhostEntity. +---@param ent Entity The entity to copy ghost parameters from. +function Tool:StartGhostEntities(ent) end + +---@class TOOL : Tool +TOOL = {} diff --git a/custom/class.Weapon.lua b/custom/class.Weapon.lua new file mode 100644 index 00000000..51e5b6ee --- /dev/null +++ b/custom/class.Weapon.lua @@ -0,0 +1,15 @@ +---@class Weapon : Entity +local Weapon = {} +---@class WEAPON : Weapon +WEAPON = Weapon + +---Returns the owner of this weapon, narrowed to [Player](https://wiki.facepunch.com/gmod/Player). +--- +--- Weapons are owned by players (or sometimes NPCs); for SWEP code `self:GetOwner()` +--- is the wielding player in the vast majority of cases. This narrows the base +--- [Entity:GetOwner](https://wiki.facepunch.com/gmod/Entity:GetOwner) return so +--- shared `Player` methods (e.g. `KeyDown`) resolve correctly in weapon code. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:GetOwner +---@return Player # The player who owns this weapon. +function Weapon:GetOwner() end diff --git a/custom/class.env_fog_controller.lua b/custom/class.env_fog_controller.lua new file mode 100644 index 00000000..1493ac51 --- /dev/null +++ b/custom/class.env_fog_controller.lua @@ -0,0 +1,2 @@ +---@class env_fog_controller : Entity +local env_fog_controller = {} diff --git a/custom/class.env_projectedtexture.lua b/custom/class.env_projectedtexture.lua new file mode 100644 index 00000000..6333349e --- /dev/null +++ b/custom/class.env_projectedtexture.lua @@ -0,0 +1,2 @@ +---@class env_projectedtexture : Entity +local env_projectedtexture = {} diff --git a/custom/class.env_sun.lua b/custom/class.env_sun.lua new file mode 100644 index 00000000..2afd0f24 --- /dev/null +++ b/custom/class.env_sun.lua @@ -0,0 +1,2 @@ +---@class env_sun : Entity +local env_sun = {} diff --git a/custom/class.npc_manhack.lua b/custom/class.npc_manhack.lua new file mode 100644 index 00000000..d79e7b4e --- /dev/null +++ b/custom/class.npc_manhack.lua @@ -0,0 +1,2 @@ +---@class npc_manhack : Entity +local npc_manhack = {} diff --git a/custom/class.npc_rollermine.lua b/custom/class.npc_rollermine.lua new file mode 100644 index 00000000..79065434 --- /dev/null +++ b/custom/class.npc_rollermine.lua @@ -0,0 +1,2 @@ +---@class npc_rollermine : Entity +local npc_rollermine = {} diff --git a/custom/class.prop_physics.lua b/custom/class.prop_physics.lua new file mode 100644 index 00000000..d4192e68 --- /dev/null +++ b/custom/class.prop_physics.lua @@ -0,0 +1,2 @@ +---@class prop_physics : Entity +local prop_physics = {} diff --git a/custom/constraint.Elastic.lua b/custom/constraint.Elastic.lua new file mode 100644 index 00000000..e986dce0 --- /dev/null +++ b/custom/constraint.Elastic.lua @@ -0,0 +1,21 @@ +---Creates an elastic rope constraint. +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Elastic +---@param ent1 Entity First entity. +---@param ent2 Entity Second entity. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +--- See Entity:TranslateBoneToPhysBone. +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). +--- See Entity:TranslateBoneToPhysBone. +---@param localPos1 Vector Position relative to the the first physics object to constrain to. +---@param localPos2 Vector Position relative to the the second physics object to constrain to. +---@param constant number Stiffness of the elastic. The larger the number the less the elastic will stretch. +---@param damping number How much energy the elastic loses. The larger the number, the less bouncy the elastic. +---@param relDamping number The amount of energy the elastic loses proportional to the relative velocity of the two objects the elastic is attached to. +---@param material? string The material of the rope. If unset, will be solid black. +---@param width number Width of rope. +---@param stretchOnly? boolean|number Apply physics forces only on stretch. +---@param color? Color The color of the rope. See Color. +---@return Entity # The created constraint. ([phys_spring](https://developer.valvesoftware.com/wiki/Phys_spring)) Will return `false` if the constraint could not be created. +---@return Entity # The created rope. ([keyframe_rope](https://developer.valvesoftware.com/wiki/Keyframe_rope)) Will return `nil` if the constraint could not be created. +function constraint.Elastic(ent1, ent2, bone1, bone2, localPos1, localPos2, constant, damping, relDamping, material, width, stretchOnly, color) end diff --git a/custom/constraint.Weld.lua b/custom/constraint.Weld.lua new file mode 100644 index 00000000..8530a7b1 --- /dev/null +++ b/custom/constraint.Weld.lua @@ -0,0 +1,14 @@ +---Creates a weld constraint. +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Weld +---@param ent1 Entity The first entity. +---@param ent2 Entity The second entity. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +--- See Entity:TranslateBoneToPhysBone. +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). +--- See Entity:TranslateBoneToPhysBone. +---@param forceLimit? number The amount of force appliable to the constraint before it will break (0 is never). +---@param noCollide? boolean|number Should `ent1` be nocollided to `ent2` via this constraint. +---@param deleteEnt1OnBreak? boolean|number If true, when `ent2` is removed, `ent1` will also be removed. +---@return Entity # The created constraint entity, or false if the constraint failed. ([phys_constraint](https://developer.valvesoftware.com/wiki/Phys_constraint)) +function constraint.Weld(ent1, ent2, bone1, bone2, forceLimit, noCollide, deleteEnt1OnBreak) end diff --git a/custom/construct.SetPhysProp.lua b/custom/construct.SetPhysProp.lua new file mode 100644 index 00000000..efd120df --- /dev/null +++ b/custom/construct.SetPhysProp.lua @@ -0,0 +1,9 @@ +---Sets props physical properties. +---@realm server +---@source https://wiki.facepunch.com/gmod/construct.SetPhysProp +---@param ply Player The player. This variable is not used and can be left out. +---@param ent Entity The entity to apply properties to. +---@param physObjID number You can use this or the argument below. This will be used in case you don't provide argument below. +---@param physObj PhysObj? The physics object to apply the properties to. +---@param data PhysProperties The table containing properties to apply. See Structures/PhysProperties. +function construct.SetPhysProp(ply, ent, physObjID, physObj, data) end diff --git a/custom/player_manager.RegisterClass.lua b/custom/player_manager.RegisterClass.lua new file mode 100644 index 00000000..9a51f776 --- /dev/null +++ b/custom/player_manager.RegisterClass.lua @@ -0,0 +1,7 @@ +---Register a class metatable to be assigned to players later. +---@realm shared +---@source https://wiki.facepunch.com/gmod/player_manager.RegisterClass +---@param name string Class name. +---@param table PlayerClass Class metatable. See the [PlayerClass](https://wiki.facepunch.com/gmod/Player_Classes) structure. +---@param base? string Base class name. +function player_manager.RegisterClass(name, table, base) end diff --git a/custom/structures.TextData.lua b/custom/structures.TextData.lua new file mode 100644 index 00000000..f7e671a9 --- /dev/null +++ b/custom/structures.TextData.lua @@ -0,0 +1,11 @@ +--- Override: make `text` and `pos` optional so that incrementally-built +--- TextData tables (e.g. in gmod_tool SWEP DrawHUD) do not produce +--- missing-fields / param-type-mismatch diagnostics. +--- The real draw.Text / draw.TextShadow functions do require these values +--- to be set before calling, but they are set on the same local table +--- before each call, not at construction time. +---@class (partial) TextData +---Text to be drawn. +---@field text? string +---This holds the X and Y coordinates. Key value 1 is x, key value 2 is y. +---@field pos? table diff --git a/custom/structures.TextureData.lua b/custom/structures.TextureData.lua new file mode 100644 index 00000000..b7d37f68 --- /dev/null +++ b/custom/structures.TextureData.lua @@ -0,0 +1,16 @@ +--- Override: make all TextureData fields optional so that incrementally-built +--- TextureData tables (e.g. in gmod_tool SWEP DrawHUD) do not produce +--- missing-fields diagnostics. +--- The real draw.TexturedQuad function does require these values to be set +--- before calling, but they are set on the same local table before each call. +---@class (partial) TextureData +---surface.GetTextureID number of the texture to be drawn. +---@field texture? number +---The x Coordinate. +---@field x? number +---The y Coordinate. +---@field y? number +---The width of the texture. +---@field w? number +---The height of the texture. +---@field h? number From f4f524b1b68e4aa0d2ff42601d156e5b03b9deee Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 22 Jun 2026 22:34:52 +0100 Subject: [PATCH 066/117] Route orphan overrides case-insensitively --- src/api-writer/glua-api-writer.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts index 9c86288c..89a37bbd 100644 --- a/src/api-writer/glua-api-writer.ts +++ b/src/api-writer/glua-api-writer.ts @@ -522,7 +522,7 @@ export class GluaApiWriter { for (const [filePath, pages] of this.files) { const baseName = filePath.split(/[\\/]/).pop() ?? ''; if (baseName.endsWith('.lua')) { - moduleFileByName.set(baseName.slice(0, -4), filePath); + moduleFileByName.set(baseName.slice(0, -4).toLowerCase(), filePath); } pages.forEach(({ page }) => { @@ -549,8 +549,11 @@ export class GluaApiWriter { const moduleMatch = pageAddress.match(/^([^.]+)\./); if (!moduleMatch) continue; - const moduleFilePath = moduleFileByName.get(moduleMatch[1]); - if (!moduleFilePath) continue; + const moduleFilePath = moduleFileByName.get(moduleMatch[1].toLowerCase()); + if (!moduleFilePath) { + console.warn(`[orphan-override] No module file found for override "${pageAddress}" (prefix "${moduleMatch[1]}"). The override will be dropped.`); + continue; + } const current = orphanFunctionOverrides.get(moduleFilePath) ?? []; current.push(override.endsWith('\n') ? override : `${override}\n`); From b2bfecfad768bf1f34510ee6b7c54e75d1d005d6 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 00:32:20 +0100 Subject: [PATCH 067/117] Change CreateFromTable call argument to table --- custom/vgui.CreateFromTable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/vgui.CreateFromTable.lua b/custom/vgui.CreateFromTable.lua index ecfa4005..e45471ef 100644 --- a/custom/vgui.CreateFromTable.lua +++ b/custom/vgui.CreateFromTable.lua @@ -3,7 +3,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.CreateFromTable ---@generic T: table ----@[call_arg("gmod.vgui_panel", "register_table")] +---@[call_arg("gmod.vgui_panel", "table")] ---@[call_arg_field("gmod.vgui_panel", "base", "Base")] ---@param metatable T Your PANEL table. ---@param parent? Panel Which panel to parent the newly created panel to. From 15eb7df6c747f34ad1811c0d6c10c055cf06a77e Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 01:00:45 +0100 Subject: [PATCH 068/117] Restore CreateFromTable register table call argument --- custom/vgui.CreateFromTable.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/vgui.CreateFromTable.lua b/custom/vgui.CreateFromTable.lua index e45471ef..ecfa4005 100644 --- a/custom/vgui.CreateFromTable.lua +++ b/custom/vgui.CreateFromTable.lua @@ -3,7 +3,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.CreateFromTable ---@generic T: table ----@[call_arg("gmod.vgui_panel", "table")] +---@[call_arg("gmod.vgui_panel", "register_table")] ---@[call_arg_field("gmod.vgui_panel", "base", "Base")] ---@param metatable T Your PANEL table. ---@param parent? Panel Which panel to parent the newly created panel to. From 8f3db74de97ff67523e4418ab2c71f34574c2319 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 01:05:40 +0100 Subject: [PATCH 069/117] Add NextBot loco field annotation --- custom/NextBot.loco.lua | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 custom/NextBot.loco.lua diff --git a/custom/NextBot.loco.lua b/custom/NextBot.loco.lua new file mode 100644 index 00000000..7e930d76 --- /dev/null +++ b/custom/NextBot.loco.lua @@ -0,0 +1,7 @@ +---@meta + +--- The `CLuaLocomotion` instance that controls this NextBot's movement. +-- Accessed via `self.loco` inside NextBot entity methods. +---@class (partial) NextBot +---@field loco CLuaLocomotion # The locomotion controller for this NextBot. +local NextBot = {} From 5468fd2a5d0f83ed3b28335fd46a5a0868a0235c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 04:51:25 +0100 Subject: [PATCH 070/117] Add DesktopWindows list entry annotations --- custom/list.Set.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/custom/list.Set.lua b/custom/list.Set.lua index e04b5dc9..51da0203 100644 --- a/custom/list.Set.lua +++ b/custom/list.Set.lua @@ -1,5 +1,15 @@ ---@meta +---Defines a desktop window entry registered via `list.Set("DesktopWindows", ...)`. +---@class DesktopWindowEntry +---@field title string The window title shown in the context menu icon label. +---@field icon string The icon material path shown in the context menu. +---@field width number The initial window width in pixels. +---@field height number The initial window height in pixels. +---@field onewindow boolean If true, only one instance of this window may be open at a time. +---@field init fun(widgetIcon: Panel, window: DFrame) Called when the user clicks the context menu icon. `widgetIcon` is the DButton icon that was clicked; `window` is the newly created DFrame. + +---@overload fun(identifier: "DesktopWindows", key: string, item: DesktopWindowEntry) ---@overload fun(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor) ---@param identifier string The identifier for the list. ---@param key any The key in the list. From ce57af7261c3377cade4d4aee3dc6cba20098a69 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 05:50:25 +0100 Subject: [PATCH 071/117] Add ToolObj class annotation --- custom/class.ToolObj.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 custom/class.ToolObj.lua diff --git a/custom/class.ToolObj.lua b/custom/class.ToolObj.lua new file mode 100644 index 00000000..c1d3fdb8 --- /dev/null +++ b/custom/class.ToolObj.lua @@ -0,0 +1,11 @@ +---@meta + +--- The prototype object for Sandbox tools. All tools are created from this object +--- via `ToolObj:Create()`, which returns a fresh `TOOL` instance that individual +--- stool files then configure. +--- +--- `ToolObj` shares most behavior with `Tool`; it only differs in the factory +--- method used to spawn a new `TOOL` table. +---@class ToolObj : Tool +---@field Create fun(self: ToolObj): TOOL Factory method that returns a new `TOOL` instance. +ToolObj = ToolObj or {} From 2b677417bbf7425a8941f4b20a0bf006800d0c2e Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:37:13 +0100 Subject: [PATCH 072/117] Fix GetConVar nullable and widen BuildCPanel --- custom/Global.GetConVar.lua | 9 +++++++++ custom/TOOL.lua | 2 +- custom/class.Tool.lua | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 custom/Global.GetConVar.lua diff --git a/custom/Global.GetConVar.lua b/custom/Global.GetConVar.lua new file mode 100644 index 00000000..920c031b --- /dev/null +++ b/custom/Global.GetConVar.lua @@ -0,0 +1,9 @@ +---Gets the [ConVar](https://wiki.facepunch.com/gmod/ConVar) with the specified name. +--- +--- **NOTE**: This function uses [Global.GetConVar_Internal](https://wiki.facepunch.com/gmod/Global.GetConVar_Internal) internally, but caches the result in Lua for quicker lookups. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.GetConVar +---@param name string Name of the ConVar to get +---@return ConVar? # The ConVar object, or nil if no such ConVar was found. +function _G.GetConVar( name ) end diff --git a/custom/TOOL.lua b/custom/TOOL.lua index b38e9d70..59c3732f 100644 --- a/custom/TOOL.lua +++ b/custom/TOOL.lua @@ -35,7 +35,7 @@ TOOL.ServerConVars = nil ---The function that is called to build the context menu for your tool. It has one argument, namely the context menu's base panel to which all of your custom panels are going to be parented to. --- --- While it might sound like a hook, it isn't - you won't receive a `self` argument inside the function. See TOOL.BuildCPanel. ----@type fun(panel: ControlPanel) +---@type fun(panel: ControlPanel, ...any) TOOL.BuildCPanel = nil ---Allows you to override the tool usage information shown when the tool is equipped. diff --git a/custom/class.Tool.lua b/custom/class.Tool.lua index dcaddb1d..88c96247 100644 --- a/custom/class.Tool.lua +++ b/custom/class.Tool.lua @@ -39,7 +39,7 @@ ---@field GhostEntity Entity|nil The current ghost entity, or nil if none. ---@field GhostEntities table? Legacy ghost entity table (unused in base code). ---@field GhostOffset table? Legacy ghost offset table (unused in base code). ----@field BuildCPanel fun(panel: ControlPanel) Called to populate the tool's control panel. Override to add your controls. +---@field BuildCPanel fun(panel: ControlPanel, ...any) Called to populate the tool's control panel. Override to add your controls. ---@field Information (string | {name: string, stage: number?, op: number?, icon: string?, icon2: string?})[]? Array of stage-information descriptors. Each element is either a plain string key or a table descriptor with optional stage/op/icon fields. ---@field AddToMenu? boolean Whether to add this tool to the spawn menu tool list. Default true. ---@field Category? string The tool category in the spawn menu (e.g. "Construction"). Default "New Category". From 5160cfcb48f6dc59d0a1b4c49ec2d72cec77200b Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 23 Jun 2026 18:51:47 +0100 Subject: [PATCH 073/117] Add missing class field annotations --- custom/TOOL.BuildCPanel.lua | 3 ++- custom/class.DFileBrowser.lua | 36 +++++++++++++++++++++------------- custom/class.DHTMLControls.lua | 25 ++++++++++++++--------- custom/class.DNumPad.lua | 14 +++++++++++++ custom/class.DScrollBar.lua | 35 +++++++++++++++++++++++++++++++++ custom/class.DTextEntry.lua | 32 ++++++++++++++++++++++++++++++ custom/class.Entity.lua | 21 ++++++++++++++++++++ custom/class.Weapon.lua | 29 +++++++++++++++++++++++++++ custom/ents.Create.lua | 1 + 9 files changed, 172 insertions(+), 24 deletions(-) create mode 100644 custom/class.DNumPad.lua create mode 100644 custom/class.DScrollBar.lua create mode 100644 custom/class.DTextEntry.lua diff --git a/custom/TOOL.BuildCPanel.lua b/custom/TOOL.BuildCPanel.lua index a87c9025..534f71a6 100644 --- a/custom/TOOL.BuildCPanel.lua +++ b/custom/TOOL.BuildCPanel.lua @@ -2,4 +2,5 @@ ---@realm client ---@source https://wiki.facepunch.com/gmod/TOOL.BuildCPanel ---@param panel ControlPanel The DForm control panel to add settings to. -function TOOL.BuildCPanel(panel) end +---@param ... any Any extra arguments passed via Tool:RebuildControlPanel are forwarded here. +function TOOL.BuildCPanel(panel, ...) end diff --git a/custom/class.DFileBrowser.lua b/custom/class.DFileBrowser.lua index d02b5195..30a0e205 100644 --- a/custom/class.DFileBrowser.lua +++ b/custom/class.DFileBrowser.lua @@ -1,16 +1,24 @@ ----@class DFileBrowser : DPanel ----@field Divider DHorizontalDivider The horizontal divider panel splitting the tree and file list. ----@field Tree DTree The tree view panel for directory navigation. ----@field FolderNode? DTree_Node The root folder node created during setup. ----@field Files? DIconBrowser|DListView The active file list or model icon browser. ----@field FileHeader? Panel The list-view column header for file paths. ----@field bSetup? boolean Whether tree and file panels have been initialized. ----@field m_strName? string ----@field m_strBaseFolder? string +---@class DFileBrowser : Panel +--- The horizontal divider separating the tree and file list. +---@field Divider DHorizontalDivider +--- The directory tree panel. +---@field Tree DTree +--- The file list panel showing files in the current folder. +---@field Files DListView +--- The current path search string. +---@field m_strSearch string +--- The base folder path to browse from. +---@field m_strBaseFolder string +--- The current folder path being viewed. +---@field m_strCurrentFolder string +--- The file extension filter string. +---@field m_strFilter string +--- The virtual file path root (e.g. "GAME", "DATA"). ---@field m_strPath string ----@field m_strSearch? string ----@field m_strFilter? string ----@field m_bModels? boolean ----@field m_strCurrentFolder? string ----@field m_bOpen? boolean +--- The display name of this file browser. +---@field m_strName string +--- Whether to show models instead of files. +---@field m_bModels boolean +--- Whether the browser is currently expanded/open. +---@field m_bOpen boolean local DFileBrowser = {} diff --git a/custom/class.DHTMLControls.lua b/custom/class.DHTMLControls.lua index f63a842f..e3672ed2 100644 --- a/custom/class.DHTMLControls.lua +++ b/custom/class.DHTMLControls.lua @@ -1,15 +1,22 @@ ---@class DHTMLControls : Panel ----@field AddressBar DTextEntry +--- The back navigation button. ---@field BackButton DImageButton +--- The forward navigation button. ---@field ForwardButton DImageButton ----@field RefreshButton DImageButton +--- The refresh/reload button. +---@field ReloadButton DImageButton +--- The home button. ---@field HomeButton DImageButton +--- The stop button. ---@field StopButton DImageButton ----@field History table ----@field Cur integer ----@field Navigating? boolean ----@field BorderSize number ----@field BackgroundColor Color ----@field HomeURL string ----@field HTML? DHTML +--- The address bar text entry. +---@field AddressBar DTextEntry +--- The DHTML panel these controls navigate. +---@field HTML DHTML +--- The current navigation history position. +---@field Cur number +--- Whether we are currently navigating via history buttons. +---@field Navigating boolean +--- The home URL to navigate to. +---@field HomeUrl string local DHTMLControls = {} diff --git a/custom/class.DNumPad.lua b/custom/class.DNumPad.lua new file mode 100644 index 00000000..d80af334 --- /dev/null +++ b/custom/class.DNumPad.lua @@ -0,0 +1,14 @@ +---@class DNumPad : Panel +--- Table of DButton panels for each keypad button (0-15). +---@field Buttons table +--- The currently selected button panel. +---@field m_SelectedButton DButton +--- The currently selected number (0-15 or -1 if none). +---@field m_iSelectedNumber number +--- Padding between buttons. +---@field m_iPadding number +--- Button size scale factor. +---@field m_bButtonSize boolean +--- Whether keys stay selected when pressed (sticky keys mode). +---@field m_bStickyKeys boolean +local DNumPad = {} diff --git a/custom/class.DScrollBar.lua b/custom/class.DScrollBar.lua new file mode 100644 index 00000000..5d02ad6d --- /dev/null +++ b/custom/class.DScrollBar.lua @@ -0,0 +1,35 @@ +---@class DVScrollBar : Panel +--- The up scroll button. +---@field btnUp DButton +--- The down scroll button. +---@field btnDown DButton +--- The scroll bar grip/slider button. +---@field btnGrip DButton +--- Current scroll offset. +---@field Offset number +--- Current scroll position. +---@field Scroll number +--- Total size of the scrollable canvas. +---@field CanvasSize number +--- Size of the scrollbar grip. +---@field BarSize number +local DVScrollBar = {} + +---@class DHScrollBar : Panel +--- The left scroll button. +---@field btnLeft DButton +--- The right scroll button. +---@field btnRight DButton +--- The scroll bar grip/slider button. +---@field btnGrip DButton +--- Current scroll offset. +---@field Offset number +--- Current scroll position. +---@field Scroll number +--- Total size of the scrollable canvas. +---@field CanvasSize number +--- Size of the scrollbar grip. +---@field BarSize number +--- Whether the scroll buttons are hidden. +---@field m_HideButtons boolean +local DHScrollBar = {} diff --git a/custom/class.DTextEntry.lua b/custom/class.DTextEntry.lua new file mode 100644 index 00000000..d11b7225 --- /dev/null +++ b/custom/class.DTextEntry.lua @@ -0,0 +1,32 @@ +---@class DTextEntry : Panel +--- Text entry input history table, used for up/down arrow navigation. +---@field History table +--- Current position in the history table (0 = none selected). +---@field HistoryPos number +--- Whether pressing enter is allowed. +---@field m_bAllowEnter boolean +--- Whether to update the convar as the user types. +---@field m_bUpdateOnType boolean +--- Whether only numeric characters are allowed. +---@field m_bNumeric boolean +--- Whether input history is enabled. +---@field m_bHistory boolean +--- Whether tab key navigation is disabled. +---@field m_bDisableTabbing boolean +--- The font name used for rendering text. +---@field m_FontName string +--- Whether to draw a border around the text entry. +---@field m_bBorder boolean +--- Whether to paint the background. +---@field m_bBackground boolean +--- The color of the text. +---@field m_colText Color +--- The color of the highlight/selection. +---@field m_colHighlight Color +--- The color of the text cursor. +---@field m_colCursor Color +--- The color of the placeholder text. +---@field m_colPlaceholder Color +--- The placeholder text shown when the entry is empty. +---@field m_txtPlaceholder string +local DTextEntry = {} diff --git a/custom/class.Entity.lua b/custom/class.Entity.lua index ec936b7e..fdee3a26 100644 --- a/custom/class.Entity.lua +++ b/custom/class.Entity.lua @@ -6,6 +6,27 @@ local Entity = {} ---@class ENTITY : Entity ENTITY = Entity +--- Base class name for inheritance (e.g. "base_entity"). +---@field Base string +--- Entity type (e.g. "anim", "ai", "nextbot", "point"). +---@field Type string +--- Whether the entity can be spawned from the spawn menu. +---@field Spawnable boolean +--- Whether only admins can spawn this entity. +---@field AdminOnly boolean +--- Display name shown in the spawn menu. +---@field PrintName string +--- Author name shown in the spawn menu. +---@field Author string +--- Contact info shown in the spawn menu. +---@field Contact string +--- Purpose description shown in the spawn menu. +---@field Purpose string +--- Usage instructions shown in the spawn menu. +---@field Instructions string +--- Whether the entity animates automatically. +---@field AutomaticFrameAdvance boolean + ---Returns a table containing all key-value pairs stored on this entity's Lua table. ---The returned table contains all fields but method calls via `:` are not supported. ---@return tableof diff --git a/custom/class.Weapon.lua b/custom/class.Weapon.lua index 51e5b6ee..7715aa73 100644 --- a/custom/class.Weapon.lua +++ b/custom/class.Weapon.lua @@ -3,6 +3,35 @@ local Weapon = {} ---@class WEAPON : Weapon WEAPON = Weapon +---@alias WeaponAmmoTable { ClipSize: number, DefaultClip: number, Automatic: boolean, Ammo: string } + +--- Display name of the weapon, shown on the HUD and in the spawn menu. +---@field PrintName string +--- Author of the weapon, displayed in the spawn menu. +---@field Author string +--- Contact information for the author, shown in the spawn menu. +---@field Contact string +--- Short description of the weapon's purpose, shown in the spawn menu. +---@field Purpose string +--- Instructions for using the weapon, shown in the spawn menu. +---@field Instructions string +--- Field of view for the view model. Default `62`. +---@field ViewModelFOV number +--- Whether to flip the view model. Default `false`. +---@field ViewModelFlip boolean +--- Path to the view model. Default `"models/weapons/v_pistol.mdl"`. +---@field ViewModel string +--- Path to the world model. Default `"models/weapons/w_357.mdl"`. +---@field WorldModel string +--- Whether the weapon can be spawned by players from the spawn menu. Default `false`. +---@field Spawnable boolean +--- Whether only admins can spawn this weapon from the spawn menu. Default `false`. +---@field AdminOnly boolean +--- Primary fire ammo configuration. +---@field Primary WeaponAmmoTable +--- Secondary fire ammo configuration. +---@field Secondary WeaponAmmoTable + ---Returns the owner of this weapon, narrowed to [Player](https://wiki.facepunch.com/gmod/Player). --- --- Weapons are owned by players (or sometimes NPCs); for SWEP code `self:GetOwner()` diff --git a/custom/ents.Create.lua b/custom/ents.Create.lua index 6d9228f0..84f01284 100644 --- a/custom/ents.Create.lua +++ b/custom/ents.Create.lua @@ -28,6 +28,7 @@ ---| "widget_axis_arrow" ---| "widget_axis_disc" ---| "widget_bone" +---| "widget_bones" ---@overload fun(class: KnownEngineEntityClass): Entity ---@generic T : Entity ---@param class `T` The classname of the entity to create. From 06466c33e760e79ad65e7b8ed8d84d6826235668 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Wed, 24 Jun 2026 04:57:41 +0100 Subject: [PATCH 074/117] Fix various annotation overrides to be accurate to source --- __tests__/custom-annotations.spec.ts | 92 +++++++++++++++++++++++++++- custom/DPanelList.Clear.lua | 5 ++ custom/Entity.FrameAdvance.lua | 5 ++ custom/PANEL.PerformLayout.lua | 8 +++ custom/TOOL.Deploy.lua | 9 +++ custom/TOOL.Holster.lua | 7 +++ custom/TOOL.LeftClick.lua | 1 - custom/Weapon.GetToolObject.lua | 7 ++- custom/class.DFileBrowser.lua | 10 +-- custom/class.DHTMLControls.lua | 8 +-- custom/class.DNumPad.lua | 4 +- custom/class.DScrollBar.lua | 35 ----------- custom/class.SpawnMenu.lua | 4 +- custom/class.Weapon.lua | 10 ++- custom/constraint.Elastic.lua | 4 +- custom/constraint.Weld.lua | 2 +- 16 files changed, 151 insertions(+), 60 deletions(-) create mode 100644 custom/DPanelList.Clear.lua create mode 100644 custom/Entity.FrameAdvance.lua create mode 100644 custom/PANEL.PerformLayout.lua create mode 100644 custom/TOOL.Deploy.lua create mode 100644 custom/TOOL.Holster.lua delete mode 100644 custom/class.DScrollBar.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index fcbf4dde..291dc998 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -2,6 +2,9 @@ import fs from 'fs'; import path from 'path'; describe('custom and plugin annotation smoke checks', () => { + const readCustom = (file: string) => fs.readFileSync(path.join(process.cwd(), 'custom', file), 'utf8'); + const readOutput = (file: string) => fs.readFileSync(path.join(process.cwd(), 'output', file), 'utf8'); + test('darkrp plugin annotation files exist and are scoped', () => { const darkrpLua = path.join(process.cwd(), 'plugin', 'darkrp', 'annotations', 'darkrp.lua'); const camiLua = path.join(process.cwd(), 'plugin', 'cami', 'annotations', 'cami.lua'); @@ -276,7 +279,7 @@ describe('custom and plugin annotation smoke checks', () => { expect(entsCreate).toMatch(/---@alias KnownEngineEntityClass/); expect(entsCreate).toMatch(/"phys_constraint"/); - expect(entsCreate).not.toMatch(/"widget_bones"/); + expect(entsCreate).toMatch(/"widget_bones"/); expect(entsCreate).toMatch(/---@overload fun\(class: KnownEngineEntityClass\): Entity/); expect(entsCreate).toMatch(/---@return \(instance\) T\|NULL/); expect(vehicleGetDriver).toMatch(/---@return Player\|NULL driver/); @@ -425,4 +428,91 @@ describe('custom and plugin annotation smoke checks', () => { expect(entsIterator).toMatch(/---@return integer # The origin index \(0\)\./); }); + test('verified source-backed annotation fixes are preserved', () => { + const dFileBrowser = readCustom('class.DFileBrowser.lua'); + const generatedDFileBrowser = readOutput('dfilebrowser.lua'); + const generatedCustomClasses = readOutput('custom_classes.lua'); + const dHtmlControls = readCustom('class.DHTMLControls.lua'); + const generatedDHtmlControls = readOutput('dhtmlcontrols.lua'); + const dNumPad = readCustom('class.DNumPad.lua'); + const generatedDNumPad = readOutput('dnumpad.lua'); + const spawnMenu = readCustom('class.SpawnMenu.lua'); + const weaponClass = readCustom('class.Weapon.lua'); + const generatedWeapon = readOutput('weapon.lua'); + const getToolObject = readCustom('Weapon.GetToolObject.lua'); + const toolLeftClick = readCustom('TOOL.LeftClick.lua'); + const generatedTool = readOutput('tool.lua'); + const weld = readCustom('constraint.Weld.lua'); + const elastic = readCustom('constraint.Elastic.lua'); + const generatedConstraint = readOutput('constraint.lua'); + const generatedEntity = readOutput('entity.lua'); + const generatedPanel = readOutput('panel.lua'); + const generatedDPanelList = readOutput('dpanellist.lua'); + + expect(dFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); + expect(dFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); + expect(generatedDFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); + expect(generatedDFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); + expect(generatedDFileBrowser).not.toMatch(/---@field Files DListView/); + + expect(generatedCustomClasses).not.toMatch(/---@class DVScrollBar : Panel[\s\S]*?---@field btnGrip DButton/); + expect(generatedCustomClasses).not.toMatch(/---@class DHScrollBar : Panel[\s\S]*?---@field btnGrip DButton/); + expect(generatedCustomClasses).not.toMatch(/---@class DVScrollBar : Panel/); + expect(generatedCustomClasses).not.toMatch(/---@class DHScrollBar : Panel/); + + expect(dHtmlControls).toMatch(/---@field RefreshButton DImageButton/); + expect(dHtmlControls).toMatch(/---@field HomeURL string/); + expect(dHtmlControls).toMatch(/---@field HTML\? DHTML/); + expect(dHtmlControls).not.toMatch(/ReloadButton|HomeUrl|---@field HTML DHTML/); + expect(generatedDHtmlControls).toMatch(/---@field RefreshButton DImageButton/); + expect(generatedDHtmlControls).toMatch(/---@field HomeURL string/); + expect(generatedDHtmlControls).toMatch(/---@field HTML\? DHTML/); + expect(generatedDHtmlControls).not.toMatch(/ReloadButton|HomeUrl|---@field HTML DHTML/); + + expect(dNumPad).toMatch(/---@field m_bButtonSize number/); + expect(generatedDNumPad).toMatch(/---@field m_bButtonSize number/); + + expect(spawnMenu).toMatch(/---@field CustomizableSpawnlistNode\? DTree_Node/); + expect(spawnMenu).toMatch(/---@field SearchPropPanel\? ContentContainer/); + expect(spawnMenu).not.toMatch(/CustomizableSpawnlistNode\? any|SearchPropPanel\? Panel/); + expect(generatedCustomClasses).toMatch(/---@field CustomizableSpawnlistNode\? DTree_Node/); + expect(generatedCustomClasses).toMatch(/---@field SearchPropPanel\? ContentContainer/); + expect(generatedCustomClasses).not.toMatch(/CustomizableSpawnlistNode\? any|SearchPropPanel\? Panel/); + + expect(weaponClass).toMatch(/---@return Entity\|Player\|NPC\|NULL/); + expect(generatedWeapon).toMatch(/---@return Entity\|Player\|NPC\|NULL/); + expect(generatedWeapon).not.toMatch(/---@return Player # The player who owns this weapon\./); + + expect(getToolObject).toMatch(/---@class gmod_tool : Weapon/); + expect(getToolObject).toMatch(/---@return Tool\|false/); + expect(getToolObject).not.toMatch(/function Weapon:GetToolObject/); + expect(generatedWeapon).toMatch(/function gmod_tool:GetToolObject\(tool\) end/); + expect(generatedWeapon).not.toMatch(/function Weapon:GetToolObject\(tool\) end/); + expect(generatedWeapon).toMatch(/---@return Tool\|false/); + + expect(toolLeftClick).not.toMatch(/fromRight/); + expect(generatedTool).not.toMatch(/fromRight/); + expect(generatedTool).toMatch(/---@param skip\? boolean/); + expect(generatedTool).toMatch(/function Tool:Deploy\(skip\) end/); + expect(generatedTool).toMatch(/function Tool:Holster\(skip\) end/); + expect(generatedTool.match(/function Tool:Deploy/g)).toHaveLength(1); + expect(generatedTool.match(/function Tool:Holster/g)).toHaveLength(1); + + expect(weld).toMatch(/---@return Entity\|false/); + expect(generatedConstraint).toMatch(/---@return Entity\|false # The created constraint entity/); + expect(elastic).toMatch(/---@return Entity\|false\|nil/); + expect(elastic).toMatch(/---@return Entity\? # The created rope/); + expect(generatedConstraint).toMatch(/---@return Entity\|false\|nil # The created constraint/); + expect(generatedConstraint).toMatch(/---@return Entity\? # The created rope/); + + expect(generatedEntity).toMatch(/---@param delta\? number/); + expect(generatedEntity).toMatch(/function Entity:FrameAdvance\(delta\) end/); + expect(generatedPanel).toMatch(/---@param width\? number/); + expect(generatedPanel).toMatch(/---@param height\? number/); + expect(generatedPanel).toMatch(/function Panel:PerformLayout\(width, height\) end/); + expect(generatedPanel.match(/function Panel:PerformLayout/g)).toHaveLength(1); + expect(generatedDPanelList).toMatch(/---@param remove\? boolean/); + expect(generatedDPanelList).toMatch(/function DPanelList:Clear\(remove\) end/); + }); + }); diff --git a/custom/DPanelList.Clear.lua b/custom/DPanelList.Clear.lua new file mode 100644 index 00000000..0f34b5f1 --- /dev/null +++ b/custom/DPanelList.Clear.lua @@ -0,0 +1,5 @@ +---Hides all child panels, and optionally deletes them. +---@realm client +---@source https://wiki.facepunch.com/gmod/DPanelList:Clear +---@param remove? boolean Whether to actually delete the panels, not just hide them. +function DPanelList:Clear(remove) end diff --git a/custom/Entity.FrameAdvance.lua b/custom/Entity.FrameAdvance.lua new file mode 100644 index 00000000..0a6cdda7 --- /dev/null +++ b/custom/Entity.FrameAdvance.lua @@ -0,0 +1,5 @@ +---Advances the entity's animation frame. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:FrameAdvance +---@param delta? number The time delta to advance by. If omitted, the engine advances by its default frame interval. +function Entity:FrameAdvance(delta) end diff --git a/custom/PANEL.PerformLayout.lua b/custom/PANEL.PerformLayout.lua new file mode 100644 index 00000000..defeba47 --- /dev/null +++ b/custom/PANEL.PerformLayout.lua @@ -0,0 +1,8 @@ +---Called by VGUI when this panel should lay out its children. +---@hook PerformLayout +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/PANEL:PerformLayout +---@param width? number The panel's current width. +---@param height? number The panel's current height. +function Panel:PerformLayout(width, height) end diff --git a/custom/TOOL.Deploy.lua b/custom/TOOL.Deploy.lua new file mode 100644 index 00000000..bd2f436b --- /dev/null +++ b/custom/TOOL.Deploy.lua @@ -0,0 +1,9 @@ +---Called when [WEAPON:Deploy](https://wiki.facepunch.com/gmod/WEAPON:Deploy) of the toolgun is called. +--- +--- This is also called when switching from another tool on the server. +---@hook Deploy +---@realm shared +---@source https://wiki.facepunch.com/gmod/TOOL:Deploy +---@param skip? boolean True when the toolgun wrapper is switching tool modes internally. +---@return boolean? # Return true to allow switching away from the toolgun using lastinv command. +function Tool:Deploy(skip) end diff --git a/custom/TOOL.Holster.lua b/custom/TOOL.Holster.lua new file mode 100644 index 00000000..64fae31c --- /dev/null +++ b/custom/TOOL.Holster.lua @@ -0,0 +1,7 @@ +---Called when [WEAPON:Holster](https://wiki.facepunch.com/gmod/WEAPON:Holster) of the toolgun is called. +---@hook Holster +---@realm shared +---@source https://wiki.facepunch.com/gmod/TOOL:Holster +---@param skip? boolean True when the toolgun wrapper is switching tool modes internally. +---@return boolean? +function Tool:Holster(skip) end diff --git a/custom/TOOL.LeftClick.lua b/custom/TOOL.LeftClick.lua index b7654898..6d032e18 100644 --- a/custom/TOOL.LeftClick.lua +++ b/custom/TOOL.LeftClick.lua @@ -3,6 +3,5 @@ ---@realm shared ---@source https://wiki.facepunch.com/gmod/TOOL:LeftClick ---@param tr TraceResult A trace from user's eyes to wherever they aim at. See Structures/TraceResult ----@overload fun(self: Tool, tr: TraceResult, fromRight: boolean): boolean ---@return boolean # Return `true` to draw the tool gun beam and play fire animations, `false` otherwise. function Tool:LeftClick(tr) end diff --git a/custom/Weapon.GetToolObject.lua b/custom/Weapon.GetToolObject.lua index b00832a5..e3d6f126 100644 --- a/custom/Weapon.GetToolObject.lua +++ b/custom/Weapon.GetToolObject.lua @@ -1,6 +1,9 @@ +---@class gmod_tool : Weapon +local gmod_tool = {} + ---Returns the tool object associated with the current or specified tool mode. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Weapon:GetToolObject ---@param tool? string The tool mode to retrieve. Defaults to the currently active tool mode. ----@return Tool? # The Tool object for the given mode, or `nil`/`false` if the mode has no tool object. -function Weapon:GetToolObject(tool) end +---@return Tool|false # The Tool object for the given mode, or `false` if the mode has no tool object. +function gmod_tool:GetToolObject(tool) end diff --git a/custom/class.DFileBrowser.lua b/custom/class.DFileBrowser.lua index 30a0e205..3b83f6ef 100644 --- a/custom/class.DFileBrowser.lua +++ b/custom/class.DFileBrowser.lua @@ -3,8 +3,10 @@ ---@field Divider DHorizontalDivider --- The directory tree panel. ---@field Tree DTree ---- The file list panel showing files in the current folder. ----@field Files DListView +--- The root folder node created when the tree is set up. +---@field FolderNode? DTree_Node +--- The file list panel, created on demand as icons in model mode or rows otherwise. +---@field Files? DIconBrowser|DListView --- The current path search string. ---@field m_strSearch string --- The base folder path to browse from. @@ -18,7 +20,7 @@ --- The display name of this file browser. ---@field m_strName string --- Whether to show models instead of files. ----@field m_bModels boolean +---@field m_bModels? boolean --- Whether the browser is currently expanded/open. ----@field m_bOpen boolean +---@field m_bOpen? boolean local DFileBrowser = {} diff --git a/custom/class.DHTMLControls.lua b/custom/class.DHTMLControls.lua index e3672ed2..4e160e86 100644 --- a/custom/class.DHTMLControls.lua +++ b/custom/class.DHTMLControls.lua @@ -4,19 +4,19 @@ --- The forward navigation button. ---@field ForwardButton DImageButton --- The refresh/reload button. ----@field ReloadButton DImageButton +---@field RefreshButton DImageButton --- The home button. ---@field HomeButton DImageButton --- The stop button. ---@field StopButton DImageButton --- The address bar text entry. ---@field AddressBar DTextEntry ---- The DHTML panel these controls navigate. ----@field HTML DHTML +--- The DHTML panel these controls navigate, assigned by SetHTML. +---@field HTML? DHTML --- The current navigation history position. ---@field Cur number --- Whether we are currently navigating via history buttons. ---@field Navigating boolean --- The home URL to navigate to. ----@field HomeUrl string +---@field HomeURL string local DHTMLControls = {} diff --git a/custom/class.DNumPad.lua b/custom/class.DNumPad.lua index d80af334..f0a249a5 100644 --- a/custom/class.DNumPad.lua +++ b/custom/class.DNumPad.lua @@ -7,8 +7,8 @@ ---@field m_iSelectedNumber number --- Padding between buttons. ---@field m_iPadding number ---- Button size scale factor. ----@field m_bButtonSize boolean +--- Button size in pixels. +---@field m_bButtonSize number --- Whether keys stay selected when pressed (sticky keys mode). ---@field m_bStickyKeys boolean local DNumPad = {} diff --git a/custom/class.DScrollBar.lua b/custom/class.DScrollBar.lua deleted file mode 100644 index 5d02ad6d..00000000 --- a/custom/class.DScrollBar.lua +++ /dev/null @@ -1,35 +0,0 @@ ----@class DVScrollBar : Panel ---- The up scroll button. ----@field btnUp DButton ---- The down scroll button. ----@field btnDown DButton ---- The scroll bar grip/slider button. ----@field btnGrip DButton ---- Current scroll offset. ----@field Offset number ---- Current scroll position. ----@field Scroll number ---- Total size of the scrollable canvas. ----@field CanvasSize number ---- Size of the scrollbar grip. ----@field BarSize number -local DVScrollBar = {} - ----@class DHScrollBar : Panel ---- The left scroll button. ----@field btnLeft DButton ---- The right scroll button. ----@field btnRight DButton ---- The scroll bar grip/slider button. ----@field btnGrip DButton ---- Current scroll offset. ----@field Offset number ---- Current scroll position. ----@field Scroll number ---- Total size of the scrollable canvas. ----@field CanvasSize number ---- Size of the scrollbar grip. ----@field BarSize number ---- Whether the scroll buttons are hidden. ----@field m_HideButtons boolean -local DHScrollBar = {} diff --git a/custom/class.SpawnMenu.lua b/custom/class.SpawnMenu.lua index 2b3f9369..79231f1f 100644 --- a/custom/class.SpawnMenu.lua +++ b/custom/class.SpawnMenu.lua @@ -4,7 +4,7 @@ ---@field CreateMenu CreationMenu The left-side creation/content menu panel. ---@field ToolToggle DImageButton The button that toggles the tool menu visibility. ---@field m_bHangOpen boolean Whether the spawn menu stays open (hang-open mode). ----@field CustomizableSpawnlistNode? any Injected reference to the customizable spawnlist node (optional). ----@field SearchPropPanel? Panel Injected reference to the search prop panel (optional). +---@field CustomizableSpawnlistNode? DTree_Node Injected reference to the customizable spawnlist node (optional). +---@field SearchPropPanel? ContentContainer Injected reference to the search results content panel (optional). ---@field StartupTool? Panel The tool item panel to select and activate on first open (set by toolpanel.lua). local SpawnMenu = {} diff --git a/custom/class.Weapon.lua b/custom/class.Weapon.lua index 7715aa73..b4c16582 100644 --- a/custom/class.Weapon.lua +++ b/custom/class.Weapon.lua @@ -32,13 +32,11 @@ WEAPON = Weapon --- Secondary fire ammo configuration. ---@field Secondary WeaponAmmoTable ----Returns the owner of this weapon, narrowed to [Player](https://wiki.facepunch.com/gmod/Player). +---Returns the owner of this weapon. --- ---- Weapons are owned by players (or sometimes NPCs); for SWEP code `self:GetOwner()` ---- is the wielding player in the vast majority of cases. This narrows the base ---- [Entity:GetOwner](https://wiki.facepunch.com/gmod/Entity:GetOwner) return so ---- shared `Player` methods (e.g. `KeyDown`) resolve correctly in weapon code. +--- Weapons can be owned by players, NPCs, other entities, or NULL while dropped, +--- initializing, or being removed. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:GetOwner ----@return Player # The player who owns this weapon. +---@return Entity|Player|NPC|NULL # The entity currently owning this weapon. function Weapon:GetOwner() end diff --git a/custom/constraint.Elastic.lua b/custom/constraint.Elastic.lua index e986dce0..38a8fece 100644 --- a/custom/constraint.Elastic.lua +++ b/custom/constraint.Elastic.lua @@ -16,6 +16,6 @@ ---@param width number Width of rope. ---@param stretchOnly? boolean|number Apply physics forces only on stretch. ---@param color? Color The color of the rope. See Color. ----@return Entity # The created constraint. ([phys_spring](https://developer.valvesoftware.com/wiki/Phys_spring)) Will return `false` if the constraint could not be created. ----@return Entity # The created rope. ([keyframe_rope](https://developer.valvesoftware.com/wiki/Keyframe_rope)) Will return `nil` if the constraint could not be created. +---@return Entity|false|nil # The created constraint. ([phys_spring](https://developer.valvesoftware.com/wiki/Phys_spring)) Returns `false` for invalid inputs and `nil` when no spring is created. +---@return Entity? # The created rope. ([keyframe_rope](https://developer.valvesoftware.com/wiki/Keyframe_rope)) Returns `nil` if no rope was created. function constraint.Elastic(ent1, ent2, bone1, bone2, localPos1, localPos2, constant, damping, relDamping, material, width, stretchOnly, color) end diff --git a/custom/constraint.Weld.lua b/custom/constraint.Weld.lua index 8530a7b1..481ea8df 100644 --- a/custom/constraint.Weld.lua +++ b/custom/constraint.Weld.lua @@ -10,5 +10,5 @@ ---@param forceLimit? number The amount of force appliable to the constraint before it will break (0 is never). ---@param noCollide? boolean|number Should `ent1` be nocollided to `ent2` via this constraint. ---@param deleteEnt1OnBreak? boolean|number If true, when `ent2` is removed, `ent1` will also be removed. ----@return Entity # The created constraint entity, or false if the constraint failed. ([phys_constraint](https://developer.valvesoftware.com/wiki/Phys_constraint)) +---@return Entity|false # The created constraint entity, or false if the constraint failed. ([phys_constraint](https://developer.valvesoftware.com/wiki/Phys_constraint)) function constraint.Weld(ent1, ent2, bone1, bone2, forceLimit, noCollide, deleteEnt1OnBreak) end From 3097aece114d6357b82983c786ed200f8f8a96a8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 03:10:12 +0100 Subject: [PATCH 075/117] Add guard metadata overrides --- custom/Entity.IsValid.lua | 7 +++++++ custom/Global.IsValid.lua | 9 +++++++++ custom/Global.isfunction.lua | 1 + 3 files changed, 17 insertions(+) create mode 100644 custom/Entity.IsValid.lua create mode 100644 custom/Global.IsValid.lua diff --git a/custom/Entity.IsValid.lua b/custom/Entity.IsValid.lua new file mode 100644 index 00000000..b464d556 --- /dev/null +++ b/custom/Entity.IsValid.lua @@ -0,0 +1,7 @@ +---Returns whether the entity is a valid entity or not. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:IsValid +---@return boolean # Whether the entity is valid. +---@return_cast self Entity +---@[self_guard("gmod.entity")] +function Entity:IsValid() end diff --git a/custom/Global.IsValid.lua b/custom/Global.IsValid.lua new file mode 100644 index 00000000..77d1cc9a --- /dev/null +++ b/custom/Global.IsValid.lua @@ -0,0 +1,9 @@ +---Returns whether an object is valid or not. (Such as entities, Panels, custom table objects and more). +--- +--- Checks that an object is not nil, has an `IsValid` method and if this method returns `true`. If the object has no `IsValid` method, it will return `false`. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.IsValid +---@param ent any The table or object to be validated. +---@return TypeGuard isValid # True if the object is valid. +function _G.IsValid(ent) end diff --git a/custom/Global.isfunction.lua b/custom/Global.isfunction.lua index bb572699..474afc5b 100644 --- a/custom/Global.isfunction.lua +++ b/custom/Global.isfunction.lua @@ -2,6 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.isfunction +---@[call_arg("gmod.member_guard", "function")] ---@param var any ---@return TypeGuard isFunction # Whether the value is a function. function _G.isfunction(var) end From 35f1b8ff9e6fc2496d0a0e536e33f1954f399874 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 10:28:01 +0100 Subject: [PATCH 076/117] Annotate ToolObj SetObject --- custom/class.Tool.lua | 8 +++----- custom/class.ToolObj.lua | 10 ++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/custom/class.Tool.lua b/custom/class.Tool.lua index 88c96247..779e9ea3 100644 --- a/custom/class.Tool.lua +++ b/custom/class.Tool.lua @@ -15,12 +15,10 @@ ---@field Pos Vector The local-space hit position (world-space for world entity). ---@field Normal Vector The local-space hit normal (world-space for world entity). ---- The Objects array on a tool. Named class with a non-nil index operator so that ---- direct `self.Objects[i]` accesses inside tool methods (GetPos, GetEnt, SetObject, etc.) ---- do not generate spurious unchecked-nil-access diagnostics. +--- The Objects array on a tool. Direct `self.Objects[i]` accesses inside tool +--- methods (GetPos, GetEnt, SetObject, etc.) return the stored slot shape. --- Callers must guarantee the index is valid before calling any getter. ----@class ToolObjects ----@operator index(integer): ToolObjectSlot +---@alias ToolObjects table ---@class Tool ---@field Mode string The tool mode string (e.g. "weld", "balloon"). diff --git a/custom/class.ToolObj.lua b/custom/class.ToolObj.lua index c1d3fdb8..a5b13763 100644 --- a/custom/class.ToolObj.lua +++ b/custom/class.ToolObj.lua @@ -8,4 +8,14 @@ --- method used to spawn a new `TOOL` table. ---@class ToolObj : Tool ---@field Create fun(self: ToolObj): TOOL Factory method that returns a new `TOOL` instance. +---@field Objects ToolObjects Array of stored constraint objects indexed 1-based. ToolObj = ToolObj or {} + +---Stores a selected object in `Objects`. +---@param id number +---@param ent Entity +---@param pos Vector +---@param phys PhysObj|nil +---@param bone number +---@param normal Vector +function ToolObj:SetObject(id, ent, pos, phys, bone, normal) end From 94b98d89a38043eedc4b163eb00f80cf7e9f57b2 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 13:24:06 +0100 Subject: [PATCH 077/117] Add owner-valid weapon callbacks --- custom/WEAPON.AdjustMouseSensitivity.lua | 7 +++++++ custom/WEAPON.DoShootEffect.lua | 6 ++++++ custom/WEAPON.DoToolTrace.lua | 9 +++++++++ custom/WEAPON.PrimaryAttack.lua | 6 ++++++ custom/WEAPON.Reload.lua | 6 ++++++ custom/WEAPON.SecondaryAttack.lua | 6 ++++++ 6 files changed, 40 insertions(+) create mode 100644 custom/WEAPON.AdjustMouseSensitivity.lua create mode 100644 custom/WEAPON.DoShootEffect.lua create mode 100644 custom/WEAPON.DoToolTrace.lua create mode 100644 custom/WEAPON.PrimaryAttack.lua create mode 100644 custom/WEAPON.Reload.lua create mode 100644 custom/WEAPON.SecondaryAttack.lua diff --git a/custom/WEAPON.AdjustMouseSensitivity.lua b/custom/WEAPON.AdjustMouseSensitivity.lua new file mode 100644 index 00000000..a674a0d2 --- /dev/null +++ b/custom/WEAPON.AdjustMouseSensitivity.lua @@ -0,0 +1,7 @@ +---Called to adjust player mouse sensitivity while this weapon is active. +---@hook AdjustMouseSensitivity +---@realm client +---@source https://wiki.facepunch.com/gmod/WEAPON:AdjustMouseSensitivity +---@return number? sensitivityMultiplier # Return a multiplier to override sensitivity. +---@[self_call_valid("GetOwner")] +function Weapon:AdjustMouseSensitivity() end diff --git a/custom/WEAPON.DoShootEffect.lua b/custom/WEAPON.DoShootEffect.lua new file mode 100644 index 00000000..f6e51047 --- /dev/null +++ b/custom/WEAPON.DoShootEffect.lua @@ -0,0 +1,6 @@ +---Called to play weapon shooting effects. +---@hook DoShootEffect +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:DoShootEffect +---@[self_call_valid("GetOwner")] +function Weapon:DoShootEffect() end diff --git a/custom/WEAPON.DoToolTrace.lua b/custom/WEAPON.DoToolTrace.lua new file mode 100644 index 00000000..302982b6 --- /dev/null +++ b/custom/WEAPON.DoToolTrace.lua @@ -0,0 +1,9 @@ +---Called by the toolgun SWEP to build a tool trace. +--- +--- This is specific to the Sandbox toolgun implementation, but is declared on +--- `Weapon` so `SWEP:DoToolTrace` overrides inherit the owner-valid callback +--- metadata without changing the global `Weapon:GetOwner` return type. +---@hook DoToolTrace +---@realm shared +---@[self_call_valid("GetOwner")] +function Weapon:DoToolTrace() end diff --git a/custom/WEAPON.PrimaryAttack.lua b/custom/WEAPON.PrimaryAttack.lua new file mode 100644 index 00000000..ef59b144 --- /dev/null +++ b/custom/WEAPON.PrimaryAttack.lua @@ -0,0 +1,6 @@ +---Called when the weapon is fired with primary attack. +---@hook PrimaryAttack +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:PrimaryAttack +---@[self_call_valid("GetOwner")] +function Weapon:PrimaryAttack() end diff --git a/custom/WEAPON.Reload.lua b/custom/WEAPON.Reload.lua new file mode 100644 index 00000000..5a1f7d5a --- /dev/null +++ b/custom/WEAPON.Reload.lua @@ -0,0 +1,6 @@ +---Called when the player reloads the weapon. +---@hook Reload +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:Reload +---@[self_call_valid("GetOwner")] +function Weapon:Reload() end diff --git a/custom/WEAPON.SecondaryAttack.lua b/custom/WEAPON.SecondaryAttack.lua new file mode 100644 index 00000000..27de9d4a --- /dev/null +++ b/custom/WEAPON.SecondaryAttack.lua @@ -0,0 +1,6 @@ +---Called when the weapon is fired with secondary attack. +---@hook SecondaryAttack +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:SecondaryAttack +---@[self_call_valid("GetOwner")] +function Weapon:SecondaryAttack() end From b74e505745c6c57968dd97264cd7a0ebf0a78a89 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:08:38 +0100 Subject: [PATCH 078/117] Add sandbox derma and entity annotations --- custom/DTree.Root.lua | 5 +++++ custom/class.ContextBase.lua | 3 +++ custom/class.ControlPanel.lua | 7 +++++++ custom/class.ControlPresets.lua | 8 ++++++++ custom/class.DImage.lua | 4 ++++ custom/class.DPanelSelect.lua | 6 ++++++ custom/class.DSlider.lua | 3 +++ custom/class.DTree_Node.lua | 7 +++++++ custom/class.GM.lua | 14 ++++++++++++++ custom/class.MatSelect.lua | 3 +++ custom/class.Panel.lua | 6 ++++++ custom/class.PostProcessIcon.lua | 10 ++++++++++ custom/class.PropSelect.lua | 2 ++ custom/class.SpawnIcon.lua | 7 +++++++ custom/class.SpawnMenu.lua | 15 +++++++++++++++ custom/class.SpawnmenuContentPanel.lua | 6 ++++++ custom/class.gmod_cameraprop.lua | 8 ++++++++ custom/class.gmod_wheel.lua | 17 +++++++++++++++++ custom/ents.Create.lua | 2 ++ 19 files changed, 133 insertions(+) create mode 100644 custom/DTree.Root.lua create mode 100644 custom/class.ContextBase.lua create mode 100644 custom/class.ControlPresets.lua create mode 100644 custom/class.DImage.lua create mode 100644 custom/class.DSlider.lua create mode 100644 custom/class.MatSelect.lua create mode 100644 custom/class.PostProcessIcon.lua create mode 100644 custom/class.PropSelect.lua create mode 100644 custom/class.SpawnIcon.lua create mode 100644 custom/class.SpawnmenuContentPanel.lua create mode 100644 custom/class.gmod_cameraprop.lua create mode 100644 custom/class.gmod_wheel.lua diff --git a/custom/DTree.Root.lua b/custom/DTree.Root.lua new file mode 100644 index 00000000..efe8d566 --- /dev/null +++ b/custom/DTree.Root.lua @@ -0,0 +1,5 @@ +---Returns the root node for this tree. +---@realm client +---@realm menu +---@return DTree_Node # The root tree node. +function DTree:Root() end diff --git a/custom/class.ContextBase.lua b/custom/class.ContextBase.lua new file mode 100644 index 00000000..c1534baa --- /dev/null +++ b/custom/class.ContextBase.lua @@ -0,0 +1,3 @@ +---@class (partial) ContextBase : Panel +---@field Label DLabel The label panel created by the shared Sandbox context control base. +local ContextBase = {} diff --git a/custom/class.ControlPanel.lua b/custom/class.ControlPanel.lua index 90e9b8ab..59c6d92a 100644 --- a/custom/class.ControlPanel.lua +++ b/custom/class.ControlPanel.lua @@ -7,3 +7,10 @@ local ControlPanel = {} ---@param text string The text to display. ---@return DLabel # The created DLabel. function ControlPanel:Label(text) end + +---Creates the tool preset selector panel for this control panel. +---@realm client +---@param group string The presets group. Must be unique. +---@param cvarList table The convar defaults used by the preset control. +---@return ControlPresets # The created ControlPresets panel. +function ControlPanel:ToolPresets(group, cvarList) end diff --git a/custom/class.ControlPresets.lua b/custom/class.ControlPresets.lua new file mode 100644 index 00000000..6a067f9f --- /dev/null +++ b/custom/class.ControlPresets.lua @@ -0,0 +1,8 @@ +---@class (partial) ControlPresets : Panel +---@field Label DLabel The visible preset group label, assigned by the control panel builder. +---@field DropDown DComboBox The preset selection dropdown. +---@field Button DImageButton The edit-preset button. +---@field AddButton DImageButton The quick-save button. +---@field Options table Available preset option data. +---@field ConVars table Console variables managed by this preset control. +local ControlPresets = {} diff --git a/custom/class.DImage.lua b/custom/class.DImage.lua new file mode 100644 index 00000000..f035e02a --- /dev/null +++ b/custom/class.DImage.lua @@ -0,0 +1,4 @@ +---@class (partial) DImage : DPanel +---@field m_Material IMaterial The material currently drawn by the image panel. +---@field m_Color Color The image color override. +local DImage = {} diff --git a/custom/class.DPanelSelect.lua b/custom/class.DPanelSelect.lua index b8fc6042..6d043a64 100644 --- a/custom/class.DPanelSelect.lua +++ b/custom/class.DPanelSelect.lua @@ -2,3 +2,9 @@ ---@field SelectedPanel? Panel ---@field OldSelectedPaintOver? function local DPanelSelect = {} + +---Adds a selectable panel to the panel select list. +---@realm client +---@param panel Panel The panel to add. +---@param convars? table ConVar values associated with the panel. +function DPanelSelect:AddPanel(panel, convars) end diff --git a/custom/class.DSlider.lua b/custom/class.DSlider.lua new file mode 100644 index 00000000..f0788472 --- /dev/null +++ b/custom/class.DSlider.lua @@ -0,0 +1,3 @@ +---@class (partial) DSlider : Panel +---@field Knob DButton The draggable knob button created in Init. +local DSlider = {} diff --git a/custom/class.DTree_Node.lua b/custom/class.DTree_Node.lua index 4ab8dc9b..b6e44c3f 100644 --- a/custom/class.DTree_Node.lua +++ b/custom/class.DTree_Node.lua @@ -12,3 +12,10 @@ ---@field CustomSpawnlist? boolean Whether this is a custom user spawnlist node. ---@field AddonSpawnlist? boolean Whether this is an addon-provided spawnlist node. local DTree_Node = {} + +---Returns the child node at the given index. +---@realm client +---@realm menu +---@param num number The zero-based child node index. +---@return DTree_Node # The child tree node. +function DTree_Node:GetChildNode(num) end diff --git a/custom/class.GM.lua b/custom/class.GM.lua index bb9025c7..8619f662 100644 --- a/custom/class.GM.lua +++ b/custom/class.GM.lua @@ -9,3 +9,17 @@ ---@field TeamBased boolean Whether the gamemode uses teams. ---@field IsSandboxDerived? boolean True for Sandbox and Sandbox-derived gamemodes. GM = {} + +---Adds a tool menu option to the sandbox spawn menu. Sandbox calls this as a +---gamemode method from `GM:AddSTOOL` even though the helper is not defined in +---the shipped Lua files as a standalone `GM` method. +---@realm client +---@param tab string The spawn menu tab name. +---@param category string The tool category. +---@param class string The tool class/name. +---@param name string The display name. +---@param cmd string The console command. +---@param config string|nil The config name. +---@param cpanel fun(panel: ControlPanel)|nil Callback used to populate the control panel. +---@param data table|nil Additional tool menu option data. +function GM:AddToolMenuOption(tab, category, class, name, cmd, config, cpanel, data) end diff --git a/custom/class.MatSelect.lua b/custom/class.MatSelect.lua new file mode 100644 index 00000000..222457f3 --- /dev/null +++ b/custom/class.MatSelect.lua @@ -0,0 +1,3 @@ +---@class (partial) MatSelect : ContextBase +---@field List DPanelList The panel list containing the material buttons. +local MatSelect = {} diff --git a/custom/class.Panel.lua b/custom/class.Panel.lua index 03a2b9c7..2b8028dc 100644 --- a/custom/class.Panel.lua +++ b/custom/class.Panel.lua @@ -10,5 +10,11 @@ Panel = Panel or {} ---@param value any The value to set. The type depends on the panel implementation. function Panel:SetValue(value) end +---Compatibility alias used by shipped Sandbox code for Panel:SetTooltip. +---@realm client +---@realm menu +---@param text string The tooltip text. +function Panel:SetToolTip(text) end + ---@class PANEL : Panel PANEL = Panel diff --git a/custom/class.PostProcessIcon.lua b/custom/class.PostProcessIcon.lua new file mode 100644 index 00000000..bfed2262 --- /dev/null +++ b/custom/class.PostProcessIcon.lua @@ -0,0 +1,10 @@ +---@class PostProcessConVarState +---@field on string Value written when the post-process effect is enabled. +---@field off? string Value written when the post-process effect is disabled. + +---@class (partial) PostProcessIcon : ContentIcon +---@field ConVars table Console variables controlled by this post-process icon. +---@field PP table Runtime post-process metadata from `list.GetEntry("PostProcess", name)`. +---@field checkbox DCheckBox The optional enable/disable checkbox. +---@field cp ControlPanel? Lazily-created control panel for this post-process entry. +local PostProcessIcon = {} diff --git a/custom/class.PropSelect.lua b/custom/class.PropSelect.lua new file mode 100644 index 00000000..8e341436 --- /dev/null +++ b/custom/class.PropSelect.lua @@ -0,0 +1,2 @@ +---@class (partial) PropSelect : ContextBase +local PropSelect = {} diff --git a/custom/class.SpawnIcon.lua b/custom/class.SpawnIcon.lua new file mode 100644 index 00000000..d7eccacf --- /dev/null +++ b/custom/class.SpawnIcon.lua @@ -0,0 +1,7 @@ +---@class (partial) SpawnIcon : DButton +local SpawnIcon = {} + +---Returns the icon name/path stored by the spawn icon. +---@realm client +---@return string # The icon name. +function SpawnIcon:GetIconName() end diff --git a/custom/class.SpawnMenu.lua b/custom/class.SpawnMenu.lua index 79231f1f..58697387 100644 --- a/custom/class.SpawnMenu.lua +++ b/custom/class.SpawnMenu.lua @@ -8,3 +8,18 @@ ---@field SearchPropPanel? ContentContainer Injected reference to the search results content panel (optional). ---@field StartupTool? Panel The tool item panel to select and activate on first open (set by toolpanel.lua). local SpawnMenu = {} + +---@class ToolMenu : Panel +local ToolMenu = {} + +---Adds an option to the tool menu panel. +---@realm client +---@param tab string The tool tab name. +---@param category string The tool category. +---@param class string The tool class/name. +---@param name string The display name. +---@param cmd string The console command. +---@param config string|nil The config name. +---@param cpanel fun(panel: ControlPanel)|nil Callback used to populate the control panel. +---@param data table|nil Additional tool menu option data. +function ToolMenu:AddToolMenuOption(tab, category, class, name, cmd, config, cpanel, data) end diff --git a/custom/class.SpawnmenuContentPanel.lua b/custom/class.SpawnmenuContentPanel.lua new file mode 100644 index 00000000..339f1bca --- /dev/null +++ b/custom/class.SpawnmenuContentPanel.lua @@ -0,0 +1,6 @@ +---@class (partial) SpawnmenuContentPanel : DPanel +---@field SelectedPanel? Panel The currently selected content panel. +---@field HorizontalDivider DHorizontalDivider The panel splitter used to host the selected content panel. +---@field ContentNavBar ContentSidebar The navigation sidebar for spawn menu content. +---@field OldSpawnlists table? Previous spawnlists passed to content population hooks. +local SpawnmenuContentPanel = {} diff --git a/custom/class.gmod_cameraprop.lua b/custom/class.gmod_cameraprop.lua new file mode 100644 index 00000000..3ab695b1 --- /dev/null +++ b/custom/class.gmod_cameraprop.lua @@ -0,0 +1,8 @@ +---@class gmod_cameraprop : Entity +local gmod_cameraprop = {} + +---Sets the entity and local position tracked by the camera prop. +---@realm server +---@param ent Entity|NULL The entity to track, or NULL for no target. +---@param localPos Vector The local tracking position. +function gmod_cameraprop:SetTracking(ent, localPos) end diff --git a/custom/class.gmod_wheel.lua b/custom/class.gmod_wheel.lua new file mode 100644 index 00000000..b6b3bf1e --- /dev/null +++ b/custom/class.gmod_wheel.lua @@ -0,0 +1,17 @@ +---@class gmod_wheel : Entity +local gmod_wheel = {} + +---@realm server +---@param motor table The wheel constraint motor data. +function gmod_wheel:SetMotor(motor) end + +---@realm server +---@param direction number The wheel direction. +function gmod_wheel:SetDirection(direction) end + +---@realm server +---@param axis Vector The wheel axis. +function gmod_wheel:SetAxis(axis) end + +---@realm server +function gmod_wheel:DoDirectionEffect() end diff --git a/custom/ents.Create.lua b/custom/ents.Create.lua index 84f01284..5c1345aa 100644 --- a/custom/ents.Create.lua +++ b/custom/ents.Create.lua @@ -6,6 +6,8 @@ ---@alias KnownEngineEntityClass ---| "gmod_anchor" ---| "gmod_hands" +---| "gmod_cameraprop" +---| "gmod_wheel" ---| "gmod_winch_controller" ---| "hunter_flechette" ---| "keyframe_rope" From 6ec3dc9e712bc2369888ed489aa39e449aa033cf Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:53:09 +0100 Subject: [PATCH 079/117] Mark IsValid as a validity guard --- custom/Global.IsValid.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/custom/Global.IsValid.lua b/custom/Global.IsValid.lua index 77d1cc9a..f099b1cd 100644 --- a/custom/Global.IsValid.lua +++ b/custom/Global.IsValid.lua @@ -6,4 +6,5 @@ ---@source https://wiki.facepunch.com/gmod/Global.IsValid ---@param ent any The table or object to be validated. ---@return TypeGuard isValid # True if the object is valid. +---@[valid_guard] function _G.IsValid(ent) end From 7e7639664d0d2b7389ed109e880441c94c351458 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:59:13 +0100 Subject: [PATCH 080/117] Add DTVar overload and skin annotations --- custom/Entity.DTVar.lua | 9 +++ custom/class.SKIN.lua | 132 +++++++++++++++++++++++++++++++--------- 2 files changed, 111 insertions(+), 30 deletions(-) create mode 100644 custom/Entity.DTVar.lua diff --git a/custom/Entity.DTVar.lua b/custom/Entity.DTVar.lua new file mode 100644 index 00000000..8f5bd2aa --- /dev/null +++ b/custom/Entity.DTVar.lua @@ -0,0 +1,9 @@ +---Adds a datatable variable accessor on an entity. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:DTVar +---@overload fun(type: string, name: string) +---@overload fun(type: string, slot: nil, name: string) +---@param type string The type of the DTVar being set up. +---@param slot number The DTVar slot. Can be omitted to use the next available slot. +---@param name string Name by which you will refer to the DTVar. +function Entity:DTVar(type, slot, name) end diff --git a/custom/class.SKIN.lua b/custom/class.SKIN.lua index 178631e2..63194ef0 100644 --- a/custom/class.SKIN.lua +++ b/custom/class.SKIN.lua @@ -70,38 +70,110 @@ ---@field TooltipText Color ---@class SKINTexScroller ----@field TrackV fun(x: number, y: number, w: number, h: number) Vertical scrollbar track texture. ----@field ButtonV_Normal fun(x: number, y: number, w: number, h: number) Vertical scroll grip, normal state. ----@field ButtonV_Hover fun(x: number, y: number, w: number, h: number) Vertical scroll grip, hovered. ----@field ButtonV_Down fun(x: number, y: number, w: number, h: number) Vertical scroll grip, pressed. ----@field ButtonV_Disabled fun(x: number, y: number, w: number, h: number) Vertical scroll grip, disabled. ----@field TrackH fun(x: number, y: number, w: number, h: number) Horizontal scrollbar track texture. ----@field ButtonH_Normal fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, normal state. ----@field ButtonH_Hover fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, hovered. ----@field ButtonH_Down fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, pressed. ----@field ButtonH_Disabled fun(x: number, y: number, w: number, h: number) Horizontal scroll grip, disabled. ----@field LeftButton_Normal fun(x: number, y: number, w: number, h: number) Left scroll arrow, normal. ----@field LeftButton_Hover fun(x: number, y: number, w: number, h: number) Left scroll arrow, hovered. ----@field LeftButton_Down fun(x: number, y: number, w: number, h: number) Left scroll arrow, pressed. ----@field LeftButton_Disabled fun(x: number, y: number, w: number, h: number) Left scroll arrow, disabled. ----@field LeftButton_Dead fun(x: number, y: number, w: number, h: number) Left scroll arrow, dead/inactive (alias used by PaintButtonLeft). ----@field UpButton_Normal fun(x: number, y: number, w: number, h: number) Up scroll arrow, normal. ----@field UpButton_Hover fun(x: number, y: number, w: number, h: number) Up scroll arrow, hovered. ----@field UpButton_Down fun(x: number, y: number, w: number, h: number) Up scroll arrow, pressed. ----@field UpButton_Disabled fun(x: number, y: number, w: number, h: number) Up scroll arrow, disabled. ----@field UpButton_Dead fun(x: number, y: number, w: number, h: number) Up scroll arrow, dead/inactive (alias used by PaintButtonUp). ----@field RightButton_Normal fun(x: number, y: number, w: number, h: number) Right scroll arrow, normal. ----@field RightButton_Hover fun(x: number, y: number, w: number, h: number) Right scroll arrow, hovered. ----@field RightButton_Down fun(x: number, y: number, w: number, h: number) Right scroll arrow, pressed. ----@field RightButton_Disabled fun(x: number, y: number, w: number, h: number) Right scroll arrow, disabled. ----@field RightButton_Dead fun(x: number, y: number, w: number, h: number) Right scroll arrow, dead/inactive (alias used by PaintButtonRight). ----@field DownButton_Normal fun(x: number, y: number, w: number, h: number) Down scroll arrow, normal. ----@field DownButton_Hover fun(x: number, y: number, w: number, h: number) Down scroll arrow, hovered. ----@field DownButton_Down fun(x: number, y: number, w: number, h: number) Down scroll arrow, pressed. ----@field DownButton_Disabled fun(x: number, y: number, w: number, h: number) Down scroll arrow, disabled. ----@field DownButton_Dead fun(x: number, y: number, w: number, h: number) Down scroll arrow, dead/inactive (alias used by PaintButtonDown). +---@field TrackV fun(x: number, y: number, w: number, h: number, col?: Color) Vertical scrollbar track texture. +---@field ButtonV_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Vertical scroll grip, normal state. +---@field ButtonV_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Vertical scroll grip, hovered. +---@field ButtonV_Down fun(x: number, y: number, w: number, h: number, col?: Color) Vertical scroll grip, pressed. +---@field ButtonV_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Vertical scroll grip, disabled. +---@field TrackH fun(x: number, y: number, w: number, h: number, col?: Color) Horizontal scrollbar track texture. +---@field ButtonH_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Horizontal scroll grip, normal state. +---@field ButtonH_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Horizontal scroll grip, hovered. +---@field ButtonH_Down fun(x: number, y: number, w: number, h: number, col?: Color) Horizontal scroll grip, pressed. +---@field ButtonH_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Horizontal scroll grip, disabled. +---@field LeftButton_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Left scroll arrow, normal. +---@field LeftButton_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Left scroll arrow, hovered. +---@field LeftButton_Down fun(x: number, y: number, w: number, h: number, col?: Color) Left scroll arrow, pressed. +---@field LeftButton_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Left scroll arrow, disabled. +---@field LeftButton_Dead fun(x: number, y: number, w: number, h: number, col?: Color) Left scroll arrow, dead/inactive (alias used by PaintButtonLeft). +---@field UpButton_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Up scroll arrow, normal. +---@field UpButton_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Up scroll arrow, hovered. +---@field UpButton_Down fun(x: number, y: number, w: number, h: number, col?: Color) Up scroll arrow, pressed. +---@field UpButton_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Up scroll arrow, disabled. +---@field UpButton_Dead fun(x: number, y: number, w: number, h: number, col?: Color) Up scroll arrow, dead/inactive (alias used by PaintButtonUp). +---@field RightButton_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Right scroll arrow, normal. +---@field RightButton_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Right scroll arrow, hovered. +---@field RightButton_Down fun(x: number, y: number, w: number, h: number, col?: Color) Right scroll arrow, pressed. +---@field RightButton_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Right scroll arrow, disabled. +---@field RightButton_Dead fun(x: number, y: number, w: number, h: number, col?: Color) Right scroll arrow, dead/inactive (alias used by PaintButtonRight). +---@field DownButton_Normal fun(x: number, y: number, w: number, h: number, col?: Color) Down scroll arrow, normal. +---@field DownButton_Hover fun(x: number, y: number, w: number, h: number, col?: Color) Down scroll arrow, hovered. +---@field DownButton_Down fun(x: number, y: number, w: number, h: number, col?: Color) Down scroll arrow, pressed. +---@field DownButton_Disabled fun(x: number, y: number, w: number, h: number, col?: Color) Down scroll arrow, disabled. +---@field DownButton_Dead fun(x: number, y: number, w: number, h: number, col?: Color) Down scroll arrow, dead/inactive (alias used by PaintButtonDown). + +---@class SKINTexPanels +---@field Normal fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Bright fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Dark fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Highlight fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexWindow +---@field Normal fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Inactive fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Close fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Close_Hover fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Close_Down fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Mini fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Mini_Hover fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Mini_Down fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Maxi fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Maxi_Hover fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Maxi_Down fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Restore fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Restore_Hover fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Restore_Down fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexMenu +---@field RightArrow fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexState +---@field Normal fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Hover fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Down fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Disabled fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexComboBox : SKINTexState +---@field Button SKINTexState + +---@class SKINTexUpDown +---@field Up SKINTexState +---@field Down SKINTexState + +---@class SKINTexSlider +---@field H SKINTexState +---@field V SKINTexState + +---@class SKINTexListBox +---@field Background fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Hovered fun(x: number, y: number, w: number, h: number, col?: Color) +---@field EvenLine fun(x: number, y: number, w: number, h: number, col?: Color) +---@field OddLine fun(x: number, y: number, w: number, h: number, col?: Color) +---@field EvenLineSelected fun(x: number, y: number, w: number, h: number, col?: Color) +---@field OddLineSelected fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexInput +---@field ListBox SKINTexListBox +---@field ComboBox SKINTexComboBox +---@field UpDown SKINTexUpDown +---@field Slider SKINTexSlider + +---@class SKINTexProgressBar +---@field Back fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Front fun(x: number, y: number, w: number, h: number, col?: Color) + +---@class SKINTexCategoryList +---@field Outer fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Inner fun(x: number, y: number, w: number, h: number, col?: Color) +---@field Header fun(x: number, y: number, w: number, h: number, col?: Color) +---@field InnerH fun(x: number, y: number, w: number, h: number, col?: Color) ---@class SKINTex +---@field Panels SKINTexPanels +---@field Window SKINTexWindow +---@field Menu SKINTexMenu +---@field Input SKINTexInput +---@field ProgressBar SKINTexProgressBar +---@field CategoryList SKINTexCategoryList ---@field Scroller SKINTexScroller --- Active Derma skin table used by derma and GWEN. From 7245e27d613ad678e9b80ffbd7e26c8f0ee8f2da Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 25 Jun 2026 20:40:55 +0100 Subject: [PATCH 081/117] Add prop_dynamic class annotation --- custom/class.prop_dynamic.lua | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 custom/class.prop_dynamic.lua diff --git a/custom/class.prop_dynamic.lua b/custom/class.prop_dynamic.lua new file mode 100644 index 00000000..064614de --- /dev/null +++ b/custom/class.prop_dynamic.lua @@ -0,0 +1,2 @@ +---@class prop_dynamic : Entity +local prop_dynamic = {} From e200cdc53ef71c570458cbf49a6c41b29ac434b8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 26 Jun 2026 00:39:28 +0100 Subject: [PATCH 082/117] Add DTree annotations --- custom/DTree.DoClick.lua | 6 ++++++ custom/DTree_Node.CreateChildNodes.lua | 10 ++++++++++ custom/DTree_Node.DoClick.lua | 5 +++++ custom/DTree_Node.DoRightClick.lua | 5 +++++ custom/DTree_Node.OnModified.lua | 4 ++++ custom/DTree_Node.OnNodeAdded.lua | 5 +++++ custom/DTree_Node.OnNodeSelected.lua | 5 +++++ custom/class.DTree_Node.lua | 2 +- 8 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 custom/DTree.DoClick.lua create mode 100644 custom/DTree_Node.CreateChildNodes.lua create mode 100644 custom/DTree_Node.DoClick.lua create mode 100644 custom/DTree_Node.DoRightClick.lua create mode 100644 custom/DTree_Node.OnModified.lua create mode 100644 custom/DTree_Node.OnNodeAdded.lua create mode 100644 custom/DTree_Node.OnNodeSelected.lua diff --git a/custom/DTree.DoClick.lua b/custom/DTree.DoClick.lua new file mode 100644 index 00000000..eabdb8ed --- /dev/null +++ b/custom/DTree.DoClick.lua @@ -0,0 +1,6 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree.lua +---@param node DTree_Node The node that was clicked. +---@return boolean # Return true to handle the click. +function DTree:DoClick(node) end diff --git a/custom/DTree_Node.CreateChildNodes.lua b/custom/DTree_Node.CreateChildNodes.lua new file mode 100644 index 00000000..47f9ebcc --- /dev/null +++ b/custom/DTree_Node.CreateChildNodes.lua @@ -0,0 +1,10 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Creates the container [DListLayout](https://wiki.facepunch.com/gmod/DListLayout) for the [DTree_Node](https://wiki.facepunch.com/gmod/DTree_Node)s. +--- +--- This is called automatically so you don't have to. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:CreateChildNodes +---@outparam self.ChildNodes DListLayout +function DTree_Node:CreateChildNodes() end diff --git a/custom/DTree_Node.DoClick.lua b/custom/DTree_Node.DoClick.lua new file mode 100644 index 00000000..25cdcb92 --- /dev/null +++ b/custom/DTree_Node.DoClick.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree_node.lua +---@return boolean # Return true to handle the click. +function DTree_Node:DoClick() end diff --git a/custom/DTree_Node.DoRightClick.lua b/custom/DTree_Node.DoRightClick.lua new file mode 100644 index 00000000..b60a0c59 --- /dev/null +++ b/custom/DTree_Node.DoRightClick.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree_node.lua +---@return boolean # Return true to handle the right-click. +function DTree_Node:DoRightClick() end diff --git a/custom/DTree_Node.OnModified.lua b/custom/DTree_Node.OnModified.lua new file mode 100644 index 00000000..00435c02 --- /dev/null +++ b/custom/DTree_Node.OnModified.lua @@ -0,0 +1,4 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree_node.lua +function DTree_Node:OnModified() end diff --git a/custom/DTree_Node.OnNodeAdded.lua b/custom/DTree_Node.OnNodeAdded.lua new file mode 100644 index 00000000..19035384 --- /dev/null +++ b/custom/DTree_Node.OnNodeAdded.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree_node.lua +---@param node Panel The panel added to this node. +function DTree_Node:OnNodeAdded(node) end diff --git a/custom/DTree_Node.OnNodeSelected.lua b/custom/DTree_Node.OnNodeSelected.lua new file mode 100644 index 00000000..f3cba300 --- /dev/null +++ b/custom/DTree_Node.OnNodeSelected.lua @@ -0,0 +1,5 @@ +---@realm client +---@realm menu +---@source garrysmod/lua/vgui/dtree_node.lua +---@param node Panel The selected panel. +function DTree_Node:OnNodeSelected(node) end diff --git a/custom/class.DTree_Node.lua b/custom/class.DTree_Node.lua index b6e44c3f..21cc06cd 100644 --- a/custom/class.DTree_Node.lua +++ b/custom/class.DTree_Node.lua @@ -17,5 +17,5 @@ local DTree_Node = {} ---@realm client ---@realm menu ---@param num number The zero-based child node index. ----@return DTree_Node # The child tree node. +---@return Panel? # The child panel, if any. function DTree_Node:GetChildNode(num) end From 7c23a9fec92741757840a2cab33d9aea943cd8f0 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 26 Jun 2026 08:00:14 +0100 Subject: [PATCH 083/117] Add base and sandbox annotation overrides --- custom/DModelPanel.SetAmbientLight.lua | 4 ++++ custom/Entity.EditValue.lua | 5 +++++ custom/Entity.SetRagdollBuildFunction.lua | 4 ++++ custom/Entity.SetSequence.lua | 5 +++++ custom/GM.CheckPassword.lua | 11 +++++++++ custom/NextBot.FindSpots.lua | 9 ++++++++ custom/Schedule.GetTask.lua | 5 +++++ custom/WEAPON.AdjustMouseSensitivity.lua | 5 ++++- custom/class.ContentContainer.lua | 5 ++++- custom/constraint.Motor.lua | 27 +++++++++++++++++++++++ custom/duplicator.Paste.lua | 8 +++++++ custom/engine.OpenDupe.lua | 8 +++++++ custom/motionsensor.BuildSkeleton.lua | 9 ++++++++ custom/scripted_ents.GetStored.lua | 8 +++++++ 14 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 custom/DModelPanel.SetAmbientLight.lua create mode 100644 custom/Entity.EditValue.lua create mode 100644 custom/Entity.SetRagdollBuildFunction.lua create mode 100644 custom/Entity.SetSequence.lua create mode 100644 custom/GM.CheckPassword.lua create mode 100644 custom/NextBot.FindSpots.lua create mode 100644 custom/Schedule.GetTask.lua create mode 100644 custom/constraint.Motor.lua create mode 100644 custom/duplicator.Paste.lua create mode 100644 custom/engine.OpenDupe.lua create mode 100644 custom/motionsensor.BuildSkeleton.lua create mode 100644 custom/scripted_ents.GetStored.lua diff --git a/custom/DModelPanel.SetAmbientLight.lua b/custom/DModelPanel.SetAmbientLight.lua new file mode 100644 index 00000000..2c571b8b --- /dev/null +++ b/custom/DModelPanel.SetAmbientLight.lua @@ -0,0 +1,4 @@ +---@realm client +---@source https://wiki.facepunch.com/gmod/DModelPanel:SetAmbientLight +---@param color Color|Vector +function DModelPanel:SetAmbientLight(color) end diff --git a/custom/Entity.EditValue.lua b/custom/Entity.EditValue.lua new file mode 100644 index 00000000..44b85df9 --- /dev/null +++ b/custom/Entity.EditValue.lua @@ -0,0 +1,5 @@ +---@realm shared +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/extensions/entity.lua +---@param variable string +---@param value string +function Entity:EditValue(variable, value) end diff --git a/custom/Entity.SetRagdollBuildFunction.lua b/custom/Entity.SetRagdollBuildFunction.lua new file mode 100644 index 00000000..9a18626e --- /dev/null +++ b/custom/Entity.SetRagdollBuildFunction.lua @@ -0,0 +1,4 @@ +---@realm server +---@source https://wiki.facepunch.com/gmod/Entity:SetRagdollBuildFunction +---@param builder fun(ragdoll: Entity)|nil +function Entity:SetRagdollBuildFunction(builder) end diff --git a/custom/Entity.SetSequence.lua b/custom/Entity.SetSequence.lua new file mode 100644 index 00000000..dde3b968 --- /dev/null +++ b/custom/Entity.SetSequence.lua @@ -0,0 +1,5 @@ +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:SetSequence +---@param sequence number|string +---@return number duration +function Entity:SetSequence(sequence) end diff --git a/custom/GM.CheckPassword.lua b/custom/GM.CheckPassword.lua new file mode 100644 index 00000000..18287657 --- /dev/null +++ b/custom/GM.CheckPassword.lua @@ -0,0 +1,11 @@ +---@hook CheckPassword +---@realm server +---@source https://wiki.facepunch.com/gmod/GM:CheckPassword +---@param steamID64 string +---@param ipAddress string +---@param svPassword string +---@param clPassword string +---@param name string +---@return boolean allow +---@return string? reason +function GM:CheckPassword(steamID64, ipAddress, svPassword, clPassword, name) end diff --git a/custom/NextBot.FindSpots.lua b/custom/NextBot.FindSpots.lua new file mode 100644 index 00000000..ca044ff8 --- /dev/null +++ b/custom/NextBot.FindSpots.lua @@ -0,0 +1,9 @@ +---@class NextBotSpot +---@field vector Vector +---@field distance number + +---@realm server +---@source https://wiki.facepunch.com/gmod/NextBot:FindSpots +---@param specs table +---@return NextBotSpot[] spots +function NextBot:FindSpots(specs) end diff --git a/custom/Schedule.GetTask.lua b/custom/Schedule.GetTask.lua new file mode 100644 index 00000000..06ccc32e --- /dev/null +++ b/custom/Schedule.GetTask.lua @@ -0,0 +1,5 @@ +---@realm server +---@source https://wiki.facepunch.com/gmod/Schedule:GetTask +---@param num number +---@return Task task +function Schedule:GetTask(num) end diff --git a/custom/WEAPON.AdjustMouseSensitivity.lua b/custom/WEAPON.AdjustMouseSensitivity.lua index a674a0d2..5164b8cf 100644 --- a/custom/WEAPON.AdjustMouseSensitivity.lua +++ b/custom/WEAPON.AdjustMouseSensitivity.lua @@ -2,6 +2,9 @@ ---@hook AdjustMouseSensitivity ---@realm client ---@source https://wiki.facepunch.com/gmod/WEAPON:AdjustMouseSensitivity +---@param defaultSensitivity number +---@param localFOV number +---@param defaultFOV number ---@return number? sensitivityMultiplier # Return a multiplier to override sensitivity. ---@[self_call_valid("GetOwner")] -function Weapon:AdjustMouseSensitivity() end +function Weapon:AdjustMouseSensitivity(defaultSensitivity, localFOV, defaultFOV) end diff --git a/custom/class.ContentContainer.lua b/custom/class.ContentContainer.lua index c3087fac..604c969c 100644 --- a/custom/class.ContentContainer.lua +++ b/custom/class.ContentContainer.lua @@ -2,8 +2,11 @@ ---@field IconList DTileLayout The tile layout panel that holds content icons, created in Init. ---@field m_pControllerPanel? Panel The controller panel (AccessorFunc-backed). ---@field m_strCategoryName? string The category name for this content container (AccessorFunc-backed). ----@field m_bTriggerSpawnlistChange boolean Whether modifications trigger the SpawnlistContentChanged hook (AccessorFunc-backed). +---@field m_bTriggerSpawnlistChange? boolean Whether modifications trigger the SpawnlistContentChanged hook (AccessorFunc-backed). local ContentContainer = {} ---@param trigger boolean function ContentContainer:SetTriggerSpawnlistChange(trigger) end + +---@param pnl Panel +function ContentContainer:Add(pnl) end diff --git a/custom/constraint.Motor.lua b/custom/constraint.Motor.lua new file mode 100644 index 00000000..82f0dda0 --- /dev/null +++ b/custom/constraint.Motor.lua @@ -0,0 +1,27 @@ +---Creates a motor constraint, a player controllable [constraint.Axis](https://wiki.facepunch.com/gmod/constraint.Axis). +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Motor +---@param ent1 Entity First entity. +---@param ent2 Entity Second entity. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +--- +--- See Entity:TranslateBoneToPhysBone. +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). Must be different from `bone1`. +--- +--- See Entity:TranslateBoneToPhysBone. +---@param localPos1 Vector Position relative to the the first physics object to constrain to. +---@param localPos2 Vector Position relative to the the second physics object to constrain to. +---@param friction number Motor friction. +---@param torque number Motor torque. +---@param forcetime number Automatic shut-off after this time has passed. A value of 0 means to stay on forever or until deactivated. +---@param nocollide? number Whether the entities should be no-collided. +---@param toggle? boolean|number Whether the constraint is on toggle. +---@param player? Player The player that will control the motor. Used to to call numpad.OnDown and numpad.OnUp. +---@param forcelimit? number Amount of force until it breaks (0 = unbreakable). +---@param key_fwd? number The key binding for "forward", corresponding to an Enums/KEY. +---@param key_bwd? number The key binding for "backwards", corresponding to an Enums/KEY. +---@param direction? number Either `1` or `-1` signifying which direction the motor should spin. +---@param localAxis? Vector Overrides axis of rotation? +---@return Entity|false # The created constraint. ([phys_torque](https://developer.valvesoftware.com/wiki/Phys_torque)) Will return `false` if the constraint could not be created. +---@return Entity? # The created axis constraint. ([phys_hinge](https://developer.valvesoftware.com/wiki/Phys_hinge)) Will return `nil` if the constraint could not be created. +function constraint.Motor(ent1, ent2, bone1, bone2, localPos1, localPos2, friction, torque, forcetime, nocollide, toggle, player, forcelimit, key_fwd, key_bwd, direction, localAxis) end diff --git a/custom/duplicator.Paste.lua b/custom/duplicator.Paste.lua new file mode 100644 index 00000000..638d9559 --- /dev/null +++ b/custom/duplicator.Paste.lua @@ -0,0 +1,8 @@ +---@realm server +---@source https://wiki.facepunch.com/gmod/duplicator.Paste +---@param Player Player? +---@param EntityList table +---@param ConstraintList table +---@return table createdEntities +---@return table createdConstraints +function duplicator.Paste(Player, EntityList, ConstraintList) end diff --git a/custom/engine.OpenDupe.lua b/custom/engine.OpenDupe.lua new file mode 100644 index 00000000..3edee5df --- /dev/null +++ b/custom/engine.OpenDupe.lua @@ -0,0 +1,8 @@ +---@class EngineDupe +---@field data string Compressed dupe data. + +---@realm client +---@source https://wiki.facepunch.com/gmod/engine.OpenDupe +---@param dupeName string +---@return EngineDupe? dupe +function engine.OpenDupe(dupeName) end diff --git a/custom/motionsensor.BuildSkeleton.lua b/custom/motionsensor.BuildSkeleton.lua new file mode 100644 index 00000000..f066597e --- /dev/null +++ b/custom/motionsensor.BuildSkeleton.lua @@ -0,0 +1,9 @@ +---@realm shared +---@source https://wiki.facepunch.com/gmod/motionsensor.BuildSkeleton +---@param translator SkeletonConvertor +---@param player Player +---@param rotation Angle +---@return table pos +---@return table ang +---@return table sensor +function motionsensor.BuildSkeleton(translator, player, rotation) end diff --git a/custom/scripted_ents.GetStored.lua b/custom/scripted_ents.GetStored.lua new file mode 100644 index 00000000..50d55531 --- /dev/null +++ b/custom/scripted_ents.GetStored.lua @@ -0,0 +1,8 @@ +---@class ScriptedEntityStored +---@field t table Registered SENT definition table. + +---@realm shared +---@source https://wiki.facepunch.com/gmod/scripted_ents.GetStored +---@param classname string +---@return ScriptedEntityStored? stored +function scripted_ents.GetStored(classname) end From b0a8dba9145d98df21aa60cfb9f39642e2565191 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 26 Jun 2026 20:21:52 +0100 Subject: [PATCH 084/117] Add ConVar guard metadata --- custom/Global.ConVarExists.lua | 8 ++++++++ custom/Global.GetConVar.lua | 1 + 2 files changed, 9 insertions(+) create mode 100644 custom/Global.ConVarExists.lua diff --git a/custom/Global.ConVarExists.lua b/custom/Global.ConVarExists.lua new file mode 100644 index 00000000..2cf72043 --- /dev/null +++ b/custom/Global.ConVarExists.lua @@ -0,0 +1,8 @@ +---Returns whether a [ConVar](https://wiki.facepunch.com/gmod/ConVar) with the given name exists or not +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.ConVarExists +---@[call_arg("gmod.convar", "exists")] +---@param name string Name of the ConVar. +---@return boolean # True if the ConVar exists, false otherwise. +function _G.ConVarExists(name) end diff --git a/custom/Global.GetConVar.lua b/custom/Global.GetConVar.lua index 920c031b..c4713100 100644 --- a/custom/Global.GetConVar.lua +++ b/custom/Global.GetConVar.lua @@ -4,6 +4,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.GetConVar +---@[call_arg("gmod.convar", "reference")] ---@param name string Name of the ConVar to get ---@return ConVar? # The ConVar object, or nil if no such ConVar was found. function _G.GetConVar( name ) end From 9e0bac2c3810fd4da19e8881249d938070b18be5 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 27 Jun 2026 01:05:42 +0100 Subject: [PATCH 085/117] Add sandbox entity class annotations --- custom/class.gmod_button.lua | 43 ++++++++++++++++++ custom/class.gmod_dynamite.lua | 39 ++++++++++++++++ custom/class.gmod_hoverball.lua | 62 ++++++++++++++++++++++++++ custom/class.gmod_lamp.lua | 79 +++++++++++++++++++++++++++++++++ custom/class.gmod_thruster.lua | 60 +++++++++++++++++++++++++ 5 files changed, 283 insertions(+) create mode 100644 custom/class.gmod_button.lua create mode 100644 custom/class.gmod_dynamite.lua create mode 100644 custom/class.gmod_hoverball.lua create mode 100644 custom/class.gmod_lamp.lua create mode 100644 custom/class.gmod_thruster.lua diff --git a/custom/class.gmod_button.lua b/custom/class.gmod_button.lua new file mode 100644 index 00000000..55380b72 --- /dev/null +++ b/custom/class.gmod_button.lua @@ -0,0 +1,43 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_button.lua +---@class gmod_button : base_gmodentity +local gmod_button = {} + +---@realm shared +---@return integer +function gmod_button:GetKey() end + +---@realm shared +---@param key integer +function gmod_button:SetKey(key) end + +---@realm shared +---@return boolean +function gmod_button:GetOn() end + +---@realm shared +---@param on boolean +function gmod_button:SetOn(on) end + +---@realm shared +---@return boolean +function gmod_button:GetIsToggle() end + +---@realm shared +---@param isToggle boolean +function gmod_button:SetIsToggle(isToggle) end + +---@realm shared +---@return string +function gmod_button:GetLabel() end + +---@realm shared +---@param label string +function gmod_button:SetLabel(label) end + +---@realm shared +---@param bEnable boolean +---@param ply? Player +function gmod_button:Toggle(bEnable, ply) end + +---@realm shared +function gmod_button:UpdateLever() end diff --git a/custom/class.gmod_dynamite.lua b/custom/class.gmod_dynamite.lua new file mode 100644 index 00000000..196fcbcf --- /dev/null +++ b/custom/class.gmod_dynamite.lua @@ -0,0 +1,39 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_dynamite.lua +---@class gmod_dynamite : base_gmodentity +local gmod_dynamite = {} + +---@realm shared +---@return boolean +function gmod_dynamite:GetShouldRemove() end + +---@realm shared +---@param shouldRemove boolean +function gmod_dynamite:SetShouldRemove(shouldRemove) end + +---@realm shared +---@return number +function gmod_dynamite:GetDamage() end + +---@realm shared +---@param damage number +function gmod_dynamite:SetDamage(damage) end + +---@realm shared +---@return number +function gmod_dynamite:GetDelay() end + +---@realm shared +---@param delay number +function gmod_dynamite:SetDelay(delay) end + +---@realm shared +---@param damage number +function gmod_dynamite:Setup(damage) end + +---@realm server +function gmod_dynamite:HandleQueuedExplosions() end + +---@realm shared +---@param delayOverride? number +---@param ply? Entity Fallbacks to self when no valid attacker entity is supplied. +function gmod_dynamite:Explode(delayOverride, ply) end diff --git a/custom/class.gmod_hoverball.lua b/custom/class.gmod_hoverball.lua new file mode 100644 index 00000000..75e88596 --- /dev/null +++ b/custom/class.gmod_hoverball.lua @@ -0,0 +1,62 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_hoverball.lua +---@class gmod_hoverball : base_gmodentity +local gmod_hoverball = {} + +---@realm shared +---@return boolean +function gmod_hoverball:GetEnabled() end + +---@realm shared +---@param enabled boolean +function gmod_hoverball:SetEnabled(enabled) end + +---@realm shared +---@return number +function gmod_hoverball:GetTargetZ() end + +---@realm shared +---@param z number +function gmod_hoverball:SetTargetZ(z) end + +---@realm shared +---@return number +function gmod_hoverball:GetSpeedVar() end + +---@realm shared +---@param speed number +function gmod_hoverball:SetSpeedVar(speed) end + +---@realm shared +---@return number +function gmod_hoverball:GetAirResistanceVar() end + +---@realm shared +---@param resistance number +function gmod_hoverball:SetAirResistanceVar(resistance) end + +---@realm shared +---@return number +function gmod_hoverball:GetSpeed() end + +---@realm shared +---@param s number +function gmod_hoverball:SetSpeed(s) end + +---@realm shared +---@return number +function gmod_hoverball:GetAirResistance() end + +---@realm shared +---@param num number +function gmod_hoverball:SetAirResistance(num) end + +---@realm shared +---@param z number +function gmod_hoverball:SetZVelocity(z) end + +---@realm shared +---@param strength number +function gmod_hoverball:SetStrength(strength) end + +---@realm shared +function gmod_hoverball:Toggle() end diff --git a/custom/class.gmod_lamp.lua b/custom/class.gmod_lamp.lua new file mode 100644 index 00000000..76a4b2c3 --- /dev/null +++ b/custom/class.gmod_lamp.lua @@ -0,0 +1,79 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_lamp.lua +---@class gmod_lamp : base_gmodentity +local gmod_lamp = {} + +---@class gmod_lamp.LightInfo +---@field Offset Vector +---@field Angle Angle +---@field NearZ number +---@field Scale number +---@field Skin number + +---@realm shared +---@return boolean +function gmod_lamp:GetOn() end + +---@realm shared +---@param on boolean +function gmod_lamp:SetOn(on) end + +---@realm shared +---@return boolean +function gmod_lamp:GetToggle() end + +---@realm shared +---@param toggle boolean +function gmod_lamp:SetToggle(toggle) end + +---@realm shared +---@return number +function gmod_lamp:GetLightFOV() end + +---@realm shared +---@param fov number +function gmod_lamp:SetLightFOV(fov) end + +---@realm shared +---@return number +function gmod_lamp:GetDistance() end + +---@realm shared +---@param distance number +function gmod_lamp:SetDistance(distance) end + +---@realm shared +---@return number +function gmod_lamp:GetBrightness() end + +---@realm shared +---@param brightness number +function gmod_lamp:SetBrightness(brightness) end + +---@realm shared +---@param ply? Player Extra arguments are ignored by the entity method but passed by the drive property. +---@return string +function gmod_lamp:GetEntityDriveMode(ply) end + +---@realm shared +---@return gmod_lamp.LightInfo +function gmod_lamp:GetLightInfo() end + +---@realm server +---@param bOn boolean +function gmod_lamp:Switch(bOn) end + +---@realm server +---@param bOn boolean +function gmod_lamp:OnSwitch(bOn) end + +---@realm server +function gmod_lamp:Toggle() end + +---@realm server +---@param name string +---@param old any +---@param new any +function gmod_lamp:OnUpdateLight(name, old, new) end + +---@realm server +function gmod_lamp:UpdateLight() end diff --git a/custom/class.gmod_thruster.lua b/custom/class.gmod_thruster.lua new file mode 100644 index 00000000..e3bb816f --- /dev/null +++ b/custom/class.gmod_thruster.lua @@ -0,0 +1,60 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_thruster.lua +---@class gmod_thruster : base_gmodentity +local gmod_thruster = {} + +---@realm shared +---@param name string +function gmod_thruster:SetEffect(name) end + +---@realm shared +---@return string +function gmod_thruster:GetEffect() end + +---@realm shared +---@param on boolean +function gmod_thruster:SetOn(on) end + +---@realm shared +---@return boolean +function gmod_thruster:IsOn() end + +---@realm shared +---@param v Vector +function gmod_thruster:SetOffset(v) end + +---@realm shared +---@return Vector +function gmod_thruster:GetOffset() end + +---@realm server +---@param force? number +---@param mul? number +function gmod_thruster:SetForce(force, mul) end + +---@realm server +---@param mul number +---@param bDown boolean +function gmod_thruster:AddMul(mul, bDown) end + +---@realm server +---@param on boolean +---@return boolean +function gmod_thruster:Switch(on) end + +---@realm server +---@param sound string +function gmod_thruster:SetSound(sound) end + +---@realm server +function gmod_thruster:StartThrustSound() end + +---@realm server +function gmod_thruster:StopThrustSound() end + +---@realm server +---@param tog boolean +function gmod_thruster:SetToggle(tog) end + +---@realm server +---@return boolean +function gmod_thruster:GetToggle() end From 4aae9da1e2667d9d2e66648bf5a012c03d5480b5 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 27 Jun 2026 02:47:15 +0100 Subject: [PATCH 086/117] Add owner guard to deploy and think callbacks --- custom/WEAPON.Deploy.lua | 7 +++++++ custom/WEAPON.Think.lua | 6 ++++++ 2 files changed, 13 insertions(+) create mode 100644 custom/WEAPON.Deploy.lua create mode 100644 custom/WEAPON.Think.lua diff --git a/custom/WEAPON.Deploy.lua b/custom/WEAPON.Deploy.lua new file mode 100644 index 00000000..69c9572b --- /dev/null +++ b/custom/WEAPON.Deploy.lua @@ -0,0 +1,7 @@ +---Called when player has just switched to this weapon. +---@hook Deploy +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:Deploy +---@return boolean? # Return true to allow switching away from this weapon using `lastinv` command. +---@[self_call_valid("GetOwner")] +function Weapon:Deploy() end diff --git a/custom/WEAPON.Think.lua b/custom/WEAPON.Think.lua new file mode 100644 index 00000000..4f4c9abc --- /dev/null +++ b/custom/WEAPON.Think.lua @@ -0,0 +1,6 @@ +---Called when the weapon thinks. +---@hook Think +---@realm shared +---@source https://wiki.facepunch.com/gmod/WEAPON:Think +---@[self_call_valid("GetOwner")] +function Weapon:Think() end From fbd0afe9fc4436d5678e649692e7f341a7d29ce5 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 27 Jun 2026 06:13:59 +0100 Subject: [PATCH 087/117] Add winch controller constraint annotations --- custom/class.gmod_winch_constraint.lua | 14 +++++++++++++ custom/class.gmod_winch_controller.lua | 27 ++++++++++++++++++++++++++ custom/constraint.Hydraulic.lua | 24 +++++++++++++++++++++++ custom/constraint.Muscle.lua | 25 ++++++++++++++++++++++++ custom/constraint.Winch.lua | 22 +++++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 custom/class.gmod_winch_constraint.lua create mode 100644 custom/class.gmod_winch_controller.lua create mode 100644 custom/constraint.Hydraulic.lua create mode 100644 custom/constraint.Muscle.lua create mode 100644 custom/constraint.Winch.lua diff --git a/custom/class.gmod_winch_constraint.lua b/custom/class.gmod_winch_constraint.lua new file mode 100644 index 00000000..d3106169 --- /dev/null +++ b/custom/class.gmod_winch_constraint.lua @@ -0,0 +1,14 @@ +---@source garrysmod/lua/includes/modules/constraint.lua +---@class gmod_winch_constraint : Entity +---@field Ent1 Entity First constrained entity. +---@field Ent2 Entity Second constrained entity. +---@field Phys1 PhysObj First constrained physics object. +---@field Phys2 PhysObj Second constrained physics object. +---@field LPos1 Vector First local constraint position. +---@field LPos2 Vector Second local constraint position. +---@field fwd_speed number Forward winch/hydraulic speed. +---@field bwd_speed number Backward winch/hydraulic speed. +---@field period number Muscle period. +---@field amplitude number Muscle amplitude. +---@field toggle boolean Toggle behavior flag. +local gmod_winch_constraint = {} diff --git a/custom/class.gmod_winch_controller.lua b/custom/class.gmod_winch_controller.lua new file mode 100644 index 00000000..54a04eac --- /dev/null +++ b/custom/class.gmod_winch_controller.lua @@ -0,0 +1,27 @@ +---@source garrysmod/gamemodes/sandbox/entities/entities/gmod_winch_controller.lua +---@class gmod_winch_controller : Entity +---@field constraint gmod_winch_constraint The spring constraint being managed. +---@field rope Entity The rope (keyframe_rope or ents.CreateClientRope) being managed. +---@field direction integer Direction of movement: -1 (DIR_BACKWARD), 0 (DIR_NONE), 1 (DIR_FORWARD). +---@field toggle boolean Toggle behavior flag inherited from constraint. +---@field current_length number Current simulated length of the rope. +---@field min_length number Minimum length limit. +---@field max_length? number Optional maximum length limit. +---@field type integer Controller type: 0 (TYPE_NORMAL), 1 (TYPE_MUSCLE). +---@field ctime number Muscle cycle/timer progress tracker. +---@field isexpanded boolean Expansion limit state flag. +---@field last_time number Real timestamp of the previous think cycle. +---@field init_time number Real timestamp of entity initialization. +local gmod_winch_controller = {} + +---@realm server +---@return integer +function gmod_winch_controller:GetDirection() end + +---@realm server +---@param n integer +function gmod_winch_controller:SetDirection(n) end + +---@realm server +---@return boolean +function gmod_winch_controller:IsExpanded() end diff --git a/custom/constraint.Hydraulic.lua b/custom/constraint.Hydraulic.lua new file mode 100644 index 00000000..9ecc4437 --- /dev/null +++ b/custom/constraint.Hydraulic.lua @@ -0,0 +1,24 @@ +---Creates a Hydraulic constraint. +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Hydraulic +---@param pl Player The player creating the constraint. +---@param ent1 Entity First entity to constrain. +---@param ent2 Entity Second entity to constrain. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). +---@param localPos1 Vector Position relative to the first physics object to constrain to. +---@param localPos2 Vector Position relative to the second physics object to constrain to. +---@param lengthMin number Minimum length of the hydraulic spring constraint. +---@param lengthMax number Maximum length of the hydraulic spring constraint. +---@param width number Width of the rope. +---@param key number Numpad key binding for the hydraulic controller. +---@param fixed number Whether the hydraulic is fixed (1) or not (0). +---@param speed number Speed of movement. +---@param material string The material of the rope. +---@param toggle boolean Toggle behavior flag. +---@param color Color The color of the rope. See Color. +---@return Entity|false|nil # The created spring constraint. Returns `false` for invalid inputs. +---@return Entity? # The created rope entity (`keyframe_rope`). Returns `nil` if no rope was created. +---@return gmod_winch_controller? # The created winch controller. +---@return Entity? # The created slider constraint if `fixed` is 1. +function constraint.Hydraulic(pl, ent1, ent2, bone1, bone2, localPos1, localPos2, lengthMin, lengthMax, width, key, fixed, speed, material, toggle, color) end diff --git a/custom/constraint.Muscle.lua b/custom/constraint.Muscle.lua new file mode 100644 index 00000000..90f20664 --- /dev/null +++ b/custom/constraint.Muscle.lua @@ -0,0 +1,25 @@ +---Creates a Muscle constraint. +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Muscle +---@param pl Player The player creating the constraint. +---@param ent1 Entity First entity to constrain. +---@param ent2 Entity Second entity to constrain. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). +---@param localPos1 Vector Position relative to the first physics object to constrain to. +---@param localPos2 Vector Position relative to the second physics object to constrain to. +---@param length1 number Min/Max length 1. +---@param length2 number Min/Max length 2. +---@param width number Width of the rope. +---@param key number Numpad key binding for the muscle controller. +---@param fixed number Whether the muscle is fixed (1) or not (0). +---@param period number Pulse frequency period/periodical adjustment. +---@param amplitude number Pulse range amplitude. +---@param starton boolean Whether the muscle starts relaxed or active. +---@param material string The material of the rope. +---@param color Color The color of the rope. See Color. +---@return Entity|false|nil # The created spring constraint. Returns `false` for invalid inputs. +---@return Entity? # The created rope entity (`keyframe_rope`). Returns `nil` if no rope was created. +---@return gmod_winch_controller? # The created winch controller. +---@return Entity? # The created slider constraint if `fixed` is 1. +function constraint.Muscle(pl, ent1, ent2, bone1, bone2, localPos1, localPos2, length1, length2, width, key, fixed, period, amplitude, starton, material, color) end diff --git a/custom/constraint.Winch.lua b/custom/constraint.Winch.lua new file mode 100644 index 00000000..d81fff49 --- /dev/null +++ b/custom/constraint.Winch.lua @@ -0,0 +1,22 @@ +---Creates a Winch constraint. +---@realm server +---@source https://wiki.facepunch.com/gmod/constraint.Winch +---@param pl Player The player creating the constraint. +---@param ent1 Entity First entity to constrain. +---@param ent2 Entity Second entity to constrain. +---@param bone1 number PhysObj number of first entity to constrain to. (0 for non-ragdolls). +---@param bone2 number PhysObj number of second entity to constrain to. (0 for non-ragdolls). +---@param localPos1 Vector Position relative to the first physics object to constrain to. +---@param localPos2 Vector Position relative to the second physics object to constrain to. +---@param width number Width of the rope. +---@param fwd_bind number Numpad key binding for forward action. +---@param bwd_bind number Numpad key binding for backward action. +---@param fwd_speed number Speed of forward movement. +---@param bwd_speed number Speed of backward movement. +---@param material string The material of the rope. +---@param toggle boolean Toggle behavior flag. +---@param color Color The color of the rope. See Color. +---@return Entity|false|nil # The created spring constraint. Returns `false` for invalid inputs. +---@return Entity? # The created rope entity (`keyframe_rope`). Returns `nil` if no rope was created. +---@return gmod_winch_controller? # The created winch controller. +function constraint.Winch(pl, ent1, ent2, bone1, bone2, localPos1, localPos2, width, fwd_bind, bwd_bind, fwd_speed, bwd_speed, material, toggle, color) end From c4267d0b50bed41eb98c616cd47c4240b4b70664 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 29 Jun 2026 07:56:43 +0100 Subject: [PATCH 088/117] Fix various annotations based on lua source --- __tests__/cli-generate-lua.spec.ts | 30 +- __tests__/custom-annotations.spec.ts | 627 ++++++-------------------- custom/Entity.GetNW2Angle.lua | 2 +- custom/Entity.GetNW2Bool.lua | 2 +- custom/Entity.GetNW2Entity.lua | 2 +- custom/Entity.GetNW2Float.lua | 2 +- custom/Entity.GetNW2Int.lua | 2 +- custom/Entity.GetNW2String.lua | 2 +- custom/Entity.GetNW2Vector.lua | 2 +- custom/Entity.GetNWAngle.lua | 2 +- custom/Entity.GetNWBool.lua | 2 +- custom/Entity.GetNWEntity.lua | 2 +- custom/Entity.GetNWFloat.lua | 2 +- custom/Entity.GetNWInt.lua | 2 +- custom/Entity.GetNWString.lua | 2 +- custom/Entity.GetNWVector.lua | 2 +- custom/Entity.GetNetworked2Angle.lua | 2 +- custom/Entity.GetNetworked2Bool.lua | 2 +- custom/Entity.GetNetworked2Entity.lua | 2 +- custom/Entity.GetNetworked2Float.lua | 2 +- custom/Entity.GetNetworked2Int.lua | 2 +- custom/Entity.GetNetworked2String.lua | 2 +- custom/Entity.GetNetworked2Vector.lua | 2 +- custom/Entity.GetNetworkedAngle.lua | 2 +- custom/Entity.GetNetworkedBool.lua | 2 +- custom/Entity.GetNetworkedEntity.lua | 2 +- custom/Entity.GetNetworkedFloat.lua | 2 +- custom/Entity.GetNetworkedInt.lua | 2 +- custom/Entity.GetNetworkedString.lua | 2 +- custom/Entity.GetNetworkedVector.lua | 2 +- custom/Global.IsHostingGame.lua | 4 - custom/Global.collectgarbage.lua | 2 +- custom/UGCPublishWindow.DoPublish.lua | 4 - custom/class.ContentSidebar.lua | 2 +- custom/class.ContextBase.lua | 2 + custom/class.DColorCube.lua | 3 + custom/class.DFrame.lua | 10 + custom/class.DHTMLControls.lua | 5 +- custom/class.DImage.lua | 6 + custom/class.DImageButton.lua | 4 + custom/class.DListView.lua | 6 + custom/class.DMenu.lua | 3 + custom/class.DMenuBar.lua | 2 + custom/class.DMenuOption.lua | 4 + custom/steamworks.SetFavorite.lua | 7 - custom/workshopfilebase.dupes.lua | 56 --- 46 files changed, 235 insertions(+), 598 deletions(-) delete mode 100644 custom/Global.IsHostingGame.lua delete mode 100644 custom/UGCPublishWindow.DoPublish.lua create mode 100644 custom/class.DMenu.lua delete mode 100644 custom/steamworks.SetFavorite.lua delete mode 100644 custom/workshopfilebase.dupes.lua diff --git a/__tests__/cli-generate-lua.spec.ts b/__tests__/cli-generate-lua.spec.ts index 301f827e..15e27e35 100644 --- a/__tests__/cli-generate-lua.spec.ts +++ b/__tests__/cli-generate-lua.spec.ts @@ -207,6 +207,10 @@ describe('cli-generate-lua', () => { writeEntityGetterPage('getnwentity.json', 'GetNWEntity', 'any', 'NULL'); writeEntityGetterPage('getnwint.json', 'GetNWInt', 'any', '0'); writeEntityGetterPage('getnetworkedentity.json', 'GetNetworkedEntity', 'Entity', 'NULL'); + writeEntityGetterPage('getnwbool.json', 'GetNWBool', 'any', 'false'); + writeEntityGetterPage('getnwstring.json', 'GetNWString', 'any', '""'); + writeEntityGetterPage('getnwvector.json', 'GetNWVector', 'any', 'Vector(0,0,0)'); + writeEntityGetterPage('getnwangle.json', 'GetNWAngle', 'any', 'Angle(0,0,0)'); try { const command = process.platform === 'win32' ? 'npm.cmd' : 'npm'; @@ -222,15 +226,27 @@ describe('cli-generate-lua', () => { expect(result.status).toBe(0); const entityLua = fs.readFileSync(path.join(outputPath, 'entity.lua'), 'utf8'); - expect(entityLua).toContain('---@overload fun(self: Entity, key: string): Entity|NULL # The value associated with the key'); - expect(entityLua).toContain('---@overload fun(self: Entity, key: string): number # The value associated with the key'); - expect(entityLua).toContain('---@overload fun(self: Entity, key: string): Entity|NULL # The retrieved value'); - expect(entityLua).toContain('---@param fallback T The value to return if we failed to retrieve the value.'); - expect(entityLua).toContain('---@return Entity|T # The value associated with the key'); - expect(entityLua).toContain('---@return number|T # The value associated with the key'); - expect(entityLua).toContain('---@return Entity|T # The retrieved value'); + const expectedGetters = [ + { name: 'GetNWEntity', overload: 'Entity|NULL', fallback: 'NULL', returns: 'Entity|T' }, + { name: 'GetNWInt', overload: 'number', fallback: '0', returns: 'number|T' }, + { name: 'GetNetworkedEntity', overload: 'Entity|NULL', fallback: 'NULL', returns: 'Entity|T' }, + { name: 'GetNWBool', overload: 'boolean', fallback: 'false', returns: 'boolean|T' }, + { name: 'GetNWString', overload: 'string', fallback: '""', returns: 'string|T' }, + { name: 'GetNWVector', overload: 'Vector', fallback: 'Vector( 0, 0, 0 )', returns: 'Vector|T' }, + { name: 'GetNWAngle', overload: 'Angle', fallback: 'Angle( 0, 0, 0 )', returns: 'Angle|T' }, + ]; + + for (const getter of expectedGetters) { + const block = entityLua.match(new RegExp(`---@source https://wiki\\.facepunch\\.com/gmod/Entity:${getter.name}[\\s\\S]*?function Entity:${getter.name}\\(key, fallback\\) end`))?.[0]; + expect(block).toBeDefined(); + expect(block).toContain(`---@overload fun(self: Entity, key: string): ${getter.overload}`); + expect(block).toContain(`---@param fallback? T=${getter.fallback}`); + expect(block).toContain(`---@return ${getter.returns}`); + } + expect(entityLua).not.toContain('---@param fallback? Entity'); expect(entityLua).not.toContain('---@param fallback? number'); + expect(entityLua).not.toMatch(/---@param fallback\? T .*Defaults to/); expect(entityLua).not.toContain('---@return any'); } finally { fs.rmSync(tmpRoot, { recursive: true, force: true }); diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 291dc998..9c27486e 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -2,8 +2,28 @@ import fs from 'fs'; import path from 'path'; describe('custom and plugin annotation smoke checks', () => { - const readCustom = (file: string) => fs.readFileSync(path.join(process.cwd(), 'custom', file), 'utf8'); - const readOutput = (file: string) => fs.readFileSync(path.join(process.cwd(), 'output', file), 'utf8'); + const customRoot = path.join(process.cwd(), 'custom'); + const outputRoot = path.join(process.cwd(), 'output'); + + const readCustom = (file: string) => fs.readFileSync(path.join(customRoot, file), 'utf8'); + const readOutput = (file: string) => fs.readFileSync(path.join(outputRoot, file), 'utf8'); + + const significantOverrideLines = (content: string) => + content + .split(/\r?\n/) + .map((line) => line.trimEnd()) + .filter((line) => line.startsWith('---@') || /^function\s+/.test(line)) + .filter((line) => !line.startsWith('---@meta') && !line.startsWith('---@source')); + + const expectCustomLinesInOutput = (customFile: string, outputFile: string) => { + const customLines = significantOverrideLines(readCustom(customFile)); + const output = readOutput(outputFile); + + expect(customLines.length).toBeGreaterThan(0); + for (const line of customLines) { + expect(output).toContain(line); + } + }; test('darkrp plugin annotation files exist and are scoped', () => { const darkrpLua = path.join(process.cwd(), 'plugin', 'darkrp', 'annotations', 'darkrp.lua'); @@ -21,498 +41,127 @@ describe('custom and plugin annotation smoke checks', () => { expect(camiContent).toMatch(/CAMI/); }); - test('new custom class overrides and global alias are present', () => { - const customRoot = path.join(process.cwd(), 'custom'); - const globals = fs.readFileSync(path.join(customRoot, '_globals.lua'), 'utf8'); - const gm = fs.readFileSync(path.join(customRoot, 'class.GM.lua'), 'utf8'); - const dCheckBoxLabel = fs.readFileSync(path.join(customRoot, 'class.DCheckBoxLabel.lua'), 'utf8'); - const dColorCube = fs.readFileSync(path.join(customRoot, 'class.DColorCube.lua'), 'utf8'); - const dColorMixer = fs.readFileSync(path.join(customRoot, 'class.DColorMixer.lua'), 'utf8'); - const dComboBox = fs.readFileSync(path.join(customRoot, 'class.DComboBox.lua'), 'utf8'); - const dFileBrowser = fs.readFileSync(path.join(customRoot, 'class.DFileBrowser.lua'), 'utf8'); - const dHtmlControls = fs.readFileSync(path.join(customRoot, 'class.DHTMLControls.lua'), 'utf8'); - const dHorizontalScroller = fs.readFileSync(path.join(customRoot, 'class.DHorizontalScroller.lua'), 'utf8'); - const dhScrollBar = fs.readFileSync(path.join(customRoot, 'class.DHScrollBar.lua'), 'utf8'); - const dListView = fs.readFileSync(path.join(customRoot, 'class.DListView.lua'), 'utf8'); - const dMenuBar = fs.readFileSync(path.join(customRoot, 'class.DMenuBar.lua'), 'utf8'); - const dMenuOption = fs.readFileSync(path.join(customRoot, 'class.DMenuOption.lua'), 'utf8'); - const dModelSelectMulti = fs.readFileSync(path.join(customRoot, 'class.DModelSelectMulti.lua'), 'utf8'); - const dNotify = fs.readFileSync(path.join(customRoot, 'class.DNotify.lua'), 'utf8'); - const dPanelList = fs.readFileSync(path.join(customRoot, 'class.DPanelList.lua'), 'utf8'); - const dPanelSelect = fs.readFileSync(path.join(customRoot, 'class.DPanelSelect.lua'), 'utf8'); - const dProperties = fs.readFileSync(path.join(customRoot, 'class.DProperties.lua'), 'utf8'); - const dTree = fs.readFileSync(path.join(customRoot, 'class.DTree.lua'), 'utf8'); - const dTreeNode = fs.readFileSync(path.join(customRoot, 'class.DTree_Node.lua'), 'utf8'); - const dvScrollBar = fs.readFileSync(path.join(customRoot, 'class.DVScrollBar.lua'), 'utf8'); - const dMenuAddPanel = fs.readFileSync(path.join(customRoot, 'DMenu.AddPanel.lua'), 'utf8'); - const dCheckBoxSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetValue.lua'), 'utf8'); - const dCheckBoxSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBox.SetChecked.lua'), 'utf8'); - const dCheckBoxLabelSetValue = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetValue.lua'), 'utf8'); - const dCheckBoxLabelSetChecked = fs.readFileSync(path.join(customRoot, 'DCheckBoxLabel.SetChecked.lua'), 'utf8'); - const dButtonUpdateColours = fs.readFileSync(path.join(customRoot, 'DButton.UpdateColours.lua'), 'utf8'); - const dFileBrowserSetOpen = fs.readFileSync(path.join(customRoot, 'DFileBrowser.SetOpen.lua'), 'utf8'); - const dImageSetMatName = fs.readFileSync(path.join(customRoot, 'DImage.SetMatName.lua'), 'utf8'); - const dMenuSetOpenSubMenu = fs.readFileSync(path.join(customRoot, 'DMenu.SetOpenSubMenu.lua'), 'utf8'); - const dPropertyGenericValueChanged = fs.readFileSync(path.join(customRoot, 'DProperty_Generic.ValueChanged.lua'), 'utf8'); - const dSliderSetNotches = fs.readFileSync(path.join(customRoot, 'DSlider.SetNotches.lua'), 'utf8'); - const dTreeNodeChildExpanded = fs.readFileSync(path.join(customRoot, 'DTree_Node.ChildExpanded.lua'), 'utf8'); - const dTreeNodePopulateChildrenAndSelf = fs.readFileSync(path.join(customRoot, 'DTree_Node.PopulateChildrenAndSelf.lua'), 'utf8'); - const dTreeNodeSetShowFiles = fs.readFileSync(path.join(customRoot, 'DTree_Node.SetShowFiles.lua'), 'utf8'); - const dTreeNodeSetWildCard = fs.readFileSync(path.join(customRoot, 'DTree_Node.SetWildCard.lua'), 'utf8'); - const panelAdd = fs.readFileSync(path.join(customRoot, 'Panel.Add.lua'), 'utf8'); - const panelSetSelectionCanvas = fs.readFileSync(path.join(customRoot, 'Panel.SetSelectionCanvas.lua'), 'utf8'); - const panelSetParent = fs.readFileSync(path.join(customRoot, 'Panel.SetParent.lua'), 'utf8'); - const panelGetCookie = fs.readFileSync(path.join(customRoot, 'Panel.GetCookie.lua'), 'utf8'); - const panelGetCookieNumber = fs.readFileSync(path.join(customRoot, 'Panel.GetCookieNumber.lua'), 'utf8'); - const panelSetCookie = fs.readFileSync(path.join(customRoot, 'Panel.SetCookie.lua'), 'utf8'); - const cookieSet = fs.readFileSync(path.join(customRoot, 'cookie.Set.lua'), 'utf8'); - const propertyAdd = fs.readFileSync(path.join(customRoot, 'PropertyAdd.lua'), 'utf8'); - const httpRequest = fs.readFileSync(path.join(customRoot, 'HTTPRequest.lua'), 'utf8'); - const globalHttp = fs.readFileSync(path.join(customRoot, 'Global.HTTP.lua'), 'utf8'); - const entsCreate = fs.readFileSync(path.join(customRoot, 'ents.Create.lua'), 'utf8'); - const vehicleGetDriver = fs.readFileSync(path.join(customRoot, 'Vehicle.GetDriver.lua'), 'utf8'); - const getNWEntity = fs.readFileSync(path.join(customRoot, 'Entity.GetNWEntity.lua'), 'utf8'); - const getNW2Entity = fs.readFileSync(path.join(customRoot, 'Entity.GetNW2Entity.lua'), 'utf8'); - const getNetworkedEntity = fs.readFileSync(path.join(customRoot, 'Entity.GetNetworkedEntity.lua'), 'utf8'); - const getNetworked2Entity = fs.readFileSync(path.join(customRoot, 'Entity.GetNetworked2Entity.lua'), 'utf8'); - const dPropertySheetAddSheet = fs.readFileSync(path.join(customRoot, 'DPropertySheet.AddSheet.lua'), 'utf8'); - const dLabelUpdateColours = fs.readFileSync(path.join(customRoot, 'DLabel.UpdateColours.lua'), 'utf8'); - const ctrlColor = fs.readFileSync(path.join(customRoot, 'class.CtrlColor.lua'), 'utf8'); - const controlPanelAddControl = fs.readFileSync(path.join(customRoot, 'ControlPanel.AddControl.lua'), 'utf8'); - const entityCopyData = fs.readFileSync(path.join(customRoot, 'EntityCopyData.lua'), 'utf8'); - const duplicatorCreateEntityFromTable = fs.readFileSync(path.join(customRoot, 'duplicator.CreateEntityFromTable.lua'), 'utf8'); - const osDate = fs.readFileSync(path.join(customRoot, 'os.date.lua'), 'utf8'); - const tableCopy = fs.readFileSync(path.join(customRoot, 'table.Copy.lua'), 'utf8'); - const contentContainer = fs.readFileSync(path.join(customRoot, 'class.ContentContainer.lua'), 'utf8'); - const propVehiclePrisonerPod = fs.readFileSync(path.join(customRoot, 'class.prop_vehicle_prisoner_pod.lua'), 'utf8'); - const propRagdoll = fs.readFileSync(path.join(customRoot, 'class.prop_ragdoll.lua'), 'utf8'); - const propDynamicOverride = fs.readFileSync(path.join(customRoot, 'class.prop_dynamic_override.lua'), 'utf8'); - const envFire = fs.readFileSync(path.join(customRoot, 'class.env_fire.lua'), 'utf8'); - const matProxyData = fs.readFileSync(path.join(customRoot, 'MatProxyData.lua'), 'utf8'); - const iMaterialSetTexture = fs.readFileSync(path.join(customRoot, 'IMaterial.SetTexture.lua'), 'utf8'); - const renderClearRenderTarget = fs.readFileSync(path.join(customRoot, 'render.ClearRenderTarget.lua'), 'utf8'); - const vguiRegisterFile = fs.readFileSync(path.join(customRoot, 'vgui.RegisterFile.lua'), 'utf8'); - const viewData = fs.readFileSync(path.join(customRoot, 'ViewData.lua'), 'utf8'); - const engineEntities = fs.readFileSync(path.join(customRoot, 'class.EngineEntities.lua'), 'utf8'); - const enginePanels = fs.readFileSync(path.join(customRoot, 'class.EnginePanels.lua'), 'utf8'); - const baseGmodEntity = fs.readFileSync(path.join(customRoot, 'class.base_gmodentity.lua'), 'utf8'); - const baseAi = fs.readFileSync(path.join(customRoot, 'class.base_ai.lua'), 'utf8'); - const effect = fs.readFileSync(path.join(customRoot, 'class.EFFECT.lua'), 'utf8'); - const luaParticleSetColor = fs.readFileSync(path.join(customRoot, 'CLuaParticle.SetColor.lua'), 'utf8'); - const renderGroup = fs.readFileSync(path.join(customRoot, 'RENDERGROUP.lua'), 'utf8'); - const skeletonConvertor = fs.readFileSync(path.join(customRoot, 'class.SkeletonConvertor.lua'), 'utf8'); - const listSet = fs.readFileSync(path.join(customRoot, 'list.Set.lua'), 'utf8'); - const serverQueryData = fs.readFileSync(path.join(customRoot, 'ServerQueryData.lua'), 'utf8'); - const skin = fs.readFileSync(path.join(customRoot, 'class.SKIN.lua'), 'utf8'); - const generatedCustomClasses = fs.readFileSync(path.join(process.cwd(), 'output', 'custom_classes.lua'), 'utf8'); - const generatedEntity = fs.readFileSync(path.join(process.cwd(), 'output', 'entity.lua'), 'utf8'); - const generatedEffect = fs.readFileSync(path.join(process.cwd(), 'output', 'effect.lua'), 'utf8'); - const generatedLuaParticle = fs.readFileSync(path.join(process.cwd(), 'output', 'cluaparticle.lua'), 'utf8'); - const generatedEnums = fs.readFileSync(path.join(process.cwd(), 'output', 'enums.lua'), 'utf8'); - const generatedGM = fs.readFileSync(path.join(process.cwd(), 'output', 'gm.lua'), 'utf8'); - const generatedList = fs.readFileSync(path.join(process.cwd(), 'output', 'list.lua'), 'utf8'); - const generatedDColorCube = fs.readFileSync(path.join(process.cwd(), 'output', 'dcolorcube.lua'), 'utf8'); - const generatedDColorMixer = fs.readFileSync(path.join(process.cwd(), 'output', 'dcolormixer.lua'), 'utf8'); - const generatedDComboBox = fs.readFileSync(path.join(process.cwd(), 'output', 'dcombobox.lua'), 'utf8'); - const generatedDFileBrowser = fs.readFileSync(path.join(process.cwd(), 'output', 'dfilebrowser.lua'), 'utf8'); - const generatedDHTMLControls = fs.readFileSync(path.join(process.cwd(), 'output', 'dhtmlcontrols.lua'), 'utf8'); - const generatedDHorizontalScroller = fs.readFileSync(path.join(process.cwd(), 'output', 'dhorizontalscroller.lua'), 'utf8'); - const generatedDHScrollBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dhscrollbar.lua'), 'utf8'); - const generatedDImage = fs.readFileSync(path.join(process.cwd(), 'output', 'dimage.lua'), 'utf8'); - const generatedDListView = fs.readFileSync(path.join(process.cwd(), 'output', 'dlistview.lua'), 'utf8'); - const generatedDMenuBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenubar.lua'), 'utf8'); - const generatedDMenuOption = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenuoption.lua'), 'utf8'); - const generatedDModelSelectMulti = fs.readFileSync(path.join(process.cwd(), 'output', 'dmodelselectmulti.lua'), 'utf8'); - const generatedDNotify = fs.readFileSync(path.join(process.cwd(), 'output', 'dnotify.lua'), 'utf8'); - const generatedDPanelList = fs.readFileSync(path.join(process.cwd(), 'output', 'dpanellist.lua'), 'utf8'); - const generatedDPanelSelect = fs.readFileSync(path.join(process.cwd(), 'output', 'dpanelselect.lua'), 'utf8'); - const generatedDProperties = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperties.lua'), 'utf8'); - const generatedDButton = fs.readFileSync(path.join(process.cwd(), 'output', 'dbutton.lua'), 'utf8'); - const generatedDLabel = fs.readFileSync(path.join(process.cwd(), 'output', 'dlabel.lua'), 'utf8'); - const generatedDMenu = fs.readFileSync(path.join(process.cwd(), 'output', 'dmenu.lua'), 'utf8'); - const generatedDPropertyGeneric = fs.readFileSync(path.join(process.cwd(), 'output', 'dproperty_generic.lua'), 'utf8'); - const generatedDSlider = fs.readFileSync(path.join(process.cwd(), 'output', 'dslider.lua'), 'utf8'); - const generatedDTreeNode = fs.readFileSync(path.join(process.cwd(), 'output', 'dtree_node.lua'), 'utf8'); - const generatedDTree = fs.readFileSync(path.join(process.cwd(), 'output', 'dtree.lua'), 'utf8'); - const generatedDVScrollBar = fs.readFileSync(path.join(process.cwd(), 'output', 'dvscrollbar.lua'), 'utf8'); - const generatedPanel = fs.readFileSync(path.join(process.cwd(), 'output', 'panel.lua'), 'utf8'); - const generatedVgui = fs.readFileSync(path.join(process.cwd(), 'output', 'vgui.lua'), 'utf8'); - const generatedRender = fs.readFileSync(path.join(process.cwd(), 'output', 'render.lua'), 'utf8'); - const generatedStructures = fs.readFileSync(path.join(process.cwd(), 'output', 'structures.lua'), 'utf8'); - const generatedEngine = fs.readFileSync(path.join(process.cwd(), 'output', 'engine.lua'), 'utf8'); - const generatedSteamworks = fs.readFileSync(path.join(process.cwd(), 'output', 'steamworks.lua'), 'utf8'); - const generatedWorkshopFileBase = fs.readFileSync(path.join(process.cwd(), 'output', 'workshopfilebase.lua'), 'utf8'); - - const customEngineGetAddons = fs.readFileSync(path.join(customRoot, 'engine.GetAddons.lua'), 'utf8'); - const customEngineGetUserContent = fs.readFileSync(path.join(customRoot, 'engine.GetUserContent.lua'), 'utf8'); - const customSteamworksGetDownloadedItems = fs.readFileSync(path.join(customRoot, 'steamworks.GetDownloadedItems.lua'), 'utf8'); - const customSteamworksFileUserInfo = fs.readFileSync(path.join(customRoot, 'steamworks.FileUserInfo.lua'), 'utf8'); - const customSteamworksFileInfo = fs.readFileSync(path.join(customRoot, 'steamworks.FileInfo.lua'), 'utf8'); - const customWorkshopfileFillFileInfo = fs.readFileSync(path.join(customRoot, 'workshopfilebase.FillFileInfo.lua'), 'utf8'); - - expect(globals).toMatch(/---@alias GPlayer Player/); - expect(globals).toMatch(/---@class NULL : Entity/); - expect(globals).toMatch(/---@alias EntityOrNULL Entity\|NULL/); - expect(globals).toMatch(/---@type NULL/); - - expect(gm).toMatch(/---@field Name string/); - expect(gm).toMatch(/---@field TeamBased boolean/); - expect(gm).toMatch(/---@field IsSandboxDerived\? boolean/); - expect(generatedGM).toMatch(/---@field Name string/); - expect(generatedGM).toMatch(/---@field TeamBased boolean/); - expect(generatedGM).toMatch(/---@field IsSandboxDerived\? boolean/); - - expect(dCheckBoxLabel).toMatch(/---@class DCheckBoxLabel : Panel/); - expect(dCheckBoxLabel).toMatch(/---@field Button DCheckBox/); - expect(dCheckBoxLabel).toMatch(/---@field Label DLabel/); - - expect(dColorCube).toMatch(/---@field BGSaturation DImage/); - expect(dColorMixer).toMatch(/---@field Palette DColorPalette/); - expect(dColorMixer).toMatch(/---@field txtR DNumberWang/); - expect(dColorMixer).toMatch(/---@field m_bPalette\? boolean/); - expect(dColorMixer).toMatch(/---@field m_ConVarA\? string/); - expect(dComboBox).toMatch(/---@field Choices table/); - expect(dComboBox).toMatch(/---@field Menu\? DMenu/); - expect(dFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); - expect(dFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); - expect(dFileBrowser).toMatch(/---@field m_strPath string/); - expect(dFileBrowser).toMatch(/---@field m_bModels\? boolean/); - expect(dFileBrowser).toMatch(/---@field m_bOpen\? boolean/); - expect(dHtmlControls).toMatch(/---@class DHTMLControls : Panel/); - expect(dHtmlControls).toMatch(/---@field AddressBar DTextEntry/); - expect(dHtmlControls).toMatch(/---@field HTML\? DHTML/); - expect(dHorizontalScroller).toMatch(/---@field Panels Panel\[]/); - expect(dhScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); - expect(dListView).toMatch(/---@field Columns DListView_Column\[]/); - expect(dListView).toMatch(/---@field pnlCanvas Panel/); - expect(dMenuBar).toMatch(/---@field Menus table/); - expect(dMenuOption).toMatch(/---@field SubMenu\? DMenu/); - expect(dModelSelectMulti).toMatch(/---@field ModelPanels table/); - expect(dNotify).toMatch(/---@field Items table/); - - expect(dPanelList).toMatch(/---@class DPanelList : DPanel/); - expect(dPanelList).toMatch(/---@field Items Panel\[]/); - expect(dPanelList).toMatch(/---@field pnlCanvas DPanel/); - expect(dPanelSelect).toMatch(/---@field SelectedPanel\? Panel/); - expect(dProperties).toMatch(/---@field Categories table/); - expect(dTree).toMatch(/---@field RootNode DTree_Node/); - expect(dTreeNode).toMatch(/---@field ChildNodes\? DListLayout/); - expect(dvScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); - expect(dMenuAddPanel).toMatch(/---@param pnl T The panel that you want to add\./); - expect(dCheckBoxSetValue).toMatch(/---@param checked any/); - expect(dCheckBoxSetChecked).toMatch(/---@param checked any/); - expect(dCheckBoxLabelSetValue).toMatch(/---@param checked any/); - expect(dCheckBoxLabelSetChecked).toMatch(/---@param checked any/); - expect(dButtonUpdateColours).toMatch(/---@param skin SKIN/); - expect(dFileBrowserSetOpen).toMatch(/---@param open any/); - expect(dFileBrowserSetOpen).toMatch(/---@param useAnim\? boolean/); - expect(dImageSetMatName).toMatch(/---@param mat\? string/); - expect(dMenuSetOpenSubMenu).toMatch(/---@param item\? Panel/); - expect(dPropertyGenericValueChanged).toMatch(/---@param force\? boolean/); - expect(generatedDPropertyGeneric).toMatch(/---@param force\? boolean/); - expect(dSliderSetNotches).toMatch(/---@param notches\? number/); - expect(generatedDSlider).toMatch(/---@param notches\? number/); - expect(dTreeNodeChildExpanded).toMatch(/---@param expanded\? boolean/); - expect(dTreeNodePopulateChildrenAndSelf).toMatch(/---@param expand\? boolean/); - expect(dTreeNodeSetShowFiles).toMatch(/---@param showFiles\? boolean/); - expect(dTreeNodeSetWildCard).toMatch(/---@param wildcard\? string/); - expect(generatedDImage).toMatch(/---@param mat\? string/); - expect(generatedDColorCube).toMatch(/---@field BGSaturation DImage/); - expect(generatedDColorMixer).toMatch(/---@field Palette DColorPalette/); - expect(generatedDColorMixer).toMatch(/---@field m_bPalette\? boolean/); - expect(generatedDComboBox).toMatch(/---@field Choices table/); - expect(generatedDFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); - expect(generatedDFileBrowser).toMatch(/---@field m_bModels\? boolean/); - expect(generatedDFileBrowser).toMatch(/---@field m_bOpen\? boolean/); - expect(generatedDHTMLControls).toMatch(/---@field HTML\? DHTML/); - expect(generatedDHorizontalScroller).toMatch(/---@field Panels Panel\[]/); - expect(generatedDHScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); - expect(generatedDListView).toMatch(/---@field Columns DListView_Column\[]/); - expect(generatedDMenuBar).toMatch(/---@field Menus table/); - expect(generatedDMenuOption).toMatch(/---@field SubMenu\? DMenu/); - expect(generatedDModelSelectMulti).toMatch(/---@field ModelPanels table/); - expect(generatedDNotify).toMatch(/---@field Items table/); - expect(generatedDPanelList).toMatch(/---@field pnlCanvas DPanel/); - expect(generatedDPanelSelect).toMatch(/---@field SelectedPanel\? Panel/); - expect(generatedDProperties).toMatch(/---@field Categories table/); - expect(generatedDTree).toMatch(/---@field RootNode DTree_Node/); - expect(generatedDTreeNode).toMatch(/---@field ChildNodes\? DListLayout/); - expect(generatedDVScrollBar).toMatch(/---@field btnGrip DScrollBarGrip/); - expect(generatedDButton).toMatch(/---@param skin SKIN/); - expect(generatedDLabel).toMatch(/---@param skin SKIN/); - expect(generatedDMenu).toMatch(/---@param item\? Panel/); - expect(generatedDTreeNode).toMatch(/---@param expanded\? boolean/); - expect(generatedDTreeNode).toMatch(/---@param expand\? boolean/); - expect(generatedDTreeNode).toMatch(/---@param showFiles\? boolean/); - expect(generatedDTreeNode).toMatch(/---@param wildcard\? string/); - expect(panelAdd).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); - expect(panelSetSelectionCanvas).toMatch(/---@param set boolean\|Panel/); - expect(panelSetParent).toMatch(/---@param parent\? Panel/); - expect(generatedPanel).toMatch(/---@overload fun\(self: Panel, className: `T`, parent: Panel\): T/); - expect(generatedPanel).toMatch(/---@param set boolean\|Panel/); - expect(generatedPanel).toMatch(/---@param parent\? Panel/); - expect(panelGetCookie).toMatch(/---@param default\? string/); - expect(panelGetCookie).toMatch(/---@return string\|nil/); - expect(panelGetCookieNumber).toMatch(/---@param default\? number/); - expect(panelGetCookieNumber).toMatch(/---@return number\|nil/); - expect(panelSetCookie).toMatch(/---@param value\? string\|number\|boolean/); - expect(cookieSet).toMatch(/---@param value\? string\|number\|boolean/); - expect(propertyAdd).toMatch(/---@field Filter fun\(self: PropertyAddRuntime, ent: Entity, player: Player\):\(check: boolean\)/); - expect(propertyAdd).toMatch(/---@class \(partial\) PropertyAddRuntime : PropertyAdd/); - expect(propertyAdd).toMatch(/---@field \[string\] any/); - expect(propertyAdd).toMatch(/---@field MsgStart fun\(self: PropertyAddRuntime\)/); - expect(propertyAdd).toMatch(/---@field MsgEnd fun\(self: PropertyAddRuntime\)/); - expect(generatedStructures).toMatch(/---@field Filter fun\(self: PropertyAddRuntime, ent: Entity, player: Player\):\(check: boolean\)/); - expect(generatedStructures).toMatch(/---@class \(partial\) PropertyAddRuntime : PropertyAdd/); - expect(generatedStructures).toMatch(/---@field \[string\] any/); - expect(generatedStructures).toMatch(/---@field MsgStart fun\(self: PropertyAddRuntime\)/); - expect(generatedStructures).toMatch(/---@field MsgEnd fun\(self: PropertyAddRuntime\)/); - - expect(httpRequest).toMatch(/---@alias HTTPRequestMethodWithParameters/); - expect(httpRequest).toMatch(/---@class \(exact\) HTTPRequestWithParameters : HTTPRequest/); - expect(httpRequest).toMatch(/---@class \(exact\) HTTPRequestWithoutParameters : HTTPRequest/); - expect(httpRequest).toMatch(/---@field method\? string/); - expect(httpRequest).toMatch(/---@field parameters\? HTTPRequestParameters/); - expect(httpRequest).toMatch(/---@field parameters nil/); - expect(globalHttp).toMatch(/---@overload fun\(parameters: HTTPRequestWithParameters\): boolean/); - expect(globalHttp).toMatch(/---@param parameters HTTPRequest The request parameters/); - - expect(entsCreate).toMatch(/---@alias KnownEngineEntityClass/); - expect(entsCreate).toMatch(/"phys_constraint"/); - expect(entsCreate).toMatch(/"widget_bones"/); - expect(entsCreate).toMatch(/---@overload fun\(class: KnownEngineEntityClass\): Entity/); - expect(entsCreate).toMatch(/---@return \(instance\) T\|NULL/); - expect(vehicleGetDriver).toMatch(/---@return Player\|NULL driver/); - expect(getNWEntity).toMatch(/---@overload fun\(self: Entity, key: string\): Entity\|NULL/); - expect(getNW2Entity).toMatch(/---@overload fun\(self: Entity, key: string\): Entity\|NULL/); - expect(getNetworkedEntity).toMatch(/---@overload fun\(self: Entity, key: string\): Entity\|NULL/); - expect(getNetworked2Entity).toMatch(/---@overload fun\(self: Entity, key: string\): Entity\|NULL/); - - expect(dPropertySheetAddSheet).toMatch(/---@class DPropertySheetSheet/); - expect(dPropertySheetAddSheet).toMatch(/---@field Tab DTab/); - expect(dPropertySheetAddSheet).toMatch(/---@return DPropertySheetSheet/); - expect(dLabelUpdateColours).toMatch(/---@param skin SKIN/); - expect(ctrlColor).toMatch(/---@class CtrlColor : Panel/); - expect(ctrlColor).toMatch(/---@field Mixer DColorMixer/); - expect(controlPanelAddControl).toMatch(/---@overload fun\(self: ControlPanel, type: "color", controlinfo: table\): CtrlColor/); - expect(controlPanelAddControl).toMatch(/---@return Panel/); - - expect(entityCopyData).toMatch(/---@class \(partial\) EntityCopyData/); - expect(entityCopyData).toMatch(/---@field Class string/); - expect(entityCopyData).toMatch(/---@field Pos\? Vector/); - expect(entityCopyData).toMatch(/---@field Angle\? Angle/); - expect(entityCopyData).toMatch(/---@field Name\? string/); - expect(entityCopyData).toMatch(/---@field PhysicsObjects\? table/); - expect(duplicatorCreateEntityFromTable).toMatch(/---@param entTable EntityCopyData/); - - expect(osDate).toMatch(/---@param format\? string/); - expect(osDate).toMatch(/---@return string\|DateData/); - expect(tableCopy).toMatch(/---@generic T : table/); - expect(tableCopy).toMatch(/---@param originalTable T/); - expect(tableCopy).toMatch(/---@return T/); - - // ContentContainer is registered as `vgui.Register("ContentContainer", PANEL, "DScrollPanel")` - // in contentcontainer.lua, so its base class is DScrollPanel (not DIconLayout). - expect(contentContainer).toMatch(/---@class ContentContainer : DScrollPanel/); - expect(contentContainer).toMatch(/function ContentContainer:SetTriggerSpawnlistChange\(trigger\) end/); - - expect(propVehiclePrisonerPod).toMatch(/---@class prop_vehicle_prisoner_pod : Vehicle/); - expect(propRagdoll).toMatch(/---@class prop_ragdoll : Entity/); - expect(propDynamicOverride).toMatch(/---@class prop_dynamic_override : Entity/); - expect(envFire).toMatch(/---@class env_fire : Entity/); - - expect(matProxyData).toMatch(/---@field init\? fun\(self: MatProxyData, mat: IMaterial, values: table\)/); - expect(matProxyData).toMatch(/---@field bind fun\(self: MatProxyData, mat: IMaterial, ent: Entity\)/); - expect(iMaterialSetTexture).toMatch(/---@param texture ITexture\|string/); - expect(renderClearRenderTarget).toMatch(/---@param color Color/); - expect(generatedRender).toMatch(/---@param color Color The color\./); - expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); - expect(vguiRegisterFile).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); - expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.load", "include"\)\]/); - expect(generatedVgui).toMatch(/---@\[call_arg\("gmod\.vgui_panel", "register_file"\)\]/); - expect(viewData).toMatch(/---@field origin\? Vector/); - expect(viewData).toMatch(/---@field angles\? Angle/); - expect(viewData).toMatch(/---@field offcenter\? table/); - expect(generatedStructures).toMatch(/---@field origin\? Vector/); - expect(generatedStructures).toMatch(/---@field angles\? Angle/); - expect(generatedStructures).toMatch(/---@field offcenter\? table/); - expect(engineEntities).toMatch(/---@class phys_constraintsystem : Entity/); - expect(engineEntities).toMatch(/---@class gmod_winch_controller : Entity/); - expect(engineEntities).toMatch(/---@class hunter_flechette : Entity/); - expect(engineEntities).toMatch(/---@class widget_bones : Entity/); - expect(enginePanels).toMatch(/---@class \(partial\) Chromium : HTML/); - expect(enginePanels).toMatch(/---@class \(partial\) ModelImage : Panel/); - expect(enginePanels).toMatch(/---@class \(partial\) URLLabel : Label/); - expect(baseGmodEntity).toMatch(/---@class base_gmodentity : Entity/); - expect(baseGmodEntity).toMatch(/function base_gmodentity:SetPlayer\(ply\) end/); - expect(baseAi).toMatch(/---@class base_ai : NPC/); - expect(generatedCustomClasses).toMatch(/---@class base_gmodentity : Entity/); - expect(generatedCustomClasses).toMatch(/function base_gmodentity:SetPlayer\(ply\) end/); - expect(generatedCustomClasses).toMatch(/---@class base_ai : NPC/); - expect(effect).toMatch(/---@class EFFECT : Entity/); - expect(effect).toMatch(/---@field Entity Entity/); - expect(generatedEffect).toMatch(/---@class EFFECT/); - expect(generatedEffect).toMatch(/---@field Entity Entity/); - expect(generatedEffect).toMatch(/---@source https:\/\/wiki\.facepunch\.com\/gmod\/EFFECT_Hooks/); - expect(luaParticleSetColor).toMatch(/---@overload fun\(self: CLuaParticle, color: Color\)/); - expect(generatedLuaParticle).toMatch(/---@overload fun\(self: CLuaParticle, color: Color\)/); - expect(renderGroup).toMatch(/RENDERGROUP_NONE = 5/); - expect(generatedEnums).toMatch(/RENDERGROUP_NONE = 5/); - expect(skeletonConvertor).toMatch(/---@class ModelEntity/); - expect(skeletonConvertor).toMatch(/---@field GetModel fun\(self: ModelEntity\): string/); - expect(skeletonConvertor).toMatch(/---@class SkeletonConvertor/); - expect(skeletonConvertor).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); - expect(skeletonConvertor).toMatch(/---@field PrePosition\? fun\(self: SkeletonConvertor, sensor: table\)/); - expect(skeletonConvertor).toMatch(/---@field Complete\? fun\(self: SkeletonConvertor, ply: Player, sensor: table, rotation: Angle, pos: table, ang: table\)/); - expect(listSet).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); - expect(serverQueryData).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); - expect(skin).toMatch(/---@class SKINColoursProperties/); - expect(skin).toMatch(/---@field Column_Disabled Color/); - expect(skin).toMatch(/---@field Border Color/); - expect(skin).toMatch(/---@field Colours SKINColours/); - expect(generatedCustomClasses).toMatch(/---@class phys_constraintsystem : Entity/); - expect(generatedCustomClasses).toMatch(/---@class gmod_winch_controller : Entity/); - expect(generatedCustomClasses).toMatch(/---@class hunter_flechette : Entity/); - expect(generatedCustomClasses).toMatch(/---@class \(partial\) Chromium : HTML/); - expect(generatedCustomClasses).toMatch(/---@class \(partial\) ModelImage : Panel/); - expect(generatedCustomClasses).toMatch(/---@class \(partial\) URLLabel : Label/); - expect(generatedCustomClasses).toMatch(/---@class ModelEntity/); - expect(generatedCustomClasses).toMatch(/---@field IsApplicable fun\(self: SkeletonConvertor, ent: ModelEntity\): boolean/); - expect(generatedCustomClasses).toMatch(/---@field Complete\? fun\(self: SkeletonConvertor, ply: Player, sensor: table, rotation: Angle, pos: table, ang: table\)/); - expect(generatedCustomClasses).toMatch(/---@class SKINColoursProperties/); - expect(generatedCustomClasses).toMatch(/---@field Column_Disabled Color/); - expect(generatedCustomClasses).toMatch(/---@field Border Color/); - expect(generatedCustomClasses).toMatch(/---@field Colours SKINColours/); - expect(generatedList).toMatch(/---@overload fun\(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor\)/); - expect(generatedStructures).toMatch(/netversion: string, luaversion: string, localization: string, gmcategory: string/); - - expect(customEngineGetAddons).toMatch(/---@class \(partial\) EngineAddon/); - expect(customEngineGetAddons).toMatch(/---@field wsid string/); - expect(customEngineGetAddons).toMatch(/---@return EngineAddon\[]/); - expect(generatedEngine).toMatch(/---@class \(partial\) EngineAddon/); - expect(generatedEngine).toMatch(/---@field wsid string/); - - expect(customEngineGetUserContent).toMatch(/---@class \(partial\) EngineUserContent/); - expect(customEngineGetUserContent).toMatch(/---@deprecated Used internally for in-game menus\./); - expect(customEngineGetUserContent).toMatch(/---@realm menu/); - expect(customEngineGetUserContent).toMatch(/---@return EngineUserContent\[]/); - expect(generatedEngine).toMatch(/---@return EngineUserContent\[]/); - - expect(customSteamworksGetDownloadedItems).toMatch(/---@return string\[]/); - expect(generatedSteamworks).toMatch(/---@return string\[]/); - - expect(customSteamworksFileInfo).toMatch(/UGCFileInfo\?/); - expect(generatedSteamworks).toMatch(/UGCFileInfo\?/); - - expect(customSteamworksFileUserInfo).toMatch(/---@class \(partial\) SteamworksFileUserInfo/); - expect(customSteamworksFileUserInfo).toMatch(/---@field error\? number/); - expect(customSteamworksFileUserInfo).toMatch(/---@param callback fun\(info: SteamworksFileUserInfo\)/); - - expect(customWorkshopfileFillFileInfo).toMatch(/---@class \(partial\) WorkshopFileInfoResults/); - expect(customWorkshopfileFillFileInfo).toMatch(/---@param results WorkshopFileInfoResults/); - expect(generatedWorkshopFileBase).toMatch(/---@class \(partial\) WorkshopFileInfoResults/); - expect(generatedWorkshopFileBase).toMatch(/---@param results WorkshopFileInfoResults/); + test('custom overrides propagate their annotation surface to generated output', () => { + const directOutputs: Array<[string, string]> = [ + ['class.ContentSidebar.lua', 'contentsidebar.lua'], + ['class.ContextBase.lua', 'contextbase.lua'], + ['class.DColorCube.lua', 'dcolorcube.lua'], + ['class.DFrame.lua', 'dframe.lua'], + ['class.DHTMLControls.lua', 'dhtmlcontrols.lua'], + ['class.DImage.lua', 'dimage.lua'], + ['class.DImageButton.lua', 'dimagebutton.lua'], + ['class.DListView.lua', 'dlistview.lua'], + ['class.DMenu.lua', 'dmenu.lua'], + ['class.DMenuBar.lua', 'dmenubar.lua'], + ['class.DMenuOption.lua', 'dmenuoption.lua'], + ['class.EFFECT.lua', 'effect.lua'], + ['DDragBase.DropAction_Copy.lua', 'ddragbase.lua'], + ['DDragBase.DropAction_Normal.lua', 'ddragbase.lua'], + ['DDragBase.DropAction_Simple.lua', 'ddragbase.lua'], + ['DFileBrowser.SetOpen.lua', 'dfilebrowser.lua'], + ['DImage.SetMatName.lua', 'dimage.lua'], + ['DMenu.SetOpenSubMenu.lua', 'dmenu.lua'], + ['DPanelList.Clear.lua', 'dpanellist.lua'], + ['Panel.PerformLayout.lua', 'panel.lua'], + ['TOOL.BuildCPanel.lua', 'tool.lua'], + ['TOOL.Deploy.lua', 'tool.lua'], + ['TOOL.Holster.lua', 'tool.lua'], + ['class.Weapon.lua', 'weapon.lua'], + ['Weapon.GetToolObject.lua', 'weapon.lua'], + ['constraint.Elastic.lua', 'constraint.lua'], + ['constraint.Weld.lua', 'constraint.lua'], + ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], + ['Global.collectgarbage.lua', 'global.lua'], + ['Weapon.GetToolObject.lua', 'weapon.lua'], + ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], + ]; + + for (const [customFile, outputFile] of directOutputs) { + expectCustomLinesInOutput(customFile, outputFile); + } }); - test('iterator overrides expose typed generic-for values', () => { - const customRoot = path.join(process.cwd(), 'custom'); - const playerIterator = fs.readFileSync(path.join(customRoot, 'player.Iterator.lua'), 'utf8'); - const entsIterator = fs.readFileSync(path.join(customRoot, 'ents.Iterator.lua'), 'utf8'); - - expect(playerIterator).toMatch(/---@return fun\(tbl: any, prev: integer\?\): integer, Player # The iterator function\./); - expect(playerIterator).toMatch(/---@return Player\[] # Table of all existing Player/); - expect(playerIterator).toMatch(/---@return integer # The origin index \(0\)\./); - - expect(entsIterator).toMatch(/---@return fun\(tbl: any, prev: integer\?\): integer, Entity # The iterator function\./); - expect(entsIterator).toMatch(/---@return Entity\[] # Table of all existing Entity/); - expect(entsIterator).toMatch(/---@return integer # The origin index \(0\)\./); + test('custom class fragments are included in the generated custom class bundle', () => { + const customClasses = readOutput('custom_classes.lua'); + const classFiles = [ + 'class.EngineEntities.lua', + 'class.EnginePanels.lua', + 'class.SKIN.lua', + 'class.SkeletonConvertor.lua', + 'class.base_ai.lua', + 'class.base_gmodentity.lua', + 'class.env_fire.lua', + 'class.prop_dynamic_override.lua', + 'class.prop_ragdoll.lua', + 'class.prop_vehicle_prisoner_pod.lua', + ]; + + for (const customFile of classFiles) { + for (const line of significantOverrideLines(readCustom(customFile))) { + expect(customClasses).toContain(line); + } + } }); - test('verified source-backed annotation fixes are preserved', () => { - const dFileBrowser = readCustom('class.DFileBrowser.lua'); - const generatedDFileBrowser = readOutput('dfilebrowser.lua'); - const generatedCustomClasses = readOutput('custom_classes.lua'); - const dHtmlControls = readCustom('class.DHTMLControls.lua'); - const generatedDHtmlControls = readOutput('dhtmlcontrols.lua'); - const dNumPad = readCustom('class.DNumPad.lua'); - const generatedDNumPad = readOutput('dnumpad.lua'); - const spawnMenu = readCustom('class.SpawnMenu.lua'); - const weaponClass = readCustom('class.Weapon.lua'); - const generatedWeapon = readOutput('weapon.lua'); - const getToolObject = readCustom('Weapon.GetToolObject.lua'); - const toolLeftClick = readCustom('TOOL.LeftClick.lua'); - const generatedTool = readOutput('tool.lua'); - const weld = readCustom('constraint.Weld.lua'); - const elastic = readCustom('constraint.Elastic.lua'); - const generatedConstraint = readOutput('constraint.lua'); - const generatedEntity = readOutput('entity.lua'); - const generatedPanel = readOutput('panel.lua'); - const generatedDPanelList = readOutput('dpanellist.lua'); - - expect(dFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); - expect(dFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); - expect(generatedDFileBrowser).toMatch(/---@field FolderNode\? DTree_Node/); - expect(generatedDFileBrowser).toMatch(/---@field Files\? DIconBrowser\|DListView/); - expect(generatedDFileBrowser).not.toMatch(/---@field Files DListView/); - - expect(generatedCustomClasses).not.toMatch(/---@class DVScrollBar : Panel[\s\S]*?---@field btnGrip DButton/); - expect(generatedCustomClasses).not.toMatch(/---@class DHScrollBar : Panel[\s\S]*?---@field btnGrip DButton/); - expect(generatedCustomClasses).not.toMatch(/---@class DVScrollBar : Panel/); - expect(generatedCustomClasses).not.toMatch(/---@class DHScrollBar : Panel/); - - expect(dHtmlControls).toMatch(/---@field RefreshButton DImageButton/); - expect(dHtmlControls).toMatch(/---@field HomeURL string/); - expect(dHtmlControls).toMatch(/---@field HTML\? DHTML/); - expect(dHtmlControls).not.toMatch(/ReloadButton|HomeUrl|---@field HTML DHTML/); - expect(generatedDHtmlControls).toMatch(/---@field RefreshButton DImageButton/); - expect(generatedDHtmlControls).toMatch(/---@field HomeURL string/); - expect(generatedDHtmlControls).toMatch(/---@field HTML\? DHTML/); - expect(generatedDHtmlControls).not.toMatch(/ReloadButton|HomeUrl|---@field HTML DHTML/); - - expect(dNumPad).toMatch(/---@field m_bButtonSize number/); - expect(generatedDNumPad).toMatch(/---@field m_bButtonSize number/); - - expect(spawnMenu).toMatch(/---@field CustomizableSpawnlistNode\? DTree_Node/); - expect(spawnMenu).toMatch(/---@field SearchPropPanel\? ContentContainer/); - expect(spawnMenu).not.toMatch(/CustomizableSpawnlistNode\? any|SearchPropPanel\? Panel/); - expect(generatedCustomClasses).toMatch(/---@field CustomizableSpawnlistNode\? DTree_Node/); - expect(generatedCustomClasses).toMatch(/---@field SearchPropPanel\? ContentContainer/); - expect(generatedCustomClasses).not.toMatch(/CustomizableSpawnlistNode\? any|SearchPropPanel\? Panel/); - - expect(weaponClass).toMatch(/---@return Entity\|Player\|NPC\|NULL/); - expect(generatedWeapon).toMatch(/---@return Entity\|Player\|NPC\|NULL/); - expect(generatedWeapon).not.toMatch(/---@return Player # The player who owns this weapon\./); - - expect(getToolObject).toMatch(/---@class gmod_tool : Weapon/); - expect(getToolObject).toMatch(/---@return Tool\|false/); - expect(getToolObject).not.toMatch(/function Weapon:GetToolObject/); - expect(generatedWeapon).toMatch(/function gmod_tool:GetToolObject\(tool\) end/); - expect(generatedWeapon).not.toMatch(/function Weapon:GetToolObject\(tool\) end/); - expect(generatedWeapon).toMatch(/---@return Tool\|false/); - - expect(toolLeftClick).not.toMatch(/fromRight/); - expect(generatedTool).not.toMatch(/fromRight/); - expect(generatedTool).toMatch(/---@param skip\? boolean/); - expect(generatedTool).toMatch(/function Tool:Deploy\(skip\) end/); - expect(generatedTool).toMatch(/function Tool:Holster\(skip\) end/); - expect(generatedTool.match(/function Tool:Deploy/g)).toHaveLength(1); - expect(generatedTool.match(/function Tool:Holster/g)).toHaveLength(1); - - expect(weld).toMatch(/---@return Entity\|false/); - expect(generatedConstraint).toMatch(/---@return Entity\|false # The created constraint entity/); - expect(elastic).toMatch(/---@return Entity\|false\|nil/); - expect(elastic).toMatch(/---@return Entity\? # The created rope/); - expect(generatedConstraint).toMatch(/---@return Entity\|false\|nil # The created constraint/); - expect(generatedConstraint).toMatch(/---@return Entity\? # The created rope/); + test('networked getter overrides keep generic fallback defaults encoded', () => { + const entityLua = readOutput('entity.lua'); + const getterFiles = fs + .readdirSync(customRoot) + .filter((file) => /^Entity\.Get(?:NW|NW2|Networked|Networked2).*\.(?:lua)$/.test(file)); + + expect(getterFiles.length).toBeGreaterThan(0); + + for (const file of getterFiles) { + const custom = readCustom(file); + const getterName = file.match(/^Entity\.(.+)\.lua$/)?.[1]; + const fallbackLine = custom + .split(/\r?\n/) + .find((line) => line.startsWith('---@param fallback? T')); + const outputBlock = entityLua.match(new RegExp(`---@source https://wiki\\.facepunch\\.com/gmod/Entity:${getterName}[\\s\\S]*?function Entity:${getterName}\\(key, fallback\\) end`))?.[0]; + + expect(getterName).toBeDefined(); + expect(fallbackLine).toBeDefined(); + expect(outputBlock).toBeDefined(); + expect(fallbackLine).toMatch(/^---@param fallback\? T=/); + expect(fallbackLine).not.toMatch(/Defaults to/); + expect(entityLua).toContain(fallbackLine); + expect(outputBlock).not.toContain('---@return any'); + } + + expect(entityLua).not.toMatch(/---@param fallback\? (?:Entity|number|string|boolean|Vector|Angle)\b/); + expect(entityLua).not.toMatch(/---@param fallback\? T .*Defaults to/); + }); - expect(generatedEntity).toMatch(/---@param delta\? number/); - expect(generatedEntity).toMatch(/function Entity:FrameAdvance\(delta\) end/); - expect(generatedPanel).toMatch(/---@param width\? number/); - expect(generatedPanel).toMatch(/---@param height\? number/); - expect(generatedPanel).toMatch(/function Panel:PerformLayout\(width, height\) end/); - expect(generatedPanel.match(/function Panel:PerformLayout/g)).toHaveLength(1); - expect(generatedDPanelList).toMatch(/---@param remove\? boolean/); - expect(generatedDPanelList).toMatch(/function DPanelList:Clear\(remove\) end/); + test('menu-only custom overrides stay absent', () => { + const removedFiles = [ + 'UGCPublishWindow.DoPublish.lua', + 'Global.IsHostingGame.lua', + 'steamworks.SetFavorite.lua', + 'workshopfilebase.dupes.lua', + ]; + + for (const file of removedFiles) { + expect(fs.existsSync(path.join(customRoot, file))).toBe(false); + } + + const workshopFileBaseOutput = readOutput('workshopfilebase.lua'); + expect(workshopFileBaseOutput).not.toContain('DupeWorkshopFileBase'); + expect(workshopFileBaseOutput).not.toContain('ws_dupe'); }); + test('global aliases and key wrapper annotations remain available', () => { + const globals = readCustom('_globals.lua'); + const generatedVgui = readOutput('vgui.lua'); + const generatedEnums = readOutput('enums.lua'); + const generatedList = readOutput('list.lua'); + + expect(globals).toContain('---@alias GPlayer Player'); + expect(globals).toContain('---@class NULL : Entity'); + expect(globals).toContain('---@alias EntityOrNULL Entity|NULL'); + expect(globals).toContain('---@type NULL'); + expect(generatedVgui).toContain('---@[call_arg("gmod.load", "include")]'); + expect(generatedVgui).toContain('---@[call_arg("gmod.vgui_panel", "register_file")]'); + expect(generatedEnums).toContain('RENDERGROUP_NONE = 5'); + expect(generatedList).toContain('---@overload fun(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor)'); + }); }); diff --git a/custom/Entity.GetNW2Angle.lua b/custom/Entity.GetNW2Angle.lua index e22ea50c..48c80d42 100644 --- a/custom/Entity.GetNW2Angle.lua +++ b/custom/Entity.GetNW2Angle.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Angle # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Angle( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Angle|T # The value associated with the key function Entity:GetNW2Angle(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNW2Bool.lua b/custom/Entity.GetNW2Bool.lua index 3bee471b..99320039 100644 --- a/custom/Entity.GetNW2Bool.lua +++ b/custom/Entity.GetNW2Bool.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): boolean # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=false The value to return if we failed to retrieve the value. ---@return boolean|T # The value associated with the key function Entity:GetNW2Bool(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNW2Entity.lua b/custom/Entity.GetNW2Entity.lua index 2b2bea14..455fb762 100644 --- a/custom/Entity.GetNW2Entity.lua +++ b/custom/Entity.GetNW2Entity.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Entity|NULL # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=NULL The value to return if we failed to retrieve the value. ---@return Entity|T # The value associated with the key function Entity:GetNW2Entity(key, fallback) end diff --git a/custom/Entity.GetNW2Float.lua b/custom/Entity.GetNW2Float.lua index 18074f3b..2701a8f4 100644 --- a/custom/Entity.GetNW2Float.lua +++ b/custom/Entity.GetNW2Float.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key function Entity:GetNW2Float(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNW2Int.lua b/custom/Entity.GetNW2Int.lua index 939929b4..b33bd10c 100644 --- a/custom/Entity.GetNW2Int.lua +++ b/custom/Entity.GetNW2Int.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key function Entity:GetNW2Int(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNW2String.lua b/custom/Entity.GetNW2String.lua index de88e7be..9c82b734 100644 --- a/custom/Entity.GetNW2String.lua +++ b/custom/Entity.GetNW2String.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): string # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T="" The value to return if we failed to retrieve the value. ---@return string|T # The value associated with the key function Entity:GetNW2String(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNW2Vector.lua b/custom/Entity.GetNW2Vector.lua index efc98c9e..cd20edad 100644 --- a/custom/Entity.GetNW2Vector.lua +++ b/custom/Entity.GetNW2Vector.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Vector # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Vector( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Vector|T # The value associated with the key function Entity:GetNW2Vector(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWAngle.lua b/custom/Entity.GetNWAngle.lua index e7278379..63bc9be7 100644 --- a/custom/Entity.GetNWAngle.lua +++ b/custom/Entity.GetNWAngle.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Angle # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Angle( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Angle|T # The value associated with the key function Entity:GetNWAngle(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWBool.lua b/custom/Entity.GetNWBool.lua index 029e5606..217a90c4 100644 --- a/custom/Entity.GetNWBool.lua +++ b/custom/Entity.GetNWBool.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): boolean # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=false The value to return if we failed to retrieve the value. ---@return boolean|T # The value associated with the key function Entity:GetNWBool(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWEntity.lua b/custom/Entity.GetNWEntity.lua index 71a1f981..d1b2eae0 100644 --- a/custom/Entity.GetNWEntity.lua +++ b/custom/Entity.GetNWEntity.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Entity|NULL # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=NULL The value to return if we failed to retrieve the value. ---@return Entity|T # The value associated with the key function Entity:GetNWEntity(key, fallback) end diff --git a/custom/Entity.GetNWFloat.lua b/custom/Entity.GetNWFloat.lua index 57f4437f..f52ad012 100644 --- a/custom/Entity.GetNWFloat.lua +++ b/custom/Entity.GetNWFloat.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key function Entity:GetNWFloat(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWInt.lua b/custom/Entity.GetNWInt.lua index 17b4bde8..42ff08e4 100644 --- a/custom/Entity.GetNWInt.lua +++ b/custom/Entity.GetNWInt.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key function Entity:GetNWInt(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWString.lua b/custom/Entity.GetNWString.lua index ee2a3698..c928bfe5 100644 --- a/custom/Entity.GetNWString.lua +++ b/custom/Entity.GetNWString.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): string # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T="" The value to return if we failed to retrieve the value. ---@return string|T # The value associated with the key function Entity:GetNWString(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNWVector.lua b/custom/Entity.GetNWVector.lua index 427a995e..ed9cdf97 100644 --- a/custom/Entity.GetNWVector.lua +++ b/custom/Entity.GetNWVector.lua @@ -4,6 +4,6 @@ ---@generic T ---@overload fun(self: Entity, key: string): Vector # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Vector( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Vector|T # The value associated with the key function Entity:GetNWVector(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2Angle.lua b/custom/Entity.GetNetworked2Angle.lua index ab415291..e68dab01 100644 --- a/custom/Entity.GetNetworked2Angle.lua +++ b/custom/Entity.GetNetworked2Angle.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Angle # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Angle( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Angle|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Angle instead. function Entity:GetNetworked2Angle(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2Bool.lua b/custom/Entity.GetNetworked2Bool.lua index e3596303..c06e572e 100644 --- a/custom/Entity.GetNetworked2Bool.lua +++ b/custom/Entity.GetNetworked2Bool.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): boolean # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=false The value to return if we failed to retrieve the value. ---@return boolean|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Bool instead. function Entity:GetNetworked2Bool(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2Entity.lua b/custom/Entity.GetNetworked2Entity.lua index be0df166..cd56efba 100644 --- a/custom/Entity.GetNetworked2Entity.lua +++ b/custom/Entity.GetNetworked2Entity.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Entity|NULL # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=NULL The value to return if we failed to retrieve the value. ---@return Entity|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Entity instead. function Entity:GetNetworked2Entity(key, fallback) end diff --git a/custom/Entity.GetNetworked2Float.lua b/custom/Entity.GetNetworked2Float.lua index df47d5f5..49b02997 100644 --- a/custom/Entity.GetNetworked2Float.lua +++ b/custom/Entity.GetNetworked2Float.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Float instead. function Entity:GetNetworked2Float(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2Int.lua b/custom/Entity.GetNetworked2Int.lua index 6ef80cb7..3c6887dc 100644 --- a/custom/Entity.GetNetworked2Int.lua +++ b/custom/Entity.GetNetworked2Int.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=0 The value to return if we failed to retrieve the value. ---@return number|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Int instead. function Entity:GetNetworked2Int(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2String.lua b/custom/Entity.GetNetworked2String.lua index 716dc205..b0d41ac2 100644 --- a/custom/Entity.GetNetworked2String.lua +++ b/custom/Entity.GetNetworked2String.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): string # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T="" The value to return if we failed to retrieve the value. ---@return string|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2String instead. function Entity:GetNetworked2String(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworked2Vector.lua b/custom/Entity.GetNetworked2Vector.lua index d15c84f5..d637e7a3 100644 --- a/custom/Entity.GetNetworked2Vector.lua +++ b/custom/Entity.GetNetworked2Vector.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Vector # The value associated with the key ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. +---@param fallback? T=Vector( 0, 0, 0 ) The value to return if we failed to retrieve the value. ---@return Vector|T # The value associated with the key ---@deprecated You should be using Entity:GetNW2Vector instead. function Entity:GetNetworked2Vector(key, fallback) end \ No newline at end of file diff --git a/custom/Entity.GetNetworkedAngle.lua b/custom/Entity.GetNetworkedAngle.lua index c0ddb9aa..e88e8e29 100644 --- a/custom/Entity.GetNetworkedAngle.lua +++ b/custom/Entity.GetNetworkedAngle.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Angle # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=Angle( 0, 0, 0 ) The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return Angle|T # The retrieved value ---@deprecated You should use Entity:GetNWAngle instead. function Entity:GetNetworkedAngle(key, fallback) end diff --git a/custom/Entity.GetNetworkedBool.lua b/custom/Entity.GetNetworkedBool.lua index 033f9ae7..d27e2307 100644 --- a/custom/Entity.GetNetworkedBool.lua +++ b/custom/Entity.GetNetworkedBool.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): boolean # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=false The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return boolean|T # The retrieved value ---@deprecated You should use Entity:GetNWBool instead. function Entity:GetNetworkedBool(key, fallback) end diff --git a/custom/Entity.GetNetworkedEntity.lua b/custom/Entity.GetNetworkedEntity.lua index 6fb0c1e4..5103fcd2 100644 --- a/custom/Entity.GetNetworkedEntity.lua +++ b/custom/Entity.GetNetworkedEntity.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Entity|NULL # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=NULL The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return Entity|T # The retrieved value ---@deprecated You should use Entity:GetNWEntity instead. function Entity:GetNetworkedEntity(key, fallback) end diff --git a/custom/Entity.GetNetworkedFloat.lua b/custom/Entity.GetNetworkedFloat.lua index 1d56ff4b..917d2c12 100644 --- a/custom/Entity.GetNetworkedFloat.lua +++ b/custom/Entity.GetNetworkedFloat.lua @@ -6,7 +6,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=0 The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return number|T # The retrieved value ---@deprecated You should use Entity:GetNWFloat instead. function Entity:GetNetworkedFloat(key, fallback) end diff --git a/custom/Entity.GetNetworkedInt.lua b/custom/Entity.GetNetworkedInt.lua index caf9d6e3..8474ebd2 100644 --- a/custom/Entity.GetNetworkedInt.lua +++ b/custom/Entity.GetNetworkedInt.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): number # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=0 The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return number|T # The retrieved value ---@deprecated You should use Entity:GetNWInt instead. function Entity:GetNetworkedInt(key, fallback) end diff --git a/custom/Entity.GetNetworkedString.lua b/custom/Entity.GetNetworkedString.lua index b5aa0aa2..1a2d6412 100644 --- a/custom/Entity.GetNetworkedString.lua +++ b/custom/Entity.GetNetworkedString.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): string # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T="" The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return string|T # The retrieved value ---@deprecated You should use Entity:GetNWString instead. function Entity:GetNetworkedString(key, fallback) end diff --git a/custom/Entity.GetNetworkedVector.lua b/custom/Entity.GetNetworkedVector.lua index cf739bbc..5263c583 100644 --- a/custom/Entity.GetNetworkedVector.lua +++ b/custom/Entity.GetNetworkedVector.lua @@ -4,7 +4,7 @@ ---@generic T ---@overload fun(self: Entity, key: string): Vector # The retrieved value ---@param key string The key that is associated with the value ----@param fallback T The value to return if we failed to retrieve the value. ( If it isn't set ) +---@param fallback? T=Vector( 0, 0, 0 ) The value to return if we failed to retrieve the value. ( If it isn't set ). ---@return Vector|T # The retrieved value ---@deprecated You should use Entity:GetNWVector instead. function Entity:GetNetworkedVector(key, fallback) end diff --git a/custom/Global.IsHostingGame.lua b/custom/Global.IsHostingGame.lua deleted file mode 100644 index 16decdb6..00000000 --- a/custom/Global.IsHostingGame.lua +++ /dev/null @@ -1,4 +0,0 @@ ----Returns true when the current menu session is hosting a local game. ----@realm menu ----@return boolean #True if the local client is hosting the active game session. -function _G.IsHostingGame() end diff --git a/custom/Global.collectgarbage.lua b/custom/Global.collectgarbage.lua index d9895fc6..16e8ce20 100644 --- a/custom/Global.collectgarbage.lua +++ b/custom/Global.collectgarbage.lua @@ -17,7 +17,7 @@ ---@overload fun(action: "setpause", arg?: integer): integer # Previous value for GC pause. ---@overload fun(action: "setstepmul", arg?: integer): integer # Previous value for GC step multiplier. ---@overload fun(action: "isrunning"): boolean # Whether the collector is currently running (x86-64 only). ----@param action? gmod.collectgarbage_action The action to run. Defaults to "collect" when omitted. +---@param action? gmod.collectgarbage_action="collect" The action to run when omitted. ---@param arg? integer The argument for "step", "setpause" and "setstepmul". ---@return any # Return type depends on the selected action. function _G.collectgarbage(action, arg) end diff --git a/custom/UGCPublishWindow.DoPublish.lua b/custom/UGCPublishWindow.DoPublish.lua deleted file mode 100644 index 18540ee6..00000000 --- a/custom/UGCPublishWindow.DoPublish.lua +++ /dev/null @@ -1,4 +0,0 @@ ----Publishes the Item or throws an error if the Title or Tags are invalid ----@realm menu ----@source https://wiki.facepunch.com/gmod/UGCPublishWindow:DoPublish -function UGCPublishWindow:DoPublish() end diff --git a/custom/class.ContentSidebar.lua b/custom/class.ContentSidebar.lua index 0bfe498f..04578c28 100644 --- a/custom/class.ContentSidebar.lua +++ b/custom/class.ContentSidebar.lua @@ -6,7 +6,7 @@ local ContentSidebar = {} ---Enables search functionality on this sidebar. ---@param stype? string The search type identifier passed to the search panel. ----@param hookname? string The hook name to populate content. Defaults to "PopulateContent". +---@param hookname? string="PopulateContent" The hook name to populate content. function ContentSidebar:EnableSearch(stype, hookname) end ---Creates and attaches the save/revert notification bar. diff --git a/custom/class.ContextBase.lua b/custom/class.ContextBase.lua index c1534baa..8c84c4b6 100644 --- a/custom/class.ContextBase.lua +++ b/custom/class.ContextBase.lua @@ -1,3 +1,5 @@ ---@class (partial) ContextBase : Panel ---@field Label DLabel The label panel created by the shared Sandbox context control base. +---@field ConVarValue? string +---@field NextPoll? number local ContextBase = {} diff --git a/custom/class.DColorCube.lua b/custom/class.DColorCube.lua index a00c7977..fc6cb6c3 100644 --- a/custom/class.DColorCube.lua +++ b/custom/class.DColorCube.lua @@ -2,4 +2,7 @@ ---@field BGSaturation DImage ---@field BGValue DImage ---@field m_BaseRGB Color +---@field m_Hue number +---@field m_OutRGB Color +---@field m_DefaultColor Color local DColorCube = {} diff --git a/custom/class.DFrame.lua b/custom/class.DFrame.lua index bc693a63..4fab9e93 100644 --- a/custom/class.DFrame.lua +++ b/custom/class.DFrame.lua @@ -5,4 +5,14 @@ ---@field btnMinim DButton The minimize button in the title bar (disabled by default). ---@field lblTitle DLabel The title label in the title bar. ---@field imgIcon DImage|nil The icon image in the title bar, if set via DFrame:SetIcon. +---@field m_bIsMenuComponent boolean +---@field m_bDraggable boolean +---@field m_bSizable boolean +---@field m_bScreenLock boolean +---@field m_bDeleteOnClose boolean +---@field m_bPaintShadow boolean +---@field m_iMinWidth number +---@field m_iMinHeight number +---@field m_bBackgroundBlur boolean +---@field m_fCreateTime number local DFrame = {} diff --git a/custom/class.DHTMLControls.lua b/custom/class.DHTMLControls.lua index 4e160e86..17cbb9fe 100644 --- a/custom/class.DHTMLControls.lua +++ b/custom/class.DHTMLControls.lua @@ -16,7 +16,10 @@ --- The current navigation history position. ---@field Cur number --- Whether we are currently navigating via history buttons. ----@field Navigating boolean +---@field Navigating? boolean --- The home URL to navigate to. ---@field HomeURL string +---@field History table +---@field BorderSize number +---@field BackgroundColor Color local DHTMLControls = {} diff --git a/custom/class.DImage.lua b/custom/class.DImage.lua index f035e02a..54e5cdcb 100644 --- a/custom/class.DImage.lua +++ b/custom/class.DImage.lua @@ -1,4 +1,10 @@ ---@class (partial) DImage : DPanel ---@field m_Material IMaterial The material currently drawn by the image panel. ---@field m_Color Color The image color override. +---@field m_bKeepAspect boolean +---@field m_strMatName? string +---@field m_strMatNameFailsafe? string +---@field ImageName string +---@field ActualWidth number +---@field ActualHeight number local DImage = {} diff --git a/custom/class.DImageButton.lua b/custom/class.DImageButton.lua index 022b9967..bc220b47 100644 --- a/custom/class.DImageButton.lua +++ b/custom/class.DImageButton.lua @@ -1,3 +1,7 @@ ---@class DImageButton : DButton ---@field m_Image DImage The internal DImage panel used to render the image. +---@field m_bStretchToFit boolean +---@field m_bDepressImage boolean +---@field ImageColor Color +---@field m_bImageDepressed? boolean local DImageButton = {} diff --git a/custom/class.DListView.lua b/custom/class.DListView.lua index 522139f2..42b8cb05 100644 --- a/custom/class.DListView.lua +++ b/custom/class.DListView.lua @@ -4,4 +4,10 @@ ---@field Sorted DListView_Line[] Lines sorted by the current column/order. ---@field pnlCanvas Panel ---@field VBar? DVScrollBar +---@field m_bDirty boolean +---@field m_bSortable boolean +---@field m_iHeaderHeight number +---@field m_iDataHeight number +---@field m_bMultiSelect boolean +---@field m_bHideHeaders boolean local DListView = {} diff --git a/custom/class.DMenu.lua b/custom/class.DMenu.lua new file mode 100644 index 00000000..4796bf97 --- /dev/null +++ b/custom/class.DMenu.lua @@ -0,0 +1,3 @@ +---@class DMenu : DScrollPanel +---@field m_pOpenSubMenu? Panel +local DMenu = {} diff --git a/custom/class.DMenuBar.lua b/custom/class.DMenuBar.lua index e11a150c..c1c34ddc 100644 --- a/custom/class.DMenuBar.lua +++ b/custom/class.DMenuBar.lua @@ -1,3 +1,5 @@ ---@class DMenuBar : DPanel ---@field Menus table +---@field m_bBackground boolean +---@field m_bIsMenuComponent boolean local DMenuBar = {} diff --git a/custom/class.DMenuOption.lua b/custom/class.DMenuOption.lua index c76bb6ba..7f8bbeab 100644 --- a/custom/class.DMenuOption.lua +++ b/custom/class.DMenuOption.lua @@ -2,4 +2,8 @@ ---@field SubMenu? DMenu ---@field SubMenuArrow? Panel ---@field m_MenuClicking? boolean +---@field m_pMenu? DMenu +---@field m_bChecked? boolean +---@field m_bCheckable? boolean +---@field m_bRadio? boolean local DMenuOption = {} diff --git a/custom/steamworks.SetFavorite.lua b/custom/steamworks.SetFavorite.lua deleted file mode 100644 index c4eca98c..00000000 --- a/custom/steamworks.SetFavorite.lua +++ /dev/null @@ -1,7 +0,0 @@ ----Sets or clears a Steam Workshop item's favorite state. ---- ----**INTERNAL**: This is used internally by the menu HTML bridge. ----@realm menu ----@param workshopItemID string|number The ID of the Steam Workshop item. ----@param favorite boolean Whether the item should be favorited. -function steamworks.SetFavorite(workshopItemID, favorite) end diff --git a/custom/workshopfilebase.dupes.lua b/custom/workshopfilebase.dupes.lua deleted file mode 100644 index b3aea346..00000000 --- a/custom/workshopfilebase.dupes.lua +++ /dev/null @@ -1,56 +0,0 @@ ----@class (partial) DupeWorkshopFileBase : WorkshopFileBase ----@field DownloadAndArm fun(wsid: string|number) Downloads and arms a subscribed dupe from the workshop. ----@field Arm fun(filename: string) Arms a local dupe file for placement. -local DupeWorkshopFileBase = {} - ----@class ws_dupe : DupeWorkshopFileBase ----Sandbox dupes workshop helper used by the menu HTML bridge. [(View Source)](https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L11) -ws_dupe = {} - ----Downloads and arms a subscribed dupe from the workshop. ---- ----**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 ----@param wsid string|number The workshop item ID. -function DupeWorkshopFileBase.DownloadAndArm(wsid) end - ----Downloads and arms a subscribed dupe from the workshop. ---- ----**INTERNAL**: This method is source-backed on the sandbox `ws_dupe` workshop helper. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 ----@param wsid string|number The workshop item ID. -function WorkshopFileBase:DownloadAndArm(wsid) end - ----Arms a local dupe file for placement. ---- ----**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 ----@param filename string The dupe file path. -function DupeWorkshopFileBase.Arm(filename) end - ----Arms a local dupe file for placement. ---- ----**INTERNAL**: This method is source-backed on the sandbox `ws_dupe` workshop helper. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 ----@param filename string The dupe file path. -function WorkshopFileBase:Arm(filename) end - ----Downloads and arms a subscribed dupe from the workshop. ---- ----**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L50 ----@param wsid string|number The workshop item ID. -function ws_dupe:DownloadAndArm(wsid) end - ----Arms a local dupe file for placement. ---- ----**INTERNAL**: This is used internally by the sandbox spawnmenu dupes UI. ----@realm menu ----@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L63 ----@param filename string The dupe file path. -function ws_dupe:Arm(filename) end From 7f7bdba5156c5640ecf5a418435b50b45c9a5c23 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 2 Jul 2026 11:14:05 +0100 Subject: [PATCH 089/117] Add DPanelList and DPanelSelect annotations --- custom/class.DPanelList.lua | 6 ++++++ custom/class.DPanelSelect.lua | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/custom/class.DPanelList.lua b/custom/class.DPanelList.lua index c563eb04..b7caa9e3 100644 --- a/custom/class.DPanelList.lua +++ b/custom/class.DPanelList.lua @@ -8,3 +8,9 @@ ---@field Horizontal boolean ---@field VBar? DVScrollBar local DPanelList = {} + +---Enables horizontal layout for child panels in this list. +---@realm client +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/vgui/dpanellist.lua +---@param horizontal boolean Whether child panels should be laid out horizontally. +function DPanelList:EnableHorizontal(horizontal) end diff --git a/custom/class.DPanelSelect.lua b/custom/class.DPanelSelect.lua index 6d043a64..10e842c6 100644 --- a/custom/class.DPanelSelect.lua +++ b/custom/class.DPanelSelect.lua @@ -8,3 +8,9 @@ local DPanelSelect = {} ---@param panel Panel The panel to add. ---@param convars? table ConVar values associated with the panel. function DPanelSelect:AddPanel(panel, convars) end + +---Selects a panel and applies its associated ConVar values. +---@realm client +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/vgui/dpanelselect.lua +---@param panel Panel The panel to select. +function DPanelSelect:SelectPanel(panel) end From 5fe8ea0e60ca70c233bfea978b09afe2f096fbc3 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 3 Jul 2026 08:09:30 +0100 Subject: [PATCH 090/117] Add side-effect-free color annotations --- custom/Global.Color.lua | 1 + custom/Global.ColorAlpha.lua | 1 + custom/Global.HSLToColor.lua | 7 +++++++ custom/Global.HSVToColor.lua | 7 +++++++ custom/Global.IsColor.lua | 1 + 5 files changed, 17 insertions(+) create mode 100644 custom/Global.HSLToColor.lua create mode 100644 custom/Global.HSVToColor.lua diff --git a/custom/Global.Color.lua b/custom/Global.Color.lua index b82b777f..d186fc7a 100644 --- a/custom/Global.Color.lua +++ b/custom/Global.Color.lua @@ -11,4 +11,5 @@ ---@[call_arg("gmod.color", "a")] ---@param a? number The alpha channel, from 0 to 255. ---@return Color +---@[side_effect_free] function _G.Color(r, g, b, a) end diff --git a/custom/Global.ColorAlpha.lua b/custom/Global.ColorAlpha.lua index 42b68ead..d5b4cb0f 100644 --- a/custom/Global.ColorAlpha.lua +++ b/custom/Global.ColorAlpha.lua @@ -5,4 +5,5 @@ ---@param color Color The Color from which to take RGB values. This color will not be modified. ---@param alpha number The new alpha value, a number between 0 and 255. Values above 255 will be clamped. ---@return Color # The new Color with the modified alpha value +---@[side_effect_free] function _G.ColorAlpha(color, alpha) end diff --git a/custom/Global.HSLToColor.lua b/custom/Global.HSLToColor.lua new file mode 100644 index 00000000..e262e1df --- /dev/null +++ b/custom/Global.HSLToColor.lua @@ -0,0 +1,7 @@ +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L76-L105 +---@return Color +---@[side_effect_free] +---@param h number +---@param s number +---@param l number +function _G.HSLToColor(h, s, l) end diff --git a/custom/Global.HSVToColor.lua b/custom/Global.HSVToColor.lua new file mode 100644 index 00000000..2bc4bca3 --- /dev/null +++ b/custom/Global.HSVToColor.lua @@ -0,0 +1,7 @@ +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L45-L74 +---@return Color +---@[side_effect_free] +---@param h number +---@param s number +---@param v number +function _G.HSVToColor(h, s, v) end diff --git a/custom/Global.IsColor.lua b/custom/Global.IsColor.lua index 31400a5f..eea802ad 100644 --- a/custom/Global.IsColor.lua +++ b/custom/Global.IsColor.lua @@ -4,4 +4,5 @@ ---@source https://wiki.facepunch.com/gmod/Global.IsColor ---@param var any ---@return TypeGuard isColor # Whether the value is a Color. +---@[side_effect_free] function _G.IsColor(var) end From 2154e0b24f7335bc79cf484ad6f1cbab02b07771 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 3 Jul 2026 08:39:39 +0100 Subject: [PATCH 091/117] Add side-effect-free color and math annotations --- custom/Global.ColorToHSL.lua | 10 ++++++++++ custom/Global.ColorToHSV.lua | 10 ++++++++++ custom/math.max.lua | 8 ++++++++ custom/math.min.lua | 8 ++++++++ 4 files changed, 36 insertions(+) create mode 100644 custom/Global.ColorToHSL.lua create mode 100644 custom/Global.ColorToHSV.lua create mode 100644 custom/math.max.lua create mode 100644 custom/math.min.lua diff --git a/custom/Global.ColorToHSL.lua b/custom/Global.ColorToHSL.lua new file mode 100644 index 00000000..8dd617dc --- /dev/null +++ b/custom/Global.ColorToHSL.lua @@ -0,0 +1,10 @@ +---Converts a Color into HSL color space. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.ColorToHSL +---@[side_effect_free] +---@param color Color The Color. +---@return number # The hue in degrees [0, 360]. +---@return number # The saturation in the range [0, 1]. +---@return number # The lightness in the range [0, 1]. +function _G.ColorToHSL(color) end diff --git a/custom/Global.ColorToHSV.lua b/custom/Global.ColorToHSV.lua new file mode 100644 index 00000000..db798194 --- /dev/null +++ b/custom/Global.ColorToHSV.lua @@ -0,0 +1,10 @@ +---Converts a Color into HSV color space. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.ColorToHSV +---@[side_effect_free] +---@param color Color The Color. +---@return number # The hue in degrees [0, 360]. +---@return number # The saturation in the range [0, 1]. +---@return number # The value in the range [0, 1]. +function _G.ColorToHSV(color) end diff --git a/custom/math.max.lua b/custom/math.max.lua new file mode 100644 index 00000000..2172b406 --- /dev/null +++ b/custom/math.max.lua @@ -0,0 +1,8 @@ +---Returns the largest value of all arguments. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/math.max +---@[side_effect_free] +---@param ... number Numbers to get the largest from. +---@return number # The largest number. +function math.max(...) end diff --git a/custom/math.min.lua b/custom/math.min.lua new file mode 100644 index 00000000..14d2c65c --- /dev/null +++ b/custom/math.min.lua @@ -0,0 +1,8 @@ +---Returns the smallest value of all arguments. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/math.min +---@[side_effect_free] +---@param ... number Numbers to get the smallest from. +---@return number # The smallest number. +function math.min(...) end From 9dada2ba3dfd3dae5ea89f028b957b8b72bd5fbf Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 3 Jul 2026 11:04:49 +0100 Subject: [PATCH 092/117] Add ConVar write annotations --- custom/ConVar.GetString.lua | 7 +++++++ custom/Global.GetConVar.lua | 1 + 2 files changed, 8 insertions(+) create mode 100644 custom/ConVar.GetString.lua diff --git a/custom/ConVar.GetString.lua b/custom/ConVar.GetString.lua new file mode 100644 index 00000000..86baa641 --- /dev/null +++ b/custom/ConVar.GetString.lua @@ -0,0 +1,7 @@ +---Returns the current [ConVar](https://wiki.facepunch.com/gmod/ConVar) value as a string. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/ConVar:GetString +---@[side_effect_free] +---@return string # The current console variable value as a string. +function ConVar:GetString() end diff --git a/custom/Global.GetConVar.lua b/custom/Global.GetConVar.lua index c4713100..0dcd3018 100644 --- a/custom/Global.GetConVar.lua +++ b/custom/Global.GetConVar.lua @@ -5,6 +5,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.GetConVar ---@[call_arg("gmod.convar", "reference")] +---@[writes_global("ConVarCache")] ---@param name string Name of the ConVar to get ---@return ConVar? # The ConVar object, or nil if no such ConVar was found. function _G.GetConVar( name ) end From 1e814746c0f9bd92234c4fa37fc41c9792ab3bfb Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 4 Jul 2026 02:44:00 +0100 Subject: [PATCH 093/117] Fix global IsValid guard return type --- __tests__/custom-annotations.spec.ts | 30 ++++++++++++++++++++++++++++ custom/Global.IsEntity.legacy..lua | 8 ++++++++ custom/Global.IsValid.lua | 7 ++++--- 3 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 custom/Global.IsEntity.legacy..lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 9c27486e..7a1c8bd0 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -164,4 +164,34 @@ describe('custom and plugin annotation smoke checks', () => { expect(generatedEnums).toContain('RENDERGROUP_NONE = 5'); expect(generatedList).toContain('---@overload fun(identifier: "SkeletonConvertor", key: string, item: SkeletonConvertor)'); }); + + test('global IsValid uses an object-wide validity guard', () => { + const globalLua = readOutput('global.lua'); + const isValidBlock = globalLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/Global\.IsValid[\s\S]*?function _G\.IsValid\(object\) end/, + )?.[0]; + + expect(isValidBlock).toBeDefined(); + expect(isValidBlock).toContain('---@param object any The table or object to be validated.'); + expect(isValidBlock).toContain('---@return TypeGuard isValid # True if the object is valid.'); + expect(isValidBlock).toContain('---@return_cast object -NULL'); + expect(isValidBlock).toContain('---@[valid_guard]'); + expect(isValidBlock).not.toContain('TypeGuard'); + expect(isValidBlock).not.toContain('---@param ent'); + expect(isValidBlock).not.toContain('function _G.IsValid(ent)'); + }); + + test('entity predicate overrides keep lowercase and legacy pages separate', () => { + const isEntityOverride = readCustom('Global.isentity.lua'); + const legacyIsEntityOverride = readCustom('Global.IsEntity.legacy..lua'); + + expect(isEntityOverride).toContain('---@source https://wiki.facepunch.com/gmod/Global.isentity'); + expect(isEntityOverride).toContain('function _G.isentity(var) end'); + expect(isEntityOverride).not.toContain('Global.IsEntity'); + + expect(legacyIsEntityOverride).toContain('---@source https://wiki.facepunch.com/gmod/Global.IsEntity(legacy)'); + expect(legacyIsEntityOverride).toContain('---@deprecated Use the function Global.isentity instead.'); + expect(legacyIsEntityOverride).toContain('function _G.IsEntity(var) end'); + expect(legacyIsEntityOverride).not.toContain('function _G.isentity'); + }); }); diff --git a/custom/Global.IsEntity.legacy..lua b/custom/Global.IsEntity.legacy..lua new file mode 100644 index 00000000..0e6ec916 --- /dev/null +++ b/custom/Global.IsEntity.legacy..lua @@ -0,0 +1,8 @@ +---Identical to isentity. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.IsEntity(legacy) +---@deprecated Use the function Global.isentity instead. +---@param var any +---@return TypeGuard isEntity # Whether the value is an Entity. +function _G.IsEntity(var) end diff --git a/custom/Global.IsValid.lua b/custom/Global.IsValid.lua index f099b1cd..34061b72 100644 --- a/custom/Global.IsValid.lua +++ b/custom/Global.IsValid.lua @@ -4,7 +4,8 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.IsValid ----@param ent any The table or object to be validated. ----@return TypeGuard isValid # True if the object is valid. +---@param object any The table or object to be validated. +---@return TypeGuard isValid # True if the object is valid. +---@return_cast object -NULL ---@[valid_guard] -function _G.IsValid(ent) end +function _G.IsValid(object) end From 87006bd771e9b463fff8f07069d11d063bf9dfdc Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:42:59 +0100 Subject: [PATCH 094/117] Fix various annotations based on lua source --- __tests__/custom-annotations.spec.ts | 108 +++++++++++++++++++++++++ custom/DPropertySheet.AddSheet.lua | 2 +- custom/DPropertySheet.GetActiveTab.lua | 2 +- custom/class.DermaAnimation.lua | 4 +- custom/controlpanel.Get.lua | 2 +- custom/gamemode.Get.lua | 2 +- custom/scripted_ents.Get.lua | 2 +- custom/string.FormattedTime.lua | 12 +-- custom/table.Copy.lua | 8 +- custom/vgui.Create.lua | 4 +- custom/vgui.CreateFromTable.lua | 4 +- custom/vgui.GetControlTable.lua | 2 +- custom/weapons.GetStored.lua | 2 +- 13 files changed, 132 insertions(+), 22 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 7a1c8bd0..1bde9a47 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -181,6 +181,114 @@ describe('custom and plugin annotation smoke checks', () => { expect(isValidBlock).not.toContain('function _G.IsValid(ent)'); }); + test('source-backed Lua helper overrides expose optional/internal arguments accurately', () => { + const stringLua = readOutput('string.lua'); + const tableLua = readOutput('table.lua'); + const formattedTimeBlock = stringLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/string\.FormattedTime[\s\S]*?function string\.FormattedTime\(seconds, format\) end/, + )?.[0]; + const tableCopyBlock = tableLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/table\.Copy[\s\S]*?function table\.Copy\(originalTable, lookupTable\) end/, + )?.[0]; + + expect(formattedTimeBlock).toBeDefined(); + expect(formattedTimeBlock).toContain('---@overload fun(seconds: number): FormattedTime'); + expect(formattedTimeBlock).toContain('---@overload fun(seconds: number, format: nil): FormattedTime'); + expect(formattedTimeBlock).toContain('---@param seconds? number Number of seconds to format.'); + expect(formattedTimeBlock).toContain('---@param format? string The format string.'); + expect(formattedTimeBlock).toContain('---@return string|FormattedTime'); + expect(formattedTimeBlock).not.toContain('---@param float'); + expect(formattedTimeBlock).not.toContain('function string.FormattedTime(float, format)'); + + expect(tableCopyBlock).toBeDefined(); + expect(tableCopyBlock).toContain('---@param lookupTable? table Table used internally to preserve cyclic references.'); + expect(tableCopyBlock).toContain('function table.Copy(originalTable, lookupTable) end'); + }); + + test('source-backed VGUI lookup and creation overrides expose nil failure paths', () => { + const vguiLua = readOutput('vgui.lua'); + const createBlock = vguiLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/vgui\.Create[\s\S]*?function vgui\.Create\(classname, parent, name\) end/, + )?.[0]; + const createFromTableBlock = vguiLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/vgui\.CreateFromTable[\s\S]*?function vgui\.CreateFromTable\(metatable, parent, name\) end/, + )?.[0]; + const getControlTableBlock = vguiLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/vgui\.GetControlTable[\s\S]*?function vgui\.GetControlTable\(Panelname\) end/, + )?.[0]; + + expect(createBlock).toBeDefined(); + expect(createBlock).toContain('---@overload fun(classname: string, parent?: Panel, name?: string): Panel?'); + expect(createBlock).toContain('---@return (instance) T?'); + + expect(createFromTableBlock).toBeDefined(); + expect(createFromTableBlock).toContain('---@param metatable T? Your PANEL table.'); + expect(createFromTableBlock).toContain('---@return (instance) Panel?'); + + expect(getControlTableBlock).toBeDefined(); + expect(getControlTableBlock).toContain('---@return (definition) `T`?'); + }); + + test('DermaAnimation class fragment matches the Lua runtime state shape', () => { + const customClasses = readOutput('custom_classes.lua'); + const dermaAnimationBlock = customClasses.match( + /---@class DermaAnimation[\s\S]*?function DermaAnimation:Active\(\) end/, + )?.[0]; + + expect(dermaAnimationBlock).toBeDefined(); + expect(dermaAnimationBlock).toContain('---@field Length? number'); + expect(dermaAnimationBlock).toContain('---@return boolean?'); + expect(dermaAnimationBlock).not.toContain('---@field Length number'); + expect(dermaAnimationBlock).not.toContain('---@return boolean\nfunction DermaAnimation:Active() end'); + }); + + test('DPropertySheet overrides expose source-backed absent tab and invalid-panel paths', () => { + const propertySheetLua = readOutput('dpropertysheet.lua'); + const addSheetBlock = propertySheetLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DPropertySheet:AddSheet[\s\S]*?function DPropertySheet:AddSheet\(name, pnl, icon, noStretchX, noStretchY, tooltip\) end/, + )?.[0]; + const getActiveTabBlock = propertySheetLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DPropertySheet:GetActiveTab[\s\S]*?function DPropertySheet:GetActiveTab\(\) end/, + )?.[0]; + + expect(addSheetBlock).toBeDefined(); + expect(addSheetBlock).toContain('---@return DPropertySheetSheet? sheet'); + + expect(getActiveTabBlock).toBeDefined(); + expect(getActiveTabBlock).toContain('---@return DTab?'); + }); + + test('source-backed registry lookup overrides expose missing-entry nil results', () => { + const controlPanelLua = readOutput('controlpanel.lua'); + const gamemodeLua = readOutput('gamemode.lua'); + const scriptedEntsLua = readOutput('scripted_ents.lua'); + const weaponsLua = readOutput('weapons.lua'); + const controlPanelGetBlock = controlPanelLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/controlpanel\.Get[\s\S]*?function controlpanel\.Get\(name\) end/, + )?.[0]; + const gamemodeGetBlock = gamemodeLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/gamemode\.Get[\s\S]*?function gamemode\.Get\(name\) end/, + )?.[0]; + const scriptedEntsGetBlock = scriptedEntsLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/scripted_ents\.Get[\s\S]*?function scripted_ents\.Get\(classname\) end/, + )?.[0]; + const weaponsGetStoredBlock = weaponsLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/weapons\.GetStored[\s\S]*?function weapons\.GetStored\(weapon_class\) end/, + )?.[0]; + + expect(controlPanelGetBlock).toBeDefined(); + expect(controlPanelGetBlock).toContain('---@return ControlPanel?'); + + expect(gamemodeGetBlock).toBeDefined(); + expect(gamemodeGetBlock).toContain('---@return (definition) `T`?'); + + expect(scriptedEntsGetBlock).toBeDefined(); + expect(scriptedEntsGetBlock).toContain('---@return (definition) `T`?'); + + expect(weaponsGetStoredBlock).toBeDefined(); + expect(weaponsGetStoredBlock).toContain('---@return (definition) `T`?'); + }); + test('entity predicate overrides keep lowercase and legacy pages separate', () => { const isEntityOverride = readCustom('Global.isentity.lua'); const legacyIsEntityOverride = readCustom('Global.IsEntity.legacy..lua'); diff --git a/custom/DPropertySheet.AddSheet.lua b/custom/DPropertySheet.AddSheet.lua index 3fc06365..6cde3b43 100644 --- a/custom/DPropertySheet.AddSheet.lua +++ b/custom/DPropertySheet.AddSheet.lua @@ -13,5 +13,5 @@ ---@param noStretchX? boolean Should DPropertySheet try to fill itself with given panel horizontally. ---@param noStretchY? boolean Should DPropertySheet try to fill itself with given panel vertically. ---@param tooltip? string Tooltip for the tab when user hovers over it with his cursor ----@return DPropertySheetSheet sheet The created sheet record. +---@return DPropertySheetSheet? sheet The created sheet record, or nil if the panel is invalid. function DPropertySheet:AddSheet(name, pnl, icon, noStretchX, noStretchY, tooltip) end diff --git a/custom/DPropertySheet.GetActiveTab.lua b/custom/DPropertySheet.GetActiveTab.lua index 4cf8346a..ca10b488 100644 --- a/custom/DPropertySheet.GetActiveTab.lua +++ b/custom/DPropertySheet.GetActiveTab.lua @@ -1,5 +1,5 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/DPropertySheet:GetActiveTab ----@return DTab # The active [DTab](https://wiki.facepunch.com/gmod/DTab). +---@return DTab? # The active [DTab](https://wiki.facepunch.com/gmod/DTab), or nil if no active tab is set. function DPropertySheet:GetActiveTab() end diff --git a/custom/class.DermaAnimation.lua b/custom/class.DermaAnimation.lua index 8a0eb7e1..2786e307 100644 --- a/custom/class.DermaAnimation.lua +++ b/custom/class.DermaAnimation.lua @@ -7,7 +7,7 @@ ---@field Running? boolean Whether the animation is currently running. ---@field Started? boolean Set true on the first tick; cleared after first call. ---@field Finished? boolean Set true on the final tick. ----@field Length number Total duration in seconds. +---@field Length? number Total duration in seconds. ---@field StartTime? number SysTime() when the animation began. ---@field EndTime? number SysTime() when the animation will end. local DermaAnimation = {} @@ -17,5 +17,5 @@ function DermaAnimation:Run() end ---@param data? any function DermaAnimation:Start(length, data) end function DermaAnimation:Stop() end ----@return boolean +---@return boolean? function DermaAnimation:Active() end diff --git a/custom/controlpanel.Get.lua b/custom/controlpanel.Get.lua index a5367280..7299d96f 100644 --- a/custom/controlpanel.Get.lua +++ b/custom/controlpanel.Get.lua @@ -2,5 +2,5 @@ ---@realm client ---@source https://wiki.facepunch.com/gmod/controlpanel.Get ---@param name string The name of the panel. ----@return ControlPanel # The ControlPanel panel. +---@return ControlPanel? # The ControlPanel panel, or nil if it cannot be created yet. function controlpanel.Get(name) end diff --git a/custom/gamemode.Get.lua b/custom/gamemode.Get.lua index 74296e78..f4c4c778 100644 --- a/custom/gamemode.Get.lua +++ b/custom/gamemode.Get.lua @@ -5,5 +5,5 @@ ---@source https://wiki.facepunch.com/gmod/gamemode.Get ---@generic T : table ---@param name `T` The name of the gamemode you want to get. ----@return (definition) `T` # The gamemode's table. +---@return (definition) `T`? # The gamemode's table, or nil if no gamemode is registered with that name. function gamemode.Get(name) end diff --git a/custom/scripted_ents.Get.lua b/custom/scripted_ents.Get.lua index 00952b3a..fd70496d 100644 --- a/custom/scripted_ents.Get.lua +++ b/custom/scripted_ents.Get.lua @@ -5,5 +5,5 @@ ---@source https://wiki.facepunch.com/gmod/scripted_ents.Get ---@generic T : table ---@param classname `T` The classname of the ENT table to return, can be an alias ----@return (definition) `T` # entTable +---@return (definition) `T`? # entTable, or nil if no scripted entity is registered with that class name. function scripted_ents.Get(classname) end diff --git a/custom/string.FormattedTime.lua b/custom/string.FormattedTime.lua index 516093d7..2ec584a8 100644 --- a/custom/string.FormattedTime.lua +++ b/custom/string.FormattedTime.lua @@ -5,9 +5,9 @@ ---@realm menu ---@realm server ---@source https://wiki.facepunch.com/gmod/string.FormattedTime ----@overload fun(float: number): FormattedTime ----@overload fun(float: number, format: nil): FormattedTime ----@param float number Number of seconds to format. ----@param format string The format string. If this is omitted, a FormattedTime table is returned instead. ----@return string # The formatted time string. -function string.FormattedTime(float, format) end +---@overload fun(seconds: number): FormattedTime +---@overload fun(seconds: number, format: nil): FormattedTime +---@param seconds? number Number of seconds to format. +---@param format? string The format string. If this is omitted, a FormattedTime table is returned instead. +---@return string|FormattedTime # The formatted time string, or a FormattedTime table when no format is supplied. +function string.FormattedTime(seconds, format) end diff --git a/custom/table.Copy.lua b/custom/table.Copy.lua index f1eda43b..035ae1a3 100644 --- a/custom/table.Copy.lua +++ b/custom/table.Copy.lua @@ -7,6 +7,8 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/table.Copy ---@generic T : table ----@param originalTable T The table to be copied. ----@return T # A deep copy of the original table -function table.Copy(originalTable) end +---@overload fun(originalTable: nil): nil +---@param originalTable T? The table to be copied. +---@param lookupTable? table Table used internally to preserve cyclic references. +---@return T? # A deep copy of the original table, or nil when originalTable is nil. +function table.Copy(originalTable, lookupTable) end diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua index fc078659..06b64501 100644 --- a/custom/vgui.Create.lua +++ b/custom/vgui.Create.lua @@ -4,7 +4,7 @@ ---@realm menu ---@source https://wiki.facepunch.com/gmod/vgui.Create ---@generic T: Panel ----@overload fun(classname: string, parent?: Panel, name?: string): Panel # Creates a panel from a dynamic class name. +---@overload fun(classname: string, parent?: Panel, name?: string): Panel? # Creates a panel from a dynamic class name. ---@[call_arg("gmod.vgui_panel", "reference")] ---@param classname `T` Classname of the panel to create. --- @@ -14,5 +14,5 @@ --- ---@param parent Panel? Panel to parent to. ---@param name string? Custom name of the created panel for scripting/debugging purposes. Can be retrieved with Panel:GetName. ----@return (instance) T #The created panel, or `nil` if creation failed for whatever reason. +---@return (instance) T? #The created panel, or `nil` if creation failed for whatever reason. function vgui.Create(classname, parent, name) end diff --git a/custom/vgui.CreateFromTable.lua b/custom/vgui.CreateFromTable.lua index ecfa4005..d9637881 100644 --- a/custom/vgui.CreateFromTable.lua +++ b/custom/vgui.CreateFromTable.lua @@ -5,8 +5,8 @@ ---@generic T: table ---@[call_arg("gmod.vgui_panel", "register_table")] ---@[call_arg_field("gmod.vgui_panel", "base", "Base")] ----@param metatable T Your PANEL table. +---@param metatable T? Your PANEL table. ---@param parent? Panel Which panel to parent the newly created panel to. ---@param name? string Custom name of the created panel for scripting/debugging purposes. Can be retrieved with Panel:GetName. ----@return (instance) Panel # The created panel, or `nil` if creation failed for whatever reason. +---@return (instance) Panel? # The created panel, or `nil` if creation failed for whatever reason. function vgui.CreateFromTable(metatable, parent, name) end diff --git a/custom/vgui.GetControlTable.lua b/custom/vgui.GetControlTable.lua index 821b1991..79674459 100644 --- a/custom/vgui.GetControlTable.lua +++ b/custom/vgui.GetControlTable.lua @@ -4,5 +4,5 @@ ---@source https://wiki.facepunch.com/gmod/vgui.GetControlTable ---@generic T : table ---@param Panelname `T` The name of the panel to get the table of. ----@return (definition) `T` # The `PANEL` table of the a Lua-defined panel with given name. +---@return (definition) `T`? # The `PANEL` table of the a Lua-defined panel with given name, or `nil` if no Lua-defined panel is registered with that name. function vgui.GetControlTable(Panelname) end diff --git a/custom/weapons.GetStored.lua b/custom/weapons.GetStored.lua index a033f217..0b973e47 100644 --- a/custom/weapons.GetStored.lua +++ b/custom/weapons.GetStored.lua @@ -5,5 +5,5 @@ ---@source https://wiki.facepunch.com/gmod/weapons.GetStored ---@generic T : table ---@param weapon_class `T` Weapon class to retrieve weapon table of ----@return (definition) `T` # The weapon table +---@return (definition) `T`? # The weapon table, or nil if no weapon is registered with that class name. function weapons.GetStored(weapon_class) end From da588049c5f57a70e1f618e0bce65fa27febc689 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 5 Jul 2026 19:32:18 +0100 Subject: [PATCH 095/117] Fix Weapon:GetOwner return type --- custom/class.Weapon.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/custom/class.Weapon.lua b/custom/class.Weapon.lua index b4c16582..643bfd87 100644 --- a/custom/class.Weapon.lua +++ b/custom/class.Weapon.lua @@ -38,5 +38,5 @@ WEAPON = Weapon --- initializing, or being removed. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Entity:GetOwner ----@return Entity|Player|NPC|NULL # The entity currently owning this weapon. +---@return Entity|NULL # The entity currently owning this weapon. function Weapon:GetOwner() end From 2a855ec8c655dcae849514d008b6f7d4d01dcb83 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:50:51 +0100 Subject: [PATCH 096/117] Remove "side-effect-free" annotation type --- custom/ConVar.GetString.lua | 1 - custom/Global.Color.lua | 1 - custom/Global.ColorAlpha.lua | 1 - custom/Global.ColorToHSL.lua | 1 - custom/Global.ColorToHSV.lua | 1 - custom/Global.HSLToColor.lua | 1 - custom/Global.HSVToColor.lua | 1 - custom/Global.IsColor.lua | 1 - custom/math.max.lua | 1 - custom/math.min.lua | 1 - 10 files changed, 10 deletions(-) diff --git a/custom/ConVar.GetString.lua b/custom/ConVar.GetString.lua index 86baa641..955dfe9d 100644 --- a/custom/ConVar.GetString.lua +++ b/custom/ConVar.GetString.lua @@ -2,6 +2,5 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/ConVar:GetString ----@[side_effect_free] ---@return string # The current console variable value as a string. function ConVar:GetString() end diff --git a/custom/Global.Color.lua b/custom/Global.Color.lua index d186fc7a..b82b777f 100644 --- a/custom/Global.Color.lua +++ b/custom/Global.Color.lua @@ -11,5 +11,4 @@ ---@[call_arg("gmod.color", "a")] ---@param a? number The alpha channel, from 0 to 255. ---@return Color ----@[side_effect_free] function _G.Color(r, g, b, a) end diff --git a/custom/Global.ColorAlpha.lua b/custom/Global.ColorAlpha.lua index d5b4cb0f..42b68ead 100644 --- a/custom/Global.ColorAlpha.lua +++ b/custom/Global.ColorAlpha.lua @@ -5,5 +5,4 @@ ---@param color Color The Color from which to take RGB values. This color will not be modified. ---@param alpha number The new alpha value, a number between 0 and 255. Values above 255 will be clamped. ---@return Color # The new Color with the modified alpha value ----@[side_effect_free] function _G.ColorAlpha(color, alpha) end diff --git a/custom/Global.ColorToHSL.lua b/custom/Global.ColorToHSL.lua index 8dd617dc..39fe5e5b 100644 --- a/custom/Global.ColorToHSL.lua +++ b/custom/Global.ColorToHSL.lua @@ -2,7 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.ColorToHSL ----@[side_effect_free] ---@param color Color The Color. ---@return number # The hue in degrees [0, 360]. ---@return number # The saturation in the range [0, 1]. diff --git a/custom/Global.ColorToHSV.lua b/custom/Global.ColorToHSV.lua index db798194..2c440585 100644 --- a/custom/Global.ColorToHSV.lua +++ b/custom/Global.ColorToHSV.lua @@ -2,7 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.ColorToHSV ----@[side_effect_free] ---@param color Color The Color. ---@return number # The hue in degrees [0, 360]. ---@return number # The saturation in the range [0, 1]. diff --git a/custom/Global.HSLToColor.lua b/custom/Global.HSLToColor.lua index e262e1df..46ca4557 100644 --- a/custom/Global.HSLToColor.lua +++ b/custom/Global.HSLToColor.lua @@ -1,6 +1,5 @@ ---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L76-L105 ---@return Color ----@[side_effect_free] ---@param h number ---@param s number ---@param l number diff --git a/custom/Global.HSVToColor.lua b/custom/Global.HSVToColor.lua index 2bc4bca3..04f39710 100644 --- a/custom/Global.HSVToColor.lua +++ b/custom/Global.HSVToColor.lua @@ -1,6 +1,5 @@ ---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L45-L74 ---@return Color ----@[side_effect_free] ---@param h number ---@param s number ---@param v number diff --git a/custom/Global.IsColor.lua b/custom/Global.IsColor.lua index eea802ad..31400a5f 100644 --- a/custom/Global.IsColor.lua +++ b/custom/Global.IsColor.lua @@ -4,5 +4,4 @@ ---@source https://wiki.facepunch.com/gmod/Global.IsColor ---@param var any ---@return TypeGuard isColor # Whether the value is a Color. ----@[side_effect_free] function _G.IsColor(var) end diff --git a/custom/math.max.lua b/custom/math.max.lua index 2172b406..9887c3ff 100644 --- a/custom/math.max.lua +++ b/custom/math.max.lua @@ -2,7 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/math.max ----@[side_effect_free] ---@param ... number Numbers to get the largest from. ---@return number # The largest number. function math.max(...) end diff --git a/custom/math.min.lua b/custom/math.min.lua index 14d2c65c..f4773dc7 100644 --- a/custom/math.min.lua +++ b/custom/math.min.lua @@ -2,7 +2,6 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/math.min ----@[side_effect_free] ---@param ... number Numbers to get the smallest from. ---@return number # The smallest number. function math.min(...) end From d3603bb0472ef84ee9d80c0e8a7cf517f6172db8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sat, 11 Jul 2026 16:23:34 +0100 Subject: [PATCH 097/117] Preserve structure annotations during merge --- __tests__/custom-annotations.spec.ts | 8 + __tests__/custom-class-override.spec.ts | 166 +++++++++++++++++++- src/api-writer/glua-api-writer.ts | 196 ++++++++++++++++++++++-- 3 files changed, 355 insertions(+), 15 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 1bde9a47..a15273a0 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -81,6 +81,14 @@ describe('custom and plugin annotation smoke checks', () => { } }); + test('GM annotations include runtime-populated structure fields', () => { + const gmLua = readOutput('gm.lua'); + + for (const field of ['FolderName', 'Folder', 'ThisClass', 'BaseClass']) { + expect(gmLua).toContain(`---@field ${field} `); + } + }); + test('custom class fragments are included in the generated custom class bundle', () => { const customClasses = readOutput('custom_classes.lua'); const classFiles = [ diff --git a/__tests__/custom-class-override.spec.ts b/__tests__/custom-class-override.spec.ts index 01887370..b8d169e8 100644 --- a/__tests__/custom-class-override.spec.ts +++ b/__tests__/custom-class-override.spec.ts @@ -5,8 +5,9 @@ import { GluaApiWriter } from '../src/api-writer/glua-api-writer.js'; describe('Custom class overrides emission', () => { const tmpDir = path.join(process.cwd(), 'output_test_tmp'); - beforeAll(() => { - if (!fs.existsSync(tmpDir)) fs.mkdirSync(tmpDir, { recursive: true }); + beforeEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.mkdirSync(tmpDir, { recursive: true }); }); afterAll(() => { @@ -25,4 +26,165 @@ describe('Custom class overrides emission', () => { expect(content).toMatch(/@class ENT/); expect(content).toMatch(/ENT = {}/); }); + + test('class overrides retain canonical metadata and deduplicated structure fields', () => { + const writer = new GluaApiWriter(tmpDir); + writer.addOverride('class.GM', [ + '---@class GM', + '---@field FolderName? integer Custom field shape.', + 'GM = {}', + '', + ].join('\n')); + + writer.writePages([{ + type: 'hook', + name: 'Think', + address: 'GM:Think', + parent: 'GM', + description: 'Runs every frame.', + arguments: [], + returns: [], + }], path.join(tmpDir, 'gm.lua'), 0); + writer.writePages([{ + type: 'struct', + name: 'GM', + address: 'GM', + description: 'Gamemode data.', + realm: 'shared', + deprecated: 'Use the replacement gamemode type.', + url: 'https://wiki.facepunch.com/gmod/Structures/GM', + fields: [ + { name: 'FolderName', type: 'string', description: 'Generated folder name.' }, + { name: 'Folder', type: 'string', description: 'Generated folder path.' }, + { name: 'Folder', type: 'number', description: 'Duplicate field from the same page.' }, + ], + }], path.join(tmpDir, 'structures.lua'), 1); + + writer.writeToDisk(); + + const gmOutput = fs.readFileSync(path.join(tmpDir, 'gm.lua'), 'utf8'); + expect(gmOutput).toContain('--- Gamemode data.'); + expect(gmOutput).toContain('---@realm shared'); + expect(gmOutput).toContain('---@source https://wiki.facepunch.com/gmod/Structures/GM'); + expect(gmOutput).toContain('---@deprecated Use the replacement gamemode type.'); + expect(gmOutput).toContain('---@field FolderName? integer Custom field shape.'); + expect(gmOutput).not.toContain('---@field FolderName string'); + expect(gmOutput).toContain('---@field Folder string'); + expect(gmOutput).not.toContain('---@field Folder number'); + expect(gmOutput).toContain('function GM:Think() end'); + expect((gmOutput.match(/---@class GM/g) ?? [])).toHaveLength(1); + expect((gmOutput.match(/---@field FolderName\?? /g) ?? [])).toHaveLength(1); + expect((gmOutput.match(/---@field Folder\?? /g) ?? [])).toHaveLength(1); + }); + + test('class placement and content are stable when modules are registered in reverse order', () => { + const generate = (directory: string, reverse: boolean) => { + fs.mkdirSync(directory, { recursive: true }); + const writer = new GluaApiWriter(directory); + writer.addOverride('class.GM', '---@class GM\nGM = {}\n'); + + const modules: Array<[any[], string, number]> = [ + [[{ + type: 'hook', + name: 'Think', + address: 'GM:Think', + parent: 'GM', + description: 'Runs every frame.', + arguments: [], + returns: [], + }], path.join(directory, 'gm.lua'), 0], + [[ + { + type: 'class', + name: 'GM', + address: 'GM_Class', + parent: 'BaseGM', + description: 'Canonical gamemode class.', + realm: 'shared', + url: 'https://wiki.facepunch.com/gmod/GM_Class', + }, + { + type: 'struct', + name: 'GM', + address: 'GM', + description: 'Gamemode data.', + realm: 'shared', + deprecated: 'Legacy structure metadata.', + url: 'https://wiki.facepunch.com/gmod/Structures/GM', + fields: [{ name: 'FolderName', type: 'string', description: 'Generated folder name.' }], + }, + ], path.join(directory, 'structures.lua'), 1], + ]; + + for (const [pages, filePath, index] of reverse ? modules.reverse() : modules) { + writer.writePages(pages, filePath, index); + } + writer.writeToDisk(); + + return fs.readFileSync(path.join(directory, 'gm.lua'), 'utf8'); + }; + + const forward = generate(path.join(tmpDir, 'forward'), false); + const reverse = generate(path.join(tmpDir, 'reverse'), true); + + expect(reverse).toBe(forward); + expect(forward).toContain('--- Canonical gamemode class.'); + expect(forward).toContain('---@source https://wiki.facepunch.com/gmod/GM_Class'); + expect(forward).toContain('---@deprecated Legacy structure metadata.'); + expect(forward).toContain('---@class GM : BaseGM'); + expect((forward.match(/---@class GM/g) ?? [])).toHaveLength(1); + expect(forward.indexOf('---@class GM')).toBeLessThan(forward.indexOf('function GM:Think() end')); + expect(fs.existsSync(path.join(tmpDir, 'forward', 'structures.lua'))).toBe(false); + expect(fs.existsSync(path.join(tmpDir, 'reverse', 'structures.lua'))).toBe(false); + }); + + test('an earlier alias module cannot consume the canonical class header', () => { + const writer = new GluaApiWriter(tmpDir); + const aliasFile = path.join(tmpDir, 'aaa.lua'); + const ownerFile = path.join(tmpDir, 'panel.lua'); + + writer.writePages([ + { + type: 'class', + name: 'PANEL', + address: 'PANEL_Hooks', + parent: 'Panel', + description: 'Alias hook surface.', + }, + { + type: 'classfunc', + name: 'AliasMethod', + address: 'PANEL:AliasMethod', + parent: 'PANEL', + description: 'Method that remains in the alias module.', + arguments: [], + returns: [], + }, + ], aliasFile, 0); + writer.writePages([{ + type: 'panel', + name: 'Panel', + address: 'Panel', + parent: 'BasePanel', + description: 'Canonical panel.', + }], ownerFile, 1); + writer.writePages([{ + type: 'struct', + name: 'Panel', + address: 'Structures/Panel', + description: 'Panel fields.', + fields: [{ name: 'Dock', type: 'number', description: 'Dock mode.' }], + }], path.join(tmpDir, 'structures.lua'), 2); + + writer.writeToDisk(); + + const aliasOutput = fs.readFileSync(aliasFile, 'utf8'); + const ownerOutput = fs.existsSync(ownerFile) ? fs.readFileSync(ownerFile, 'utf8') : ''; + expect(aliasOutput).toContain('function Panel:AliasMethod() end'); + expect(aliasOutput).not.toContain('---@class (partial) Panel'); + expect(aliasOutput).not.toContain('---@field Dock number'); + expect(ownerOutput).toContain('---@class (partial) Panel : BasePanel'); + expect(ownerOutput).toContain('---@field Dock number'); + expect((`${aliasOutput}\n${ownerOutput}`.match(/---@class \(partial\) Panel/g) ?? [])).toHaveLength(1); + }); }); diff --git a/src/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts index 89a37bbd..85b5dcfc 100644 --- a/src/api-writer/glua-api-writer.ts +++ b/src/api-writer/glua-api-writer.ts @@ -1,4 +1,4 @@ -import { ClassFunction, Enum, Function, HookFunction, LibraryFunction, TypePage, Panel, PanelFunction, Realm, Struct, WikiPage, isPanel, FunctionArgument, FunctionCallback } from '../scrapers/wiki-page-markup-scraper.js'; +import { ClassFunction, Enum, Function, HookFunction, LibraryFunction, TypePage, Panel, PanelFunction, Realm, Struct, StructField, WikiPage, isPanel, FunctionArgument, FunctionCallback } from '../scrapers/wiki-page-markup-scraper.js'; import { indentText, wrapInComment, removeNewlines, safeFileName, toLowerCamelCase } from '../utils/string.js'; import { isClassFunction, @@ -50,10 +50,26 @@ type FunctionGenericHint = { returnsCollection: boolean; }; +type ClassMetadata = { + description?: string; + realm?: Realm; + url?: string; + parent?: string; + deprecated?: string; +}; + +type PlannedClass = ClassMetadata & { + name: string; + outputFilePath: string; + fields: StructField[]; +}; + export class GluaApiWriter { private readonly writtenClasses: Set = new Set(); private readonly writtenLibraryGlobals: Set = new Set(); private readonly pageOverrides: Map = new Map(); + private readonly plannedClasses: Map = new Map(); + private currentOutputFilePath?: string; private readonly files: Map = new Map(); @@ -109,6 +125,33 @@ export class GluaApiWriter { return trimmedOverride.replace(classValuePattern, `${trimmedFields}\n\n$1`); } + private getOverrideFieldNames(override: string) { + return new Set( + [...override.matchAll(/^---@field\s+([^\s?]+)\??(?:\s|$)/gm)] + .map(match => match[1]), + ); + } + + private injectClassParentIntoOverride(override: string, className: string, parent?: string) { + if (!parent) + return override; + + const escapedClassName = className.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const classPattern = new RegExp(`(^---@class(?: \\(partial\\))? ${escapedClassName})(?!\\s*:)`, 'm'); + return override.replace(classPattern, `$1 : ${parent}`); + } + + private writeClassMetadata(metadata: ClassMetadata) { + let api = metadata.description ? `${wrapInComment(metadata.description, false)}\n` : ''; + api += this.writeRealmAnnotations(metadata.realm); + api += this.writeSourceAnnotation(metadata.url); + + if (metadata.deprecated) + api += `---@deprecated ${removeNewlines(metadata.deprecated)}\n`; + + return api; + } + /** * Checks if a class name has aliases that should be generated. */ @@ -181,24 +224,27 @@ export class GluaApiWriter { } // Remove debug logging - private writeClassStart(className: string, realm?: Realm, url?: string, parent?: string, deprecated?: string, description?: string, classFields: string = '') { + private writeClassStart(className: string, realm?: Realm, url?: string, parent?: string, deprecated?: string, description?: string, classFields: string = '', includeMetadataWithOverride: boolean = false) { let api: string = ''; // Resolve class name to canonical form const canonicalClassName = this.resolveToCanonicalClassName(className); const isAlias = canonicalClassName !== className; + const plannedClass = this.plannedClasses.get(canonicalClassName); + + if (this.currentOutputFilePath && plannedClass && plannedClass.outputFilePath !== this.currentOutputFilePath) + return ''; if (!this.writtenClasses.has(canonicalClassName)) { const classOverride = `class.${canonicalClassName}`; if (this.pageOverrides.has(classOverride)) { - api += this.injectClassFieldsIntoOverride(this.pageOverrides.get(classOverride)!, canonicalClassName, classFields) + '\n\n'; - } else { - api += description ? `${wrapInComment(description, false)}\n` : ''; - api += this.writeRealmAnnotations(realm); - api += this.writeSourceAnnotation(url); + if (includeMetadataWithOverride) + api += this.writeClassMetadata({ realm, url, deprecated, description }); - if (deprecated) - api += `---@deprecated ${removeNewlines(deprecated)}\n`; + const override = this.injectClassParentIntoOverride(this.pageOverrides.get(classOverride)!, canonicalClassName, parent); + api += this.injectClassFieldsIntoOverride(override, canonicalClassName, classFields) + '\n\n'; + } else { + api += this.writeClassMetadata({ realm, url, deprecated, description }); api += `---@class (partial) ${canonicalClassName}`; @@ -490,8 +536,122 @@ export class GluaApiWriter { return this.files.get(filePath) ?? []; } - public makeApiFromPages(pages: IndexedWikiPage[]) { + private collectClassPlans() { + this.plannedClasses.clear(); + + const entries = [...this.files.entries()] + .flatMap(([filePath, pages]) => pages.map(page => ({ ...page, filePath }))) + .sort((a, b) => + a.filePath.localeCompare(b.filePath) + || a.page.address.localeCompare(b.page.address) + || a.index - b.index, + ); + const outputFiles = [...this.files.keys()].sort((a, b) => a.localeCompare(b)); + const classNames = new Set(); + + for (const { page } of entries) { + let className: string | undefined; + if (isClass(page) || isStruct(page) || isPanel(page)) + className = page.name; + else if (isClassFunction(page) || isHookFunction(page) || isPanelFunction(page)) + className = page.parent; + + if (className) + classNames.add(this.resolveToCanonicalClassName(className)); + } + + for (const canonicalClassName of [...classNames].sort((a, b) => a.localeCompare(b))) { + const relevantEntries = entries.filter(({ page }) => { + const pageClassName = isClass(page) || isStruct(page) || isPanel(page) + ? page.name + : isClassFunction(page) || isHookFunction(page) || isPanelFunction(page) + ? page.parent + : undefined; + return pageClassName !== undefined + && this.resolveToCanonicalClassName(pageClassName) === canonicalClassName; + }); + const metadataEntries = relevantEntries + .filter(({ page }) => isClass(page) || isStruct(page) || isPanel(page)) + .sort((a, b) => { + const exactNameDifference = Number(a.page.name !== canonicalClassName) - Number(b.page.name !== canonicalClassName); + if (exactNameDifference !== 0) return exactNameDifference; + + const kindPriority = (page: WikiPage) => isClass(page) ? 0 : isStruct(page) ? 1 : 2; + return kindPriority(a.page) - kindPriority(b.page) + || a.filePath.localeCompare(b.filePath) + || a.page.address.localeCompare(b.page.address) + || a.index - b.index; + }); + const metadataPages = metadataEntries.map(({ page }) => page); + const firstMetadataValue = (select: (page: WikiPage) => T | undefined) => { + for (const page of metadataPages) { + const value = select(page); + if (value !== undefined && value !== '') return value; + } + return undefined; + }; + const matchingModule = outputFiles.find(filePath => { + const baseName = filePath.split(/[\\/]/).pop()?.replace(/\.lua$/i, '') ?? ''; + return baseName.toLowerCase() === canonicalClassName.toLowerCase(); + }); + const outputFilePath = matchingModule + ?? metadataEntries[0]?.filePath + ?? relevantEntries[0].filePath; + const customOverride = this.pageOverrides.get(`class.${canonicalClassName}`) ?? ''; + const customFieldNames = this.getOverrideFieldNames(customOverride); + const writtenFieldNames = new Set(customFieldNames); + const fields: StructField[] = []; + + for (const { page } of relevantEntries) { + if (!isStruct(page)) continue; + + for (const field of page.fields) { + const fieldName = GluaApiWriter.safeName(field.name); + if (writtenFieldNames.has(fieldName)) continue; + + writtenFieldNames.add(fieldName); + fields.push(field); + } + } + + this.plannedClasses.set(canonicalClassName, { + name: canonicalClassName, + outputFilePath, + fields, + description: firstMetadataValue(page => page.description), + realm: firstMetadataValue(page => page.realm), + url: firstMetadataValue(page => page.url), + parent: firstMetadataValue(page => 'parent' in page ? page.parent : undefined), + deprecated: firstMetadataValue(page => page.deprecated), + }); + } + } + + private writePlannedClasses(filePath: string) { let api = ''; + const plans = [...this.plannedClasses.values()] + .filter(plan => plan.outputFilePath === filePath) + .sort((a, b) => a.name.localeCompare(b.name)); + + for (const plan of plans) { + const classFields = plan.fields.map(field => this.writeStructField(field)).join(''); + api += this.writeClassStart( + plan.name, + plan.realm, + plan.url, + plan.parent, + plan.deprecated, + plan.description, + classFields, + true, + ); + } + + return api; + } + + public makeApiFromPages(pages: IndexedWikiPage[], filePath?: string) { + let api = filePath ? this.writePlannedClasses(filePath) : ''; pages.sort((a, b) => a.index - b.index); @@ -519,6 +679,10 @@ export class GluaApiWriter { const usedOverrides = new Set(); const moduleFileByName = new Map(); + this.writtenClasses.clear(); + this.writtenLibraryGlobals.clear(); + this.collectClassPlans(); + for (const [filePath, pages] of this.files) { const baseName = filePath.split(/[\\/]/).pop() ?? ''; if (baseName.endsWith('.lua')) { @@ -532,13 +696,19 @@ export class GluaApiWriter { // Process module files first so that class overrides with corresponding wiki // pages are emitted inline (via writeClassStart) alongside their methods. - this.files.forEach((pages: IndexedWikiPage[], filePath: string) => { - let api = this.makeApiFromPages(pages); + for (const [filePath, pages] of [...this.files.entries()].sort(([a], [b]) => a.localeCompare(b))) { + let api = ''; + this.currentOutputFilePath = filePath; + try { + api = this.makeApiFromPages(pages, filePath); + } finally { + this.currentOutputFilePath = undefined; + } if (api.length > 0) { fs.appendFileSync(filePath, '---@meta\n\n' + api); } - }); + } const orphanFunctionOverrides = new Map(); From 667bf1a614ca066fc7f9225fdc13eda350f61acf Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 12 Jul 2026 07:54:21 +0100 Subject: [PATCH 098/117] Fix panel and player annotations --- custom/Global.AccessorFunc.lua | 2 +- custom/class.VoiceNotify.lua | 9 +++++++++ custom/player.GetBySteamID.lua | 6 ++++++ custom/player.GetBySteamID64.lua | 6 ++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 custom/class.VoiceNotify.lua create mode 100644 custom/player.GetBySteamID.lua create mode 100644 custom/player.GetBySteamID64.lua diff --git a/custom/Global.AccessorFunc.lua b/custom/Global.AccessorFunc.lua index 3371b5da..6317474f 100644 --- a/custom/Global.AccessorFunc.lua +++ b/custom/Global.AccessorFunc.lua @@ -3,7 +3,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.AccessorFunc ----@accessorfunc 2 +---@accessorfunc 3 ---@param tab table The table to add the accessor functions to. ---@param key any The key of the table to be get/set. ---@param name string The name of the functions (will be prefixed with Get and Set). diff --git a/custom/class.VoiceNotify.lua b/custom/class.VoiceNotify.lua new file mode 100644 index 00000000..db26a538 --- /dev/null +++ b/custom/class.VoiceNotify.lua @@ -0,0 +1,9 @@ +---@class VoiceNotify : DPanel +---@field LabelName DLabel +---@field Avatar AvatarImage +---@field Color Color +---@field ply Player +local VoiceNotify = {} + +---@param ply Player +function VoiceNotify:Setup(ply) end diff --git a/custom/player.GetBySteamID.lua b/custom/player.GetBySteamID.lua new file mode 100644 index 00000000..ad91f9c9 --- /dev/null +++ b/custom/player.GetBySteamID.lua @@ -0,0 +1,6 @@ +---Gets the player with the specified SteamID. +---@realm shared +---@source https://wiki.facepunch.com/gmod/player.GetBySteamID +---@param steamID string The Player:SteamID to find the player by. +---@return Player|false # Player if one is found, `false` otherwise. +function player.GetBySteamID(steamID) end diff --git a/custom/player.GetBySteamID64.lua b/custom/player.GetBySteamID64.lua new file mode 100644 index 00000000..96383ee4 --- /dev/null +++ b/custom/player.GetBySteamID64.lua @@ -0,0 +1,6 @@ +---Gets the player with the specified SteamID64. +---@realm shared +---@source https://wiki.facepunch.com/gmod/player.GetBySteamID64 +---@param steamID64 string The Player:SteamID64 to find the player by. +---@return Player|false # Player if one is found, `false` otherwise. +function player.GetBySteamID64(steamID64) end From c52bdb4bf9af722029aaf0bea20619af076f7e61 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:01:52 +0100 Subject: [PATCH 099/117] Annotate VGUI parent relationships --- custom/Panel.Add.lua | 1 + custom/vgui.Create.lua | 1 + custom/vgui.CreateX.lua | 1 + 3 files changed, 3 insertions(+) diff --git a/custom/Panel.Add.lua b/custom/Panel.Add.lua index 1f4eec89..f1784cf2 100644 --- a/custom/Panel.Add.lua +++ b/custom/Panel.Add.lua @@ -7,6 +7,7 @@ ---@overload fun(self: Panel, panelTable: table): Panel # Creates a panel from a PANEL table and parents it to this panel. ---@overload fun(self: Panel, className: `T`, parent: Panel): T # Creates a panel by class name with an explicit parent. ---@[call_arg("gmod.vgui_panel", "reference")] +---@[call_arg("gmod.vgui_panel", "parent_self")] ---@param className `T` The panel class name to create and add. ---@return (instance) T # The created panel. function Panel:Add(className) end diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua index 06b64501..1fe59ba3 100644 --- a/custom/vgui.Create.lua +++ b/custom/vgui.Create.lua @@ -12,6 +12,7 @@ --- --- New panels can be registered via vgui.Register --- +---@[call_arg("gmod.vgui_panel", "parent")] ---@param parent Panel? Panel to parent to. ---@param name string? Custom name of the created panel for scripting/debugging purposes. Can be retrieved with Panel:GetName. ---@return (instance) T? #The created panel, or `nil` if creation failed for whatever reason. diff --git a/custom/vgui.CreateX.lua b/custom/vgui.CreateX.lua index 4cff4a81..18ad3192 100644 --- a/custom/vgui.CreateX.lua +++ b/custom/vgui.CreateX.lua @@ -6,6 +6,7 @@ ---@generic T : Panel ---@[call_arg("gmod.vgui_panel", "reference")] ---@param class `T` Class of the panel to create +---@[call_arg("gmod.vgui_panel", "parent")] ---@param parent? Panel If specified, parents created panel to given one ---@param name? string Name of the created panel ---@return (instance) T # Created panel From 68d494aa0ddf51486258de1b727d01f86f7d8487 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Tue, 14 Jul 2026 20:04:41 +0100 Subject: [PATCH 100/117] Remove self-reference --- __tests__/custom-class-override.spec.ts | 27 +++++++++++++++++++++++++ src/api-writer/glua-api-writer.ts | 7 ++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/__tests__/custom-class-override.spec.ts b/__tests__/custom-class-override.spec.ts index b8d169e8..fdde7735 100644 --- a/__tests__/custom-class-override.spec.ts +++ b/__tests__/custom-class-override.spec.ts @@ -187,4 +187,31 @@ describe('Custom class overrides emission', () => { expect(ownerOutput).toContain('---@field Dock number'); expect((`${aliasOutput}\n${ownerOutput}`.match(/---@class \(partial\) Panel/g) ?? [])).toHaveLength(1); }); + + test('alias metadata cannot make its canonical class inherit from itself', () => { + const writer = new GluaApiWriter(tmpDir); + const aliasFile = path.join(tmpDir, 'aaa.lua'); + const ownerFile = path.join(tmpDir, 'panel.lua'); + + writer.addOverride('class.Panel', '---@class Panel\nPanel = Panel or {}\n'); + writer.writePages([{ + type: 'class', + name: 'PANEL', + address: 'PANEL_Hooks', + parent: 'Panel', + description: 'Alias hook surface.', + }], aliasFile, 0); + writer.writePages([{ + type: 'class', + name: 'Panel', + address: 'Panel', + description: 'Canonical panel.', + }], ownerFile, 1); + + writer.writeToDisk(); + + const ownerOutput = fs.readFileSync(ownerFile, 'utf8'); + expect(ownerOutput).toContain('---@class Panel\n'); + expect(ownerOutput).not.toContain('---@class Panel : Panel'); + }); }); diff --git a/src/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts index 85b5dcfc..b1990e57 100644 --- a/src/api-writer/glua-api-writer.ts +++ b/src/api-writer/glua-api-writer.ts @@ -621,7 +621,12 @@ export class GluaApiWriter { description: firstMetadataValue(page => page.description), realm: firstMetadataValue(page => page.realm), url: firstMetadataValue(page => page.url), - parent: firstMetadataValue(page => 'parent' in page ? page.parent : undefined), + parent: firstMetadataValue(page => { + const parent = 'parent' in page ? page.parent : undefined; + return parent && this.resolveToCanonicalClassName(parent) !== canonicalClassName + ? parent + : undefined; + }), deprecated: firstMetadataValue(page => page.deprecated), }); } From 4839c71e40cb0bf8ea2014acd57a443d50376143 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Wed, 15 Jul 2026 20:52:24 +0100 Subject: [PATCH 101/117] Add base entities --- custom/class.base_anim.lua | 4 ++++ custom/class.base_brush.lua | 4 ++++ custom/class.base_entity.lua | 4 ++++ custom/class.base_filter.lua | 4 ++++ custom/class.base_nextbot.lua | 4 ++++ custom/class.base_point.lua | 4 ++++ 6 files changed, 24 insertions(+) create mode 100644 custom/class.base_anim.lua create mode 100644 custom/class.base_brush.lua create mode 100644 custom/class.base_entity.lua create mode 100644 custom/class.base_filter.lua create mode 100644 custom/class.base_nextbot.lua create mode 100644 custom/class.base_point.lua diff --git a/custom/class.base_anim.lua b/custom/class.base_anim.lua new file mode 100644 index 00000000..97c78874 --- /dev/null +++ b/custom/class.base_anim.lua @@ -0,0 +1,4 @@ +---Base scripted animated entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_anim.lua +---@class base_anim : base_entity +local base_anim = {} diff --git a/custom/class.base_brush.lua b/custom/class.base_brush.lua new file mode 100644 index 00000000..82ac4cc4 --- /dev/null +++ b/custom/class.base_brush.lua @@ -0,0 +1,4 @@ +---Base scripted brush entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_brush.lua +---@class base_brush : base_entity +local base_brush = {} diff --git a/custom/class.base_entity.lua b/custom/class.base_entity.lua new file mode 100644 index 00000000..884bc7a4 --- /dev/null +++ b/custom/class.base_entity.lua @@ -0,0 +1,4 @@ +---Root scripted entity base shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_entity/shared.lua +---@class base_entity : Entity +local base_entity = {} diff --git a/custom/class.base_filter.lua b/custom/class.base_filter.lua new file mode 100644 index 00000000..aa7cd9d8 --- /dev/null +++ b/custom/class.base_filter.lua @@ -0,0 +1,4 @@ +---Base scripted filter entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_filter.lua +---@class base_filter : base_entity +local base_filter = {} diff --git a/custom/class.base_nextbot.lua b/custom/class.base_nextbot.lua new file mode 100644 index 00000000..219cfd9d --- /dev/null +++ b/custom/class.base_nextbot.lua @@ -0,0 +1,4 @@ +---Base scripted NextBot entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_nextbot/shared.lua +---@class base_nextbot : NextBot +local base_nextbot = {} diff --git a/custom/class.base_point.lua b/custom/class.base_point.lua new file mode 100644 index 00000000..47cf9552 --- /dev/null +++ b/custom/class.base_point.lua @@ -0,0 +1,4 @@ +---Base scripted point entity shipped by the base gamemode. +---@source garrysmod/gamemodes/base/entities/entities/base_point.lua +---@class base_point : base_entity +local base_point = {} From b463032969e35304cf26ca67a0e77c0327362eb8 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 16 Jul 2026 05:16:40 +0100 Subject: [PATCH 102/117] Fix / add various annotations --- __tests__/custom-annotations.spec.ts | 55 +++++++++++++++++++++++++++- custom/DForm.TextEntry.lua | 1 + custom/DPanelList.SortByMember.lua | 7 ++++ custom/DTree.AddNode.lua | 8 ++++ custom/DTree_Node.AddNode.lua | 8 ++++ custom/Tool.GetSWEP.lua | 6 +++ custom/Tool.GetWeapon.lua | 2 +- custom/Weapon.CheckLimit.lua | 6 +++ custom/class.Tool.lua | 6 +-- custom/vgui.CreateFromTable.lua | 2 +- 10 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 custom/DPanelList.SortByMember.lua create mode 100644 custom/DTree.AddNode.lua create mode 100644 custom/DTree_Node.AddNode.lua create mode 100644 custom/Tool.GetSWEP.lua create mode 100644 custom/Weapon.CheckLimit.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index a15273a0..4b5fada4 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -59,15 +59,22 @@ describe('custom and plugin annotation smoke checks', () => { ['DDragBase.DropAction_Normal.lua', 'ddragbase.lua'], ['DDragBase.DropAction_Simple.lua', 'ddragbase.lua'], ['DFileBrowser.SetOpen.lua', 'dfilebrowser.lua'], + ['DForm.TextEntry.lua', 'dform.lua'], ['DImage.SetMatName.lua', 'dimage.lua'], ['DMenu.SetOpenSubMenu.lua', 'dmenu.lua'], ['DPanelList.Clear.lua', 'dpanellist.lua'], + ['DPanelList.SortByMember.lua', 'dpanellist.lua'], + ['DTree.AddNode.lua', 'dtree.lua'], + ['DTree_Node.AddNode.lua', 'dtree_node.lua'], ['Panel.PerformLayout.lua', 'panel.lua'], ['TOOL.BuildCPanel.lua', 'tool.lua'], ['TOOL.Deploy.lua', 'tool.lua'], ['TOOL.Holster.lua', 'tool.lua'], + ['Tool.GetSWEP.lua', 'tool.lua'], + ['Tool.GetWeapon.lua', 'tool.lua'], ['class.Weapon.lua', 'weapon.lua'], ['Weapon.GetToolObject.lua', 'weapon.lua'], + ['Weapon.CheckLimit.lua', 'weapon.lua'], ['constraint.Elastic.lua', 'constraint.lua'], ['constraint.Weld.lua', 'constraint.lua'], ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], @@ -231,12 +238,58 @@ describe('custom and plugin annotation smoke checks', () => { expect(createFromTableBlock).toBeDefined(); expect(createFromTableBlock).toContain('---@param metatable T? Your PANEL table.'); - expect(createFromTableBlock).toContain('---@return (instance) Panel?'); + expect(createFromTableBlock).toContain('---@return (instance) T?'); expect(getControlTableBlock).toBeDefined(); expect(getControlTableBlock).toContain('---@return (definition) `T`?'); }); + test('base Lua VGUI and tool overrides preserve concrete runtime types', () => { + const dtreeLua = readOutput('dtree.lua'); + const dtreeNodeLua = readOutput('dtree_node.lua'); + const dformLua = readOutput('dform.lua'); + const dpanelListLua = readOutput('dpanellist.lua'); + const toolLua = readOutput('tool.lua'); + const weaponLua = readOutput('weapon.lua'); + + const dtreeAddNode = dtreeLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DTree:AddNode[\s\S]*?function DTree:AddNode\(name, icon\) end/, + )?.[0]; + const nodeAddNode = dtreeNodeLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DTree_Node:AddNode[\s\S]*?function DTree_Node:AddNode\(name, icon\) end/, + )?.[0]; + const textEntry = dformLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DForm:TextEntry[\s\S]*?function DForm:TextEntry\(label, convar\) end/, + )?.[0]; + const sortByMember = dpanelListLua.match( + /---@source https:\/\/github\.com\/Facepunch\/garrysmod\/blob\/master\/garrysmod\/lua\/vgui\/dpanellist\.lua#L403[\s\S]*?function DPanelList:SortByMember\(key, desc\) end/, + )?.[0]; + const getSwep = toolLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/Tool:GetSWEP[\s\S]*?function Tool:GetSWEP\(\) end/, + )?.[0]; + const getWeapon = toolLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/Tool:GetWeapon[\s\S]*?function Tool:GetWeapon\(\) end/, + )?.[0]; + const checkLimit = weaponLua.match( + /---@source https:\/\/github\.com\/Facepunch\/garrysmod\/blob\/master\/garrysmod\/gamemodes\/sandbox\/entities\/weapons\/gmod_tool\/shared\.lua#L69[\s\S]*?function gmod_tool:CheckLimit\(limitName\) end/, + )?.[0]; + + expect(dtreeAddNode).toContain('---@return DTree_Node'); + expect(nodeAddNode).toContain('---@return DTree_Node'); + expect(textEntry).toContain('---@return DTextEntry'); + expect(textEntry).toContain('---@return DLabel'); + expect(textEntry!.indexOf('---@return DTextEntry')).toBeLessThan( + textEntry!.indexOf('---@return DLabel'), + ); + expect(sortByMember).toBeDefined(); + expect(sortByMember).toContain('---@param key any'); + expect(sortByMember).toContain('---@param desc? boolean'); + expect(getSwep).toContain('---@return gmod_tool'); + expect(getWeapon).toContain('---@return gmod_tool'); + expect(checkLimit).toContain('---@param limitName string'); + expect(checkLimit).toContain('---@return boolean'); + }); + test('DermaAnimation class fragment matches the Lua runtime state shape', () => { const customClasses = readOutput('custom_classes.lua'); const dermaAnimationBlock = customClasses.match( diff --git a/custom/DForm.TextEntry.lua b/custom/DForm.TextEntry.lua index ea9c0320..52b2fd74 100644 --- a/custom/DForm.TextEntry.lua +++ b/custom/DForm.TextEntry.lua @@ -4,4 +4,5 @@ ---@param label string The label for the text entry. ---@param convar? string The convar to link the text entry to. ---@return DTextEntry # The created DTextEntry +---@return DLabel # The label created for the text entry. function DForm:TextEntry(label, convar) end diff --git a/custom/DPanelList.SortByMember.lua b/custom/DPanelList.SortByMember.lua new file mode 100644 index 00000000..32b6afb4 --- /dev/null +++ b/custom/DPanelList.SortByMember.lua @@ -0,0 +1,7 @@ +---Sorts the list's items by a table member. +---@realm client +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/vgui/dpanellist.lua#L403 +---@param key any The member key to sort by. +---@param desc? boolean Whether to sort in descending order. Defaults to true. +function DPanelList:SortByMember(key, desc) end diff --git a/custom/DTree.AddNode.lua b/custom/DTree.AddNode.lua new file mode 100644 index 00000000..b1309e45 --- /dev/null +++ b/custom/DTree.AddNode.lua @@ -0,0 +1,8 @@ +---Adds a node to the tree. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree:AddNode +---@param name string Name of the node. +---@param icon? string The icon shown next to the node. +---@return DTree_Node # The created node. +function DTree:AddNode(name, icon) end diff --git a/custom/DTree_Node.AddNode.lua b/custom/DTree_Node.AddNode.lua new file mode 100644 index 00000000..c9acc811 --- /dev/null +++ b/custom/DTree_Node.AddNode.lua @@ -0,0 +1,8 @@ +---Adds a child node to this tree node. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree_Node:AddNode +---@param name string Name of the node. +---@param icon? string The icon shown next to the node. +---@return DTree_Node # The created node. +function DTree_Node:AddNode(name, icon) end diff --git a/custom/Tool.GetSWEP.lua b/custom/Tool.GetSWEP.lua new file mode 100644 index 00000000..bf85585b --- /dev/null +++ b/custom/Tool.GetSWEP.lua @@ -0,0 +1,6 @@ +---Returns the Tool Gun (`gmod_tool`) Scripted Weapon. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Tool:GetSWEP +---@return gmod_tool # The tool gun weapon. +---@deprecated Use Tool:GetWeapon instead. +function Tool:GetSWEP() end diff --git a/custom/Tool.GetWeapon.lua b/custom/Tool.GetWeapon.lua index a28d8d04..fe5e2628 100644 --- a/custom/Tool.GetWeapon.lua +++ b/custom/Tool.GetWeapon.lua @@ -4,5 +4,5 @@ --- ToolObj:Create() initialising SWEP to nil. ---@realm shared ---@source https://wiki.facepunch.com/gmod/Tool:GetWeapon ----@return Weapon # The tool gun weapon (`gmod_tool`). Always valid after Init. +---@return gmod_tool # The tool gun weapon. Always valid after Init. function Tool:GetWeapon() end diff --git a/custom/Weapon.CheckLimit.lua b/custom/Weapon.CheckLimit.lua new file mode 100644 index 00000000..787f39a0 --- /dev/null +++ b/custom/Weapon.CheckLimit.lua @@ -0,0 +1,6 @@ +---Checks whether the tool gun's owner can create another object of the given limit type. +---@realm shared +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/entities/weapons/gmod_tool/shared.lua#L69 +---@param limitName string The sandbox limit name to check. +---@return boolean # Whether another object can be created. +function gmod_tool:CheckLimit(limitName) end diff --git a/custom/class.Tool.lua b/custom/class.Tool.lua index 779e9ea3..73dc2e18 100644 --- a/custom/class.Tool.lua +++ b/custom/class.Tool.lua @@ -22,8 +22,8 @@ ---@class Tool ---@field Mode string The tool mode string (e.g. "weld", "balloon"). ----@field SWEP Weapon The weapon entity this tool belongs to. ----@field Weapon Weapon Alias for SWEP; the weapon entity this tool belongs to. +---@field SWEP gmod_tool The tool gun weapon entity this tool belongs to. +---@field Weapon gmod_tool Alias for SWEP; the tool gun weapon entity this tool belongs to. ---@field Owner Player The player who owns this tool. ---@field Objects ToolObjects Array of stored constraint objects indexed 1-based. ---@field Stage number The current stage of the tool. @@ -52,7 +52,7 @@ Tool = Tool or {} ---Returns the Tool Gun (`gmod_tool`) Scripted Weapon. Never nil at runtime after Init. ----@return Weapon # The tool gun weapon. (`gmod_tool`) +---@return gmod_tool # The tool gun weapon. function Tool:GetWeapon() end ---Initializes a ghost entity from the given entity's model/pos/angles. diff --git a/custom/vgui.CreateFromTable.lua b/custom/vgui.CreateFromTable.lua index d9637881..fe6defbf 100644 --- a/custom/vgui.CreateFromTable.lua +++ b/custom/vgui.CreateFromTable.lua @@ -8,5 +8,5 @@ ---@param metatable T? Your PANEL table. ---@param parent? Panel Which panel to parent the newly created panel to. ---@param name? string Custom name of the created panel for scripting/debugging purposes. Can be retrieved with Panel:GetName. ----@return (instance) Panel? # The created panel, or `nil` if creation failed for whatever reason. +---@return (instance) T? # The created panel, or `nil` if creation failed for whatever reason. function vgui.CreateFromTable(metatable, parent, name) end From 63c3e1c934317a3b2c49c4781205b4032a9645b7 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:46:27 +0100 Subject: [PATCH 103/117] Add CompileFile annotations --- custom/Global.CompileFile.lua | 8 ++++++++ custom/Global.setfenv.lua | 10 ++++++++++ 2 files changed, 18 insertions(+) create mode 100644 custom/Global.CompileFile.lua create mode 100644 custom/Global.setfenv.lua diff --git a/custom/Global.CompileFile.lua b/custom/Global.CompileFile.lua new file mode 100644 index 00000000..a120d437 --- /dev/null +++ b/custom/Global.CompileFile.lua @@ -0,0 +1,8 @@ +---Attempts to compile the given file. If successful, returns a function that can be called to perform the actual execution of the script. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Global.CompileFile +---@[call_arg("gmod.load", "compilefile")] +---@param path string Path to the file, relative to the `garrysmod/lua/` directory. +---@param showError? boolean Decides whether or not a non-halting error should be thrown on compile failure. +---@return function? # The function which executes the script, or nil on failure. +function _G.CompileFile(path, showError) end diff --git a/custom/Global.setfenv.lua b/custom/Global.setfenv.lua new file mode 100644 index 00000000..1a0141cd --- /dev/null +++ b/custom/Global.setfenv.lua @@ -0,0 +1,10 @@ +---Sets the environment for a function or a stack level. Can be used to sandbox code. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/Global.setfenv +---@[call_arg("gmod.environment", "target")] +---@param location function|integer The function to set the environment for, or a number representing stack level. +---@[call_arg("gmod.environment", "environment")] +---@param environment table Table to be used as the the environment. +---@return function? # The function passed, otherwise nil. +function _G.setfenv(location, environment) end From 92b73f0580ef25678a9ee7bb0d63ee12c87cc8fe Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:46:44 +0100 Subject: [PATCH 104/117] Fix various annotations --- custom/DForm.ComboBox.lua | 9 +++++++++ custom/Panel.SelectAllText.lua | 7 +++++++ custom/debug.getlocal.lua | 14 ++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 custom/DForm.ComboBox.lua create mode 100644 custom/Panel.SelectAllText.lua create mode 100644 custom/debug.getlocal.lua diff --git a/custom/DForm.ComboBox.lua b/custom/DForm.ComboBox.lua new file mode 100644 index 00000000..f37d22dd --- /dev/null +++ b/custom/DForm.ComboBox.lua @@ -0,0 +1,9 @@ +---Adds a combo box to the form. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DForm:ComboBox +---@param title string Text to the left of the combo box. +---@param convar? string Console variable to change when the user selects something from the dropdown. +---@return DComboBox # The created DComboBox +---@return DLabel # The created DLabel +function DForm:ComboBox(title, convar) end diff --git a/custom/Panel.SelectAllText.lua b/custom/Panel.SelectAllText.lua new file mode 100644 index 00000000..62519be3 --- /dev/null +++ b/custom/Panel.SelectAllText.lua @@ -0,0 +1,7 @@ +---Selects all text in a text-based panel. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/Panel:SelectAllText +---@param resetCursorPos? boolean Whether to reset the cursor position. +---@deprecated Duplicate of Panel:SelectAll. +function Panel:SelectAllText(resetCursorPos) end diff --git a/custom/debug.getlocal.lua b/custom/debug.getlocal.lua new file mode 100644 index 00000000..04fd6fe0 --- /dev/null +++ b/custom/debug.getlocal.lua @@ -0,0 +1,14 @@ +---Returns the name and value of a local variable at a stack level or in a function. +--- +---The thread argument is optional. An out-of-range stack level or local index returns nil. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/debug.getlocal +---@overload fun(level: integer|function, index: integer): string?, any +---@param thread thread +---@param level integer|function +---@param index integer +---@return string? +---@return any +---@nodiscard +function debug.getlocal(thread, level, index) end From b0482d3841afe2f5f6d19be96f45c8859b1957ea Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Thu, 16 Jul 2026 13:53:02 +0100 Subject: [PATCH 105/117] Add debug.sethook override --- custom/debug.sethook.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 custom/debug.sethook.lua diff --git a/custom/debug.sethook.lua b/custom/debug.sethook.lua new file mode 100644 index 00000000..139bea2d --- /dev/null +++ b/custom/debug.sethook.lua @@ -0,0 +1,11 @@ +---Sets a Lua debug hook, or removes the current hook when called without arguments. +---@realm shared +---@realm menu +---@source https://wiki.facepunch.com/gmod/debug.sethook +---@overload fun() +---@overload fun(hook: function, mask: string, count?: number) +---@param thread thread +---@param hook function +---@param mask string +---@param count? number +function debug.sethook(thread, hook, mask, count) end From a1262cb2f1ab166c688d2a155cbdf6897a3eabd1 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 19 Jul 2026 21:21:26 +0100 Subject: [PATCH 106/117] Add isentity type guard --- __tests__/custom-annotations.spec.ts | 4 +++- custom/{Global.isentity.lua => Global.IsEntity.lua} | 0 2 files changed, 3 insertions(+), 1 deletion(-) rename custom/{Global.isentity.lua => Global.IsEntity.lua} (100%) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 4b5fada4..14468d64 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -79,6 +79,7 @@ describe('custom and plugin annotation smoke checks', () => { ['constraint.Weld.lua', 'constraint.lua'], ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], ['Global.collectgarbage.lua', 'global.lua'], + ['Global.IsEntity.lua', 'global.lua'], ['Weapon.GetToolObject.lua', 'weapon.lua'], ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], ]; @@ -351,10 +352,11 @@ describe('custom and plugin annotation smoke checks', () => { }); test('entity predicate overrides keep lowercase and legacy pages separate', () => { - const isEntityOverride = readCustom('Global.isentity.lua'); + const isEntityOverride = readCustom('Global.IsEntity.lua'); const legacyIsEntityOverride = readCustom('Global.IsEntity.legacy..lua'); expect(isEntityOverride).toContain('---@source https://wiki.facepunch.com/gmod/Global.isentity'); + expect(isEntityOverride).toContain('---@return TypeGuard isEntity'); expect(isEntityOverride).toContain('function _G.isentity(var) end'); expect(isEntityOverride).not.toContain('Global.IsEntity'); diff --git a/custom/Global.isentity.lua b/custom/Global.IsEntity.lua similarity index 100% rename from custom/Global.isentity.lua rename to custom/Global.IsEntity.lua From 106d1766bbc4e25ed7f881088cfe28875d7ae7cb Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 20 Jul 2026 06:47:25 +0100 Subject: [PATCH 107/117] Fix panel hooks --- __tests__/api-writer/glua-api-writer.spec.ts | 24 ++++++++++++++ __tests__/custom-annotations.spec.ts | 21 ++++++++++++ .../scrapers/wiki-page-markup-scraper.spec.ts | 33 ++++++++++++++++++- custom/DTree.OnNodeSelected.lua | 8 +++++ custom/DTree_Node.OnNodeSelected.lua | 10 ++++-- src/api-writer/glua-api-writer.ts | 15 ++++++--- src/scrapers/wiki-page-markup-scraper.ts | 19 ++++++++++- 7 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 custom/DTree.OnNodeSelected.lua diff --git a/__tests__/api-writer/glua-api-writer.spec.ts b/__tests__/api-writer/glua-api-writer.spec.ts index cc758d75..698677f0 100644 --- a/__tests__/api-writer/glua-api-writer.spec.ts +++ b/__tests__/api-writer/glua-api-writer.spec.ts @@ -29,6 +29,30 @@ describe('GLua API Writer', () => { expect(api).toContain('function GM:PlayerInitialSpawn(player, transition) end'); }); + it('emits panel hooks as panel-owned callback contracts', () => { + const markup = ` + + This function is called when a node within a tree is selected. + Client and Menu + + The node that was selected. + +`; + const response = { + url: 'https://wiki.facepunch.com/gmod/DTree:OnNodeSelected?format=text', + }; + const [page] = new WikiPageMarkupScraper(response.url).getScrapeCallback()(response, markup) as WikiPage[]; + const writer = new GluaApiWriter(); + writer.writePages([page], mockFilePath); + const api = writer.makeApiFromPages(writer.getPages(mockFilePath)); + + expect(api).toContain('---@hook OnNodeSelected'); + expect(api).toContain('---@realm client'); + expect(api).toContain('---@realm menu'); + expect(api).toContain('---@param node Panel The node that was selected.'); + expect(api).toContain('function DTree:OnNodeSelected(node) end'); + }); + it('should emit source and realm annotations when present', () => { const writer = new GluaApiWriter(); const api = writer.writePage({ diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 14468d64..80686942 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -65,8 +65,10 @@ describe('custom and plugin annotation smoke checks', () => { ['DPanelList.Clear.lua', 'dpanellist.lua'], ['DPanelList.SortByMember.lua', 'dpanellist.lua'], ['DTree.AddNode.lua', 'dtree.lua'], + ['DTree.OnNodeSelected.lua', 'dtree.lua'], ['DTree_Node.AddNode.lua', 'dtree_node.lua'], ['Panel.PerformLayout.lua', 'panel.lua'], + ['DTree_Node.OnNodeSelected.lua', 'dtree_node.lua'], ['TOOL.BuildCPanel.lua', 'tool.lua'], ['TOOL.Deploy.lua', 'tool.lua'], ['TOOL.Holster.lua', 'tool.lua'], @@ -248,6 +250,7 @@ describe('custom and plugin annotation smoke checks', () => { test('base Lua VGUI and tool overrides preserve concrete runtime types', () => { const dtreeLua = readOutput('dtree.lua'); const dtreeNodeLua = readOutput('dtree_node.lua'); + const panelLua = readOutput('panel.lua'); const dformLua = readOutput('dform.lua'); const dpanelListLua = readOutput('dpanellist.lua'); const toolLua = readOutput('tool.lua'); @@ -259,6 +262,12 @@ describe('custom and plugin annotation smoke checks', () => { const nodeAddNode = dtreeNodeLua.match( /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DTree_Node:AddNode[\s\S]*?function DTree_Node:AddNode\(name, icon\) end/, )?.[0]; + const dtreeOnNodeSelected = dtreeLua.match( + /---@hook OnNodeSelected[\s\S]*?function DTree:OnNodeSelected\(node\) end/, + )?.[0]; + const nodeOnNodeSelected = dtreeNodeLua.match( + /---@hook OnNodeSelected[\s\S]*?function DTree_Node:OnNodeSelected\(node\) end/, + )?.[0]; const textEntry = dformLua.match( /---@source https:\/\/wiki\.facepunch\.com\/gmod\/DForm:TextEntry[\s\S]*?function DForm:TextEntry\(label, convar\) end/, )?.[0]; @@ -277,6 +286,18 @@ describe('custom and plugin annotation smoke checks', () => { expect(dtreeAddNode).toContain('---@return DTree_Node'); expect(nodeAddNode).toContain('---@return DTree_Node'); + expect(dtreeOnNodeSelected).toBeDefined(); + expect(dtreeOnNodeSelected).toContain('---@realm client'); + expect(dtreeOnNodeSelected).toContain('---@realm menu'); + expect(dtreeOnNodeSelected).toContain('---@source https://wiki.facepunch.com/gmod/DTree:OnNodeSelected'); + expect(dtreeOnNodeSelected).toContain('---@param node DTree_Node The node that was selected.'); + expect(nodeOnNodeSelected).toBeDefined(); + expect(nodeOnNodeSelected).toContain('---@source https://wiki.facepunch.com/gmod/DTree_Node:OnNodeSelected'); + expect(nodeOnNodeSelected).toContain('function DTree_Node:OnNodeSelected(node) end'); + expect(nodeOnNodeSelected).toContain('---@realm client'); + expect(nodeOnNodeSelected).toContain('---@realm menu'); + expect(nodeOnNodeSelected).toContain('---@param node DTree_Node'); + expect(panelLua).not.toContain('Panel.propPanel'); expect(textEntry).toContain('---@return DTextEntry'); expect(textEntry).toContain('---@return DLabel'); expect(textEntry!.indexOf('---@return DTextEntry')).toBeLessThan( diff --git a/__tests__/scrapers/wiki-page-markup-scraper.spec.ts b/__tests__/scrapers/wiki-page-markup-scraper.spec.ts index 9aec9230..53c354e9 100644 --- a/__tests__/scrapers/wiki-page-markup-scraper.spec.ts +++ b/__tests__/scrapers/wiki-page-markup-scraper.spec.ts @@ -1,6 +1,6 @@ import { markup as classFunctionMarkup, json as classFunctionJson } from '../test-data/offline-sites/gmod-wiki/class-function-weapon-allowsautoswitchto'; import { markup as libraryFunctionMarkup, json as libraryFunctionJson } from '../test-data/offline-sites/gmod-wiki/library-function-ai-getscheduleid'; -import { ClassFunction, Enum, HookFunction, LibraryFunction, Struct, WikiPageMarkupScraper } from '../../src/scrapers/wiki-page-markup-scraper'; +import { ClassFunction, Enum, HookFunction, LibraryFunction, Struct, WikiPage, WikiPageMarkupScraper } from '../../src/scrapers/wiki-page-markup-scraper'; import { markup as hookMarkup, json as hookJson } from '../test-data/offline-sites/gmod-wiki/hook-player-initial-spawn'; import { markup as structMarkup, json as structJson } from '../test-data/offline-sites/gmod-wiki/struct-ang-pos'; import { markup as enumMarkup, json as enumJson } from '../test-data/offline-sites/gmod-wiki/enums-use'; @@ -89,6 +89,37 @@ describe('GMod Wiki Page Markup Parse', () => { expect(scrapeCallback(responseMock, hookMarkup)).toEqual([hookJson]); }); + it('parses panel hooks with their panel owner and callback signature', () => { + const markup = ` + + This function is called when a node within a tree is selected. + Client and Menu + + The node that was selected. + +`; + const responseMock = { + url: 'https://wiki.facepunch.com/gmod/DTree:OnNodeSelected?format=text', + }; + const [page] = new WikiPageMarkupScraper(responseMock.url).getScrapeCallback()(responseMock, markup) as WikiPage[]; + + expect(page).toMatchObject({ + type: 'panelhook', + parent: 'DTree', + name: 'OnNodeSelected', + address: 'DTree:OnNodeSelected', + realm: 'client and menu', + isPanelHook: 'yes', + arguments: [{ + args: [{ + name: 'node', + type: 'Panel', + description: 'The node that was selected.', + }], + }], + }); + }); + it('should be able to parse a enum markup', async () => { fetchMock.mockResponseOnce(enumMarkup); diff --git a/custom/DTree.OnNodeSelected.lua b/custom/DTree.OnNodeSelected.lua new file mode 100644 index 00000000..95e47544 --- /dev/null +++ b/custom/DTree.OnNodeSelected.lua @@ -0,0 +1,8 @@ +---This function is called when a node within a tree is selected. +--- +---@hook OnNodeSelected +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DTree:OnNodeSelected +---@param node DTree_Node The node that was selected. +function DTree:OnNodeSelected(node) end diff --git a/custom/DTree_Node.OnNodeSelected.lua b/custom/DTree_Node.OnNodeSelected.lua index f3cba300..6475b588 100644 --- a/custom/DTree_Node.OnNodeSelected.lua +++ b/custom/DTree_Node.OnNodeSelected.lua @@ -1,5 +1,11 @@ +---**INTERNAL**: This is used internally - although you're able to use it you probably shouldn't. +--- +--- Called when this or a sub node is selected. Do not use this, it is not for override. +--- +--- Use [DTree:OnNodeSelected](https://wiki.facepunch.com/gmod/DTree:OnNodeSelected) or [DTree_Node:DoClick](https://wiki.facepunch.com/gmod/DTree_Node:DoClick) instead. +---@hook OnNodeSelected ---@realm client ---@realm menu ----@source garrysmod/lua/vgui/dtree_node.lua ----@param node Panel The selected panel. +---@source https://wiki.facepunch.com/gmod/DTree_Node:OnNodeSelected +---@param node DTree_Node function DTree_Node:OnNodeSelected(node) end diff --git a/src/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts index b1990e57..eaa52445 100644 --- a/src/api-writer/glua-api-writer.ts +++ b/src/api-writer/glua-api-writer.ts @@ -1,4 +1,4 @@ -import { ClassFunction, Enum, Function, HookFunction, LibraryFunction, TypePage, Panel, PanelFunction, Realm, Struct, StructField, WikiPage, isPanel, FunctionArgument, FunctionCallback } from '../scrapers/wiki-page-markup-scraper.js'; +import { ClassFunction, Enum, Function, HookFunction, LibraryFunction, TypePage, Panel, PanelFunction, PanelHookFunction, Realm, Struct, StructField, WikiPage, isPanel, FunctionArgument, FunctionCallback } from '../scrapers/wiki-page-markup-scraper.js'; import { indentText, wrapInComment, removeNewlines, safeFileName, toLowerCamelCase } from '../utils/string.js'; import { isClassFunction, @@ -7,6 +7,7 @@ import { isLibrary, isClass, isPanelFunction, + isPanelHookFunction, isStruct, isEnum, } from '../scrapers/wiki-page-markup-scraper.js'; @@ -213,6 +214,8 @@ export class GluaApiWriter { return this.writePanel(page); else if (isPanelFunction(page)) return this.writePanelFunction(page); + else if (isPanelHookFunction(page)) + return this.writePanelHookFunction(page); else if (isEnum(page)) return this.writeEnum(page); else if (isStruct(page)) @@ -355,6 +358,10 @@ export class GluaApiWriter { return this.writeFunctionWithOverloads(func, ':'); } + private writePanelHookFunction(func: PanelHookFunction) { + return this.writeFunctionWithOverloads(func, ':'); + } + private writeFunctionWithOverloads(func: Function, indexer: string, prefix: string = '') { let api = prefix; @@ -553,7 +560,7 @@ export class GluaApiWriter { let className: string | undefined; if (isClass(page) || isStruct(page) || isPanel(page)) className = page.name; - else if (isClassFunction(page) || isHookFunction(page) || isPanelFunction(page)) + else if (isClassFunction(page) || isHookFunction(page) || isPanelFunction(page) || isPanelHookFunction(page)) className = page.parent; if (className) @@ -564,7 +571,7 @@ export class GluaApiWriter { const relevantEntries = entries.filter(({ page }) => { const pageClassName = isClass(page) || isStruct(page) || isPanel(page) ? page.name - : isClassFunction(page) || isHookFunction(page) || isPanelFunction(page) + : isClassFunction(page) || isHookFunction(page) || isPanelFunction(page) || isPanelHookFunction(page) ? page.parent : undefined; return pageClassName !== undefined @@ -999,7 +1006,7 @@ export class GluaApiWriter { if (func.description) luaDocComment += `---${wrapInComment(func.description)}\n`; - if (isHookFunction(func)) + if (isHookFunction(func) || isPanelHookFunction(func)) luaDocComment += `---@hook ${func.name}\n`; luaDocComment += this.writeRealmAnnotations(realm); diff --git a/src/scrapers/wiki-page-markup-scraper.ts b/src/scrapers/wiki-page-markup-scraper.ts index cbc752dc..87c52637 100644 --- a/src/scrapers/wiki-page-markup-scraper.ts +++ b/src/scrapers/wiki-page-markup-scraper.ts @@ -3,7 +3,7 @@ import { deserializeXml } from '../utils/xml.js'; import { Cheerio, CheerioAPI } from 'cheerio'; import { AnyNode, Element as DOMElement } from 'domhandler'; -export type WikiFunctionType = 'panelfunc' | 'classfunc' | 'libraryfunc' | 'hook'; +export type WikiFunctionType = 'panelfunc' | 'panelhook' | 'classfunc' | 'libraryfunc' | 'hook'; export type Realm = 'menu' | 'client' | 'server' | 'shared' | 'client and menu' | 'shared and menu'; export type CommonWikiProperties = { @@ -63,6 +63,11 @@ export type PanelFunction = Function & { isPanelFunction: 'yes'; }; +export type PanelHookFunction = Function & { + type: 'panelhook'; + isPanelHook: 'yes'; +}; + export type EnumValue = { key: string; value: string; @@ -100,6 +105,7 @@ export type TypePage = CommonWikiProperties & { }; export type WikiPage = ClassFunction | LibraryFunction | HookFunction | PanelFunction | Panel | Enum | Struct | TypePage + | PanelHookFunction /** * Guards @@ -120,6 +126,10 @@ export function isPanelFunction(page: WikiPage): page is PanelFunction { return page.type === 'panelfunc'; } +export function isPanelHookFunction(page: WikiPage): page is PanelHookFunction { + return page.type === 'panelhook'; +} + export function isPanel(page: WikiPage): page is Panel { return page.type === 'panel'; } @@ -388,6 +398,7 @@ export class WikiPageMarkupScraper extends Scraper { const isLibraryFunction = mainElement.attr('type') === 'libraryfunc'; const isHookFunction = mainElement.attr('type') === 'hook'; const isPanelFunction = mainElement.attr('type') === 'panelfunc'; + const isPanelHookFunction = mainElement.attr('type') === 'panelhook'; const argumentList: FunctionArgumentList[] = []; for (const argSet of $('args')) { @@ -474,6 +485,12 @@ export class WikiPageMarkupScraper extends Scraper { type: 'panelfunc', isPanelFunction: 'yes' }; + } else if (isPanelHookFunction) { + return { + ...base, + type: 'panelhook', + isPanelHook: 'yes' + }; } } else if (isTypePage) { const $el = $('type'); From 90f79b86c27ce7d5986f046bedfe45111648aaff Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 20 Jul 2026 08:03:02 +0100 Subject: [PATCH 108/117] Add sandbox gamemode annotations --- __tests__/custom-annotations.spec.ts | 10 ++++++++++ custom/GM.AddNotify.lua | 7 +++++++ custom/Global.FixInvalidPhysicsObject.lua | 5 +++++ 3 files changed, 22 insertions(+) create mode 100644 custom/GM.AddNotify.lua create mode 100644 custom/Global.FixInvalidPhysicsObject.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 80686942..30728d9c 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -81,7 +81,9 @@ describe('custom and plugin annotation smoke checks', () => { ['constraint.Weld.lua', 'constraint.lua'], ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], ['Global.collectgarbage.lua', 'global.lua'], + ['Global.FixInvalidPhysicsObject.lua', 'global.lua'], ['Global.IsEntity.lua', 'global.lua'], + ['GM.AddNotify.lua', 'gm.lua'], ['Weapon.GetToolObject.lua', 'weapon.lua'], ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], ]; @@ -99,6 +101,14 @@ describe('custom and plugin annotation smoke checks', () => { } }); + test('sandbox overrides preserve their realm-specific declarations', () => { + const gmLua = readOutput('gm.lua'); + const globalLua = readOutput('global.lua'); + + expect(gmLua).toContain('---@realm client\n---@source sandbox/gamemode/cl_notice.lua\n---@param str string\n---@param type integer\n---@param length number\nfunction GM:AddNotify(str, type, length) end'); + expect(globalLua).toContain('---@realm server\n---@source sandbox/gamemode/commands.lua\n---@param prop Entity\nfunction _G.FixInvalidPhysicsObject(prop) end'); + }); + test('custom class fragments are included in the generated custom class bundle', () => { const customClasses = readOutput('custom_classes.lua'); const classFiles = [ diff --git a/custom/GM.AddNotify.lua b/custom/GM.AddNotify.lua new file mode 100644 index 00000000..53777c00 --- /dev/null +++ b/custom/GM.AddNotify.lua @@ -0,0 +1,7 @@ +---Displays a notification through the current gamemode. +---@realm client +---@source sandbox/gamemode/cl_notice.lua +---@param str string +---@param type integer +---@param length number +function GM:AddNotify(str, type, length) end diff --git a/custom/Global.FixInvalidPhysicsObject.lua b/custom/Global.FixInvalidPhysicsObject.lua new file mode 100644 index 00000000..14b1ec36 --- /dev/null +++ b/custom/Global.FixInvalidPhysicsObject.lua @@ -0,0 +1,5 @@ +---Attempts to correct an invalid physics object on a prop. +---@realm server +---@source sandbox/gamemode/commands.lua +---@param prop Entity +function _G.FixInvalidPhysicsObject(prop) end From 67603fe6309c4a86acb1c616a098847bce0cb0d4 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 20 Jul 2026 08:40:55 +0100 Subject: [PATCH 109/117] Fix assert and pairs --- __tests__/custom-annotations.spec.ts | 6 ++++-- custom/Global.assert.lua | 3 ++- custom/Global.pairs.lua | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 30728d9c..bdf8052a 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -80,9 +80,11 @@ describe('custom and plugin annotation smoke checks', () => { ['constraint.Elastic.lua', 'constraint.lua'], ['constraint.Weld.lua', 'constraint.lua'], ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], + ['Global.assert.lua', 'global.lua'], ['Global.collectgarbage.lua', 'global.lua'], ['Global.FixInvalidPhysicsObject.lua', 'global.lua'], ['Global.IsEntity.lua', 'global.lua'], + ['Global.pairs.lua', 'global.lua'], ['GM.AddNotify.lua', 'gm.lua'], ['Weapon.GetToolObject.lua', 'weapon.lua'], ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], @@ -102,8 +104,8 @@ describe('custom and plugin annotation smoke checks', () => { }); test('sandbox overrides preserve their realm-specific declarations', () => { - const gmLua = readOutput('gm.lua'); - const globalLua = readOutput('global.lua'); + const gmLua = readOutput('gm.lua').replace(/\r\n/g, '\n'); + const globalLua = readOutput('global.lua').replace(/\r\n/g, '\n'); expect(gmLua).toContain('---@realm client\n---@source sandbox/gamemode/cl_notice.lua\n---@param str string\n---@param type integer\n---@param length number\nfunction GM:AddNotify(str, type, length) end'); expect(globalLua).toContain('---@realm server\n---@source sandbox/gamemode/commands.lua\n---@param prop Entity\nfunction _G.FixInvalidPhysicsObject(prop) end'); diff --git a/custom/Global.assert.lua b/custom/Global.assert.lua index abb294d2..1efe7316 100644 --- a/custom/Global.assert.lua +++ b/custom/Global.assert.lua @@ -7,4 +7,5 @@ ---@param expression T # The expression to assert. ---@param ... T1... # Error Message and any arguments to return on success. ---@return std.NotNull, T1... # If successful, returns the first argument. On error, returns error message. -function _G.assert(expression, ...) end \ No newline at end of file +---@[return_alias(0)] +function _G.assert(expression, ...) end diff --git a/custom/Global.pairs.lua b/custom/Global.pairs.lua index 09a6c770..ad9242e1 100644 --- a/custom/Global.pairs.lua +++ b/custom/Global.pairs.lua @@ -8,4 +8,5 @@ ---@generic K, V, I ---@param t table | V[] | {[K]: V} # The table being iterated over. ---@return (fun(tbl: table, index: I?):K, V), table, I? # The iterator function -function _G.pairs(t) end \ No newline at end of file +---@[builtin_alias("pairs")] +function _G.pairs(t) end From 1f608e9fb9499f8d9f3cc474af6477a93e83b133 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:02:28 +0100 Subject: [PATCH 110/117] Add some missing VGUI annotations --- __tests__/custom-annotations.spec.ts | 10 ++++++++++ custom/DHorizontalScroller.AddPanel.lua | 8 ++++++++ custom/DPanelList.ScrollToChild.lua | 6 ++++++ custom/Panel.SetParent.lua | 2 ++ custom/class.DCollapsibleCategory.lua | 4 ++++ custom/class.DForm.lua | 4 ++++ custom/class.DHorizontalScroller.lua | 9 +++++++++ custom/duplicator.EntityModifiers.lua | 8 ++++++++ 8 files changed, 51 insertions(+) create mode 100644 custom/DHorizontalScroller.AddPanel.lua create mode 100644 custom/DPanelList.ScrollToChild.lua create mode 100644 custom/class.DCollapsibleCategory.lua create mode 100644 custom/class.DForm.lua create mode 100644 custom/duplicator.EntityModifiers.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index bdf8052a..685e0439 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -80,6 +80,7 @@ describe('custom and plugin annotation smoke checks', () => { ['constraint.Elastic.lua', 'constraint.lua'], ['constraint.Weld.lua', 'constraint.lua'], ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], + ['duplicator.EntityModifiers.lua', 'duplicator.lua'], ['Global.assert.lua', 'global.lua'], ['Global.collectgarbage.lua', 'global.lua'], ['Global.FixInvalidPhysicsObject.lua', 'global.lua'], @@ -384,6 +385,15 @@ describe('custom and plugin annotation smoke checks', () => { expect(weaponsGetStoredBlock).toContain('---@return (definition) `T`?'); }); + test('base registries expose their runtime call shapes', () => { + const duplicatorLua = readOutput('duplicator.lua'); + + expect(duplicatorLua).toContain( + '---@type table', + ); + expect(duplicatorLua).toContain('duplicator.EntityModifiers = {}'); + }); + test('entity predicate overrides keep lowercase and legacy pages separate', () => { const isEntityOverride = readCustom('Global.IsEntity.lua'); const legacyIsEntityOverride = readCustom('Global.IsEntity.legacy..lua'); diff --git a/custom/DHorizontalScroller.AddPanel.lua b/custom/DHorizontalScroller.AddPanel.lua new file mode 100644 index 00000000..08703a74 --- /dev/null +++ b/custom/DHorizontalScroller.AddPanel.lua @@ -0,0 +1,8 @@ +---Adds a panel to the DHorizontalScroller. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DHorizontalScroller:AddPanel +---@[call_arg("gmod.vgui_panel", "reference")] +---@[call_arg_field("gmod.vgui_panel", "parent_self", "pnlCanvas")] +---@param pnl Panel The panel to add. It will be automatically parented. +function DHorizontalScroller:AddPanel(pnl) end diff --git a/custom/DPanelList.ScrollToChild.lua b/custom/DPanelList.ScrollToChild.lua new file mode 100644 index 00000000..531ab96d --- /dev/null +++ b/custom/DPanelList.ScrollToChild.lua @@ -0,0 +1,6 @@ +---Scrolls the panel list to center a child panel vertically. +---@realm client +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/vgui/dpanellist.lua#L391 +---@param panel Panel The child panel to scroll to. +function DPanelList:ScrollToChild(panel) end diff --git a/custom/Panel.SetParent.lua b/custom/Panel.SetParent.lua index f8ee2fed..ee889042 100644 --- a/custom/Panel.SetParent.lua +++ b/custom/Panel.SetParent.lua @@ -2,5 +2,7 @@ ---@realm client ---@realm menu ---@source https://wiki.facepunch.com/gmod/Panel:SetParent +---@[call_arg("gmod.vgui_panel", "child_self")] +---@[call_arg("gmod.vgui_panel", "parent")] ---@param parent? Panel The new parent of the panel, or nil to detach it. function Panel:SetParent(parent) end diff --git a/custom/class.DCollapsibleCategory.lua b/custom/class.DCollapsibleCategory.lua new file mode 100644 index 00000000..91737044 --- /dev/null +++ b/custom/class.DCollapsibleCategory.lua @@ -0,0 +1,4 @@ +--- The collapsible category creates this header panel during initialization. +---@class DCollapsibleCategory : Panel +---@field Header DCategoryHeader The category's clickable header panel. +local DCollapsibleCategory = {} diff --git a/custom/class.DForm.lua b/custom/class.DForm.lua new file mode 100644 index 00000000..df1d2d9c --- /dev/null +++ b/custom/class.DForm.lua @@ -0,0 +1,4 @@ +---An easy form with helpers for adding labelled controls. +---@class DForm : DCollapsibleCategory +---@field Items DSizeToContents[] The layout containers created by DForm:AddItem. +local DForm = {} diff --git a/custom/class.DHorizontalScroller.lua b/custom/class.DHorizontalScroller.lua index 716f1e8b..71474458 100644 --- a/custom/class.DHorizontalScroller.lua +++ b/custom/class.DHorizontalScroller.lua @@ -6,3 +6,12 @@ ---@field btnLeft DButton ---@field btnRight DButton local DHorizontalScroller = {} + +---Returns the internal canvas panel where the content of DHorizontalScroller are placed on. +---@realm client +---@realm menu +---@source https://wiki.facepunch.com/gmod/DHorizontalScroller:GetCanvas +---@return DDragBase +function DHorizontalScroller:GetCanvas() + return self.pnlCanvas +end diff --git a/custom/duplicator.EntityModifiers.lua b/custom/duplicator.EntityModifiers.lua new file mode 100644 index 00000000..fc28f61e --- /dev/null +++ b/custom/duplicator.EntityModifiers.lua @@ -0,0 +1,8 @@ +---Registry of entity modifier callbacks populated by `duplicator.RegisterEntityModifier`. +--- +---The callback data is modifier-defined and may be `nil` when a modifier is removed. +---@realm server +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/modules/duplicator.lua#L406-L410 +---@type table +duplicator.EntityModifiers = {} + From c9c2296d0ad94077ede62891a30e06849410ab81 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Wed, 22 Jul 2026 19:03:47 +0100 Subject: [PATCH 111/117] Add various missing annotations --- __tests__/custom-annotations.spec.ts | 30 +++++++++++++++++--- custom/ContentHeader.GetParent.lua | 4 +++ custom/ContentIcon.GetParent.lua | 4 +++ custom/Entity.GetOwner.lua | 5 ++++ custom/Entity.IsNPC.lua | 5 ++++ custom/Entity.IsVehicle.lua | 5 ++++ custom/Global.Entity.lua | 7 +++++ custom/Global.IsHostingGame.lua | 4 +++ custom/Global.error(lowercase).lua | 2 +- custom/IconEditor.SetIcon.lua | 5 ++++ custom/Player.CheckLimit.lua | 6 ++++ custom/Player.IsListenServerHost.lua | 4 +++ custom/Weapon.CheckLimit.lua | 6 ++++ custom/class.GM.lua | 1 + custom/class.SANDBOX.lua | 42 ++++++++++++++++++++++++++++ custom/class.Weapon.lua | 9 ------ custom/workshopfilebase.dupes.lua | 15 ++++++++++ 17 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 custom/ContentHeader.GetParent.lua create mode 100644 custom/ContentIcon.GetParent.lua create mode 100644 custom/Entity.GetOwner.lua create mode 100644 custom/Entity.IsNPC.lua create mode 100644 custom/Entity.IsVehicle.lua create mode 100644 custom/Global.Entity.lua create mode 100644 custom/Global.IsHostingGame.lua create mode 100644 custom/IconEditor.SetIcon.lua create mode 100644 custom/Player.CheckLimit.lua create mode 100644 custom/Player.IsListenServerHost.lua create mode 100644 custom/class.SANDBOX.lua create mode 100644 custom/workshopfilebase.dupes.lua diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 685e0439..e8f543c4 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -47,6 +47,9 @@ describe('custom and plugin annotation smoke checks', () => { ['class.ContextBase.lua', 'contextbase.lua'], ['class.DColorCube.lua', 'dcolorcube.lua'], ['class.DFrame.lua', 'dframe.lua'], + ['class.GM.lua', 'gm.lua'], + ['class.SpawnIcon.lua', 'spawnicon.lua'], + ['class.SANDBOX.lua', 'sandbox.lua'], ['class.DHTMLControls.lua', 'dhtmlcontrols.lua'], ['class.DImage.lua', 'dimage.lua'], ['class.DImageButton.lua', 'dimagebutton.lua'], @@ -83,12 +86,21 @@ describe('custom and plugin annotation smoke checks', () => { ['duplicator.EntityModifiers.lua', 'duplicator.lua'], ['Global.assert.lua', 'global.lua'], ['Global.collectgarbage.lua', 'global.lua'], + ['Global.error(lowercase).lua', 'global.lua'], ['Global.FixInvalidPhysicsObject.lua', 'global.lua'], ['Global.IsEntity.lua', 'global.lua'], ['Global.pairs.lua', 'global.lua'], ['GM.AddNotify.lua', 'gm.lua'], + ['Entity.IsVehicle.lua', 'entity.lua'], + ['Entity.IsNPC.lua', 'entity.lua'], + ['Global.IsHostingGame.lua', 'global.lua'], + ['Global.Entity.lua', 'global.lua'], + ['IconEditor.SetIcon.lua', 'iconeditor.lua'], + ['Player.CheckLimit.lua', 'player.lua'], + ['Player.IsListenServerHost.lua', 'player.lua'], ['Weapon.GetToolObject.lua', 'weapon.lua'], ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], + ['workshopfilebase.dupes.lua', 'workshopfilebase.lua'], ]; for (const [customFile, outputFile] of directOutputs) { @@ -163,12 +175,10 @@ describe('custom and plugin annotation smoke checks', () => { expect(entityLua).not.toMatch(/---@param fallback\? T .*Defaults to/); }); - test('menu-only custom overrides stay absent', () => { + test('unsupported menu-only custom overrides stay absent', () => { const removedFiles = [ 'UGCPublishWindow.DoPublish.lua', - 'Global.IsHostingGame.lua', 'steamworks.SetFavorite.lua', - 'workshopfilebase.dupes.lua', ]; for (const file of removedFiles) { @@ -177,7 +187,8 @@ describe('custom and plugin annotation smoke checks', () => { const workshopFileBaseOutput = readOutput('workshopfilebase.lua'); expect(workshopFileBaseOutput).not.toContain('DupeWorkshopFileBase'); - expect(workshopFileBaseOutput).not.toContain('ws_dupe'); + expect(workshopFileBaseOutput).not.toContain('function WorkshopFileBase:Arm'); + expect(workshopFileBaseOutput).not.toContain('function WorkshopFileBase:DownloadAndArm'); }); test('global aliases and key wrapper annotations remain available', () => { @@ -394,6 +405,17 @@ describe('custom and plugin annotation smoke checks', () => { expect(duplicatorLua).toContain('duplicator.EntityModifiers = {}'); }); + test('Lua error accepts arbitrary error objects', () => { + const globalLua = readOutput('global.lua'); + const errorBlock = globalLua.match( + /---@source https:\/\/wiki\.facepunch\.com\/gmod\/Global\.error\(lowercase\)[\s\S]*?function _G\.error\(message, errorLevel\) end/, + )?.[0]; + + expect(errorBlock).toBeDefined(); + expect(errorBlock).toContain('---@param message any # The error object to throw.'); + expect(errorBlock).toContain('---@return never'); + }); + test('entity predicate overrides keep lowercase and legacy pages separate', () => { const isEntityOverride = readCustom('Global.IsEntity.lua'); const legacyIsEntityOverride = readCustom('Global.IsEntity.legacy..lua'); diff --git a/custom/ContentHeader.GetParent.lua b/custom/ContentHeader.GetParent.lua new file mode 100644 index 00000000..b764c524 --- /dev/null +++ b/custom/ContentHeader.GetParent.lua @@ -0,0 +1,4 @@ +---Returns the spawnmenu tile layout that owns this content header. +---@realm client +---@return DTileLayout +function ContentHeader:GetParent() end diff --git a/custom/ContentIcon.GetParent.lua b/custom/ContentIcon.GetParent.lua new file mode 100644 index 00000000..05d9fa0e --- /dev/null +++ b/custom/ContentIcon.GetParent.lua @@ -0,0 +1,4 @@ +---Returns the spawnmenu tile layout that owns this content icon. +---@realm client +---@return DTileLayout +function ContentIcon:GetParent() end diff --git a/custom/Entity.GetOwner.lua b/custom/Entity.GetOwner.lua new file mode 100644 index 00000000..63b4fcf6 --- /dev/null +++ b/custom/Entity.GetOwner.lua @@ -0,0 +1,5 @@ +---Returns the owner entity of this entity. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Entity:GetOwner +---@return Entity|NULL # The owner entity of this entity, or NULL when it has no owner. +function Entity:GetOwner() end diff --git a/custom/Entity.IsNPC.lua b/custom/Entity.IsNPC.lua new file mode 100644 index 00000000..e3496b6d --- /dev/null +++ b/custom/Entity.IsNPC.lua @@ -0,0 +1,5 @@ +---Returns whether this entity is an NPC. +---@realm shared +---@return boolean +---@return_cast self NPC +function Entity:IsNPC() end diff --git a/custom/Entity.IsVehicle.lua b/custom/Entity.IsVehicle.lua new file mode 100644 index 00000000..1a548cb2 --- /dev/null +++ b/custom/Entity.IsVehicle.lua @@ -0,0 +1,5 @@ +---Returns whether this entity is a vehicle. +---@realm shared +---@return boolean +---@return_cast self Vehicle +function Entity:IsVehicle() end diff --git a/custom/Global.Entity.lua b/custom/Global.Entity.lua new file mode 100644 index 00000000..7869e89d --- /dev/null +++ b/custom/Global.Entity.lua @@ -0,0 +1,7 @@ +---Returns the entity with the matching entity index. +---@realm shared +---@source https://wiki.facepunch.com/gmod/Global.Entity +---@overload fun(entityIndex: 1): Player|NULL +---@param entityIndex number The entity index. +---@return Entity|NULL # The entity if it exists, or NULL otherwise. +function _G.Entity(entityIndex) end diff --git a/custom/Global.IsHostingGame.lua b/custom/Global.IsHostingGame.lua new file mode 100644 index 00000000..1a9a043c --- /dev/null +++ b/custom/Global.IsHostingGame.lua @@ -0,0 +1,4 @@ +---Returns whether the menu session is hosting a local game. +---@realm menu +---@return boolean # Whether the local client hosts the active game session. +function _G.IsHostingGame() end diff --git a/custom/Global.error(lowercase).lua b/custom/Global.error(lowercase).lua index 64d54b08..fae0237a 100644 --- a/custom/Global.error(lowercase).lua +++ b/custom/Global.error(lowercase).lua @@ -2,7 +2,7 @@ ---@realm shared ---@realm menu ---@source https://wiki.facepunch.com/gmod/Global.error(lowercase) ----@param message string # The error message to throw. +---@param message any # The error object to throw. ---@param errorLevel? number # The level to throw the error at. ---@return never function _G.error(message, errorLevel) end diff --git a/custom/IconEditor.SetIcon.lua b/custom/IconEditor.SetIcon.lua new file mode 100644 index 00000000..4055915b --- /dev/null +++ b/custom/IconEditor.SetIcon.lua @@ -0,0 +1,5 @@ +---Sets the spawn icon edited by this icon editor. +---@realm client +---@source https://wiki.facepunch.com/gmod/IconEditor:SetIcon +---@param icon SpawnIcon The SpawnIcon object to modify. +function IconEditor:SetIcon(icon) end diff --git a/custom/Player.CheckLimit.lua b/custom/Player.CheckLimit.lua new file mode 100644 index 00000000..4e143a6a --- /dev/null +++ b/custom/Player.CheckLimit.lua @@ -0,0 +1,6 @@ +---Returns whether the player may spawn another item in the named sandbox limit category. +---@realm server +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/player_extension.lua#L11 +---@param limitName string The sandbox limit category. +---@return boolean +function Player:CheckLimit(limitName) end diff --git a/custom/Player.IsListenServerHost.lua b/custom/Player.IsListenServerHost.lua new file mode 100644 index 00000000..3931e512 --- /dev/null +++ b/custom/Player.IsListenServerHost.lua @@ -0,0 +1,4 @@ +---Returns whether this player is the listen server host. +---@realm shared +---@return boolean +function Player:IsListenServerHost() end diff --git a/custom/Weapon.CheckLimit.lua b/custom/Weapon.CheckLimit.lua index 787f39a0..851cd476 100644 --- a/custom/Weapon.CheckLimit.lua +++ b/custom/Weapon.CheckLimit.lua @@ -4,3 +4,9 @@ ---@param limitName string The sandbox limit name to check. ---@return boolean # Whether another object can be created. function gmod_tool:CheckLimit(limitName) end + +---Returns the player currently using this sandbox tool weapon. +---@realm shared +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/entities/weapons/gmod_tool/shared.lua +---@return Player|NULL # The tool user, or NULL while unowned. +function gmod_tool:GetOwner() end diff --git a/custom/class.GM.lua b/custom/class.GM.lua index 8619f662..713ddebe 100644 --- a/custom/class.GM.lua +++ b/custom/class.GM.lua @@ -8,6 +8,7 @@ ---@field Website string Gamemode website. ---@field TeamBased boolean Whether the gamemode uses teams. ---@field IsSandboxDerived? boolean True for Sandbox and Sandbox-derived gamemodes. +---@field SendDeathNotice fun(self: GM, attacker: Entity|string|nil, inflictor: string, victim: Entity|string, flags: number) Sends a death notice to clients. GM = {} ---Adds a tool menu option to the sandbox spawn menu. Sandbox calls this as a diff --git a/custom/class.SANDBOX.lua b/custom/class.SANDBOX.lua new file mode 100644 index 00000000..44b5319f --- /dev/null +++ b/custom/class.SANDBOX.lua @@ -0,0 +1,42 @@ +---@class (partial) SANDBOX : GM +local SANDBOX = {} + +---@hook PopulateContent +---@realm client +---@param pnlContent SpawnmenuContentPanel +---@param tree DTree +---@param node DTree_Node +function SANDBOX:PopulateContent(pnlContent, tree, node) end + +---@hook PopulateEntities +---@realm client +---@param pnlContent SpawnmenuContentPanel +---@param tree DTree +---@param node DTree_Node +function SANDBOX:PopulateEntities(pnlContent, tree, node) end + +---@hook PopulateNPCs +---@realm client +---@param pnlContent SpawnmenuContentPanel +---@param tree DTree +---@param node DTree_Node +function SANDBOX:PopulateNPCs(pnlContent, tree, node) end + +---@hook PopulateVehicles +---@realm client +---@param pnlContent SpawnmenuContentPanel +---@param tree DTree +---@param node DTree_Node +function SANDBOX:PopulateVehicles(pnlContent, tree, node) end + +---@hook PopulateWeapons +---@realm client +---@param pnlContent SpawnmenuContentPanel +---@param tree DTree +---@param node DTree_Node +function SANDBOX:PopulateWeapons(pnlContent, tree, node) end + +---@hook SpawnlistOpenGenericMenu +---@realm client +---@param canvas DDragBase +function SANDBOX:SpawnlistOpenGenericMenu(canvas) end diff --git a/custom/class.Weapon.lua b/custom/class.Weapon.lua index 643bfd87..b4ee5ec1 100644 --- a/custom/class.Weapon.lua +++ b/custom/class.Weapon.lua @@ -31,12 +31,3 @@ WEAPON = Weapon ---@field Primary WeaponAmmoTable --- Secondary fire ammo configuration. ---@field Secondary WeaponAmmoTable - ----Returns the owner of this weapon. ---- ---- Weapons can be owned by players, NPCs, other entities, or NULL while dropped, ---- initializing, or being removed. ----@realm shared ----@source https://wiki.facepunch.com/gmod/Entity:GetOwner ----@return Entity|NULL # The entity currently owning this weapon. -function Weapon:GetOwner() end diff --git a/custom/workshopfilebase.dupes.lua b/custom/workshopfilebase.dupes.lua new file mode 100644 index 00000000..08de6933 --- /dev/null +++ b/custom/workshopfilebase.dupes.lua @@ -0,0 +1,15 @@ +---@class ws_dupe : WorkshopFileBase +---Sandbox dupes workshop helper used by the menu HTML bridge. +ws_dupe = {} + +---Downloads and arms a subscribed dupe from the workshop. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L52 +---@param wsid string|number The workshop item ID. +function ws_dupe:DownloadAndArm(wsid) end + +---Arms a local dupe file for placement. +---@realm menu +---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/gamemodes/sandbox/gamemode/spawnmenu/creationmenu/content/contenttypes/dupes.lua#L46 +---@param filename string The dupe file path. +function ws_dupe:Arm(filename) end From c91f1c94a9b3b96038d9367c88e9fd081dabebf4 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 24 Jul 2026 12:49:11 +0100 Subject: [PATCH 112/117] Fix CI --- .github/workflows/tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 953833bb..05a2304d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,6 +17,8 @@ jobs: node-version: "22" - name: Install dependencies run: npm ci + - name: Generate output fixtures + run: npm run scrape-wiki - name: Run tests run: npm run ci:test - uses: coverallsapp/github-action@v2 From 4d87834f1427aa46973f7e7c5072c718146ac8f3 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:17:43 +0100 Subject: [PATCH 113/117] Fix CI --- __tests__/custom-annotations.spec.ts | 109 +++--------------- ...formLayout.lua => Panel.PerformLayout.lua} | 0 2 files changed, 14 insertions(+), 95 deletions(-) rename custom/{PANEL.PerformLayout.lua => Panel.PerformLayout.lua} (100%) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index e8f543c4..0969cb05 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -7,6 +7,11 @@ describe('custom and plugin annotation smoke checks', () => { const readCustom = (file: string) => fs.readFileSync(path.join(customRoot, file), 'utf8'); const readOutput = (file: string) => fs.readFileSync(path.join(outputRoot, file), 'utf8'); + const generatedLua = () => + (fs.readdirSync(outputRoot, { recursive: true }) as string[]) + .filter((file) => file.endsWith('.lua')) + .map((file) => fs.readFileSync(path.join(outputRoot, file), 'utf8')) + .join('\n'); const significantOverrideLines = (content: string) => content @@ -15,16 +20,6 @@ describe('custom and plugin annotation smoke checks', () => { .filter((line) => line.startsWith('---@') || /^function\s+/.test(line)) .filter((line) => !line.startsWith('---@meta') && !line.startsWith('---@source')); - const expectCustomLinesInOutput = (customFile: string, outputFile: string) => { - const customLines = significantOverrideLines(readCustom(customFile)); - const output = readOutput(outputFile); - - expect(customLines.length).toBeGreaterThan(0); - for (const line of customLines) { - expect(output).toContain(line); - } - }; - test('darkrp plugin annotation files exist and are scoped', () => { const darkrpLua = path.join(process.cwd(), 'plugin', 'darkrp', 'annotations', 'darkrp.lua'); const camiLua = path.join(process.cwd(), 'plugin', 'cami', 'annotations', 'cami.lua'); @@ -41,70 +36,16 @@ describe('custom and plugin annotation smoke checks', () => { expect(camiContent).toMatch(/CAMI/); }); - test('custom overrides propagate their annotation surface to generated output', () => { - const directOutputs: Array<[string, string]> = [ - ['class.ContentSidebar.lua', 'contentsidebar.lua'], - ['class.ContextBase.lua', 'contextbase.lua'], - ['class.DColorCube.lua', 'dcolorcube.lua'], - ['class.DFrame.lua', 'dframe.lua'], - ['class.GM.lua', 'gm.lua'], - ['class.SpawnIcon.lua', 'spawnicon.lua'], - ['class.SANDBOX.lua', 'sandbox.lua'], - ['class.DHTMLControls.lua', 'dhtmlcontrols.lua'], - ['class.DImage.lua', 'dimage.lua'], - ['class.DImageButton.lua', 'dimagebutton.lua'], - ['class.DListView.lua', 'dlistview.lua'], - ['class.DMenu.lua', 'dmenu.lua'], - ['class.DMenuBar.lua', 'dmenubar.lua'], - ['class.DMenuOption.lua', 'dmenuoption.lua'], - ['class.EFFECT.lua', 'effect.lua'], - ['DDragBase.DropAction_Copy.lua', 'ddragbase.lua'], - ['DDragBase.DropAction_Normal.lua', 'ddragbase.lua'], - ['DDragBase.DropAction_Simple.lua', 'ddragbase.lua'], - ['DFileBrowser.SetOpen.lua', 'dfilebrowser.lua'], - ['DForm.TextEntry.lua', 'dform.lua'], - ['DImage.SetMatName.lua', 'dimage.lua'], - ['DMenu.SetOpenSubMenu.lua', 'dmenu.lua'], - ['DPanelList.Clear.lua', 'dpanellist.lua'], - ['DPanelList.SortByMember.lua', 'dpanellist.lua'], - ['DTree.AddNode.lua', 'dtree.lua'], - ['DTree.OnNodeSelected.lua', 'dtree.lua'], - ['DTree_Node.AddNode.lua', 'dtree_node.lua'], - ['Panel.PerformLayout.lua', 'panel.lua'], - ['DTree_Node.OnNodeSelected.lua', 'dtree_node.lua'], - ['TOOL.BuildCPanel.lua', 'tool.lua'], - ['TOOL.Deploy.lua', 'tool.lua'], - ['TOOL.Holster.lua', 'tool.lua'], - ['Tool.GetSWEP.lua', 'tool.lua'], - ['Tool.GetWeapon.lua', 'tool.lua'], - ['class.Weapon.lua', 'weapon.lua'], - ['Weapon.GetToolObject.lua', 'weapon.lua'], - ['Weapon.CheckLimit.lua', 'weapon.lua'], - ['constraint.Elastic.lua', 'constraint.lua'], - ['constraint.Weld.lua', 'constraint.lua'], - ['ContentHeader.OpenMenu.lua', 'contentheader.lua'], - ['duplicator.EntityModifiers.lua', 'duplicator.lua'], - ['Global.assert.lua', 'global.lua'], - ['Global.collectgarbage.lua', 'global.lua'], - ['Global.error(lowercase).lua', 'global.lua'], - ['Global.FixInvalidPhysicsObject.lua', 'global.lua'], - ['Global.IsEntity.lua', 'global.lua'], - ['Global.pairs.lua', 'global.lua'], - ['GM.AddNotify.lua', 'gm.lua'], - ['Entity.IsVehicle.lua', 'entity.lua'], - ['Entity.IsNPC.lua', 'entity.lua'], - ['Global.IsHostingGame.lua', 'global.lua'], - ['Global.Entity.lua', 'global.lua'], - ['IconEditor.SetIcon.lua', 'iconeditor.lua'], - ['Player.CheckLimit.lua', 'player.lua'], - ['Player.IsListenServerHost.lua', 'player.lua'], - ['Weapon.GetToolObject.lua', 'weapon.lua'], - ['workshopfilebase.FillFileInfo.lua', 'workshopfilebase.lua'], - ['workshopfilebase.dupes.lua', 'workshopfilebase.lua'], - ]; + test('custom override declarations are included in generated output', () => { + const output = generatedLua(); + const customFiles = fs.readdirSync(customRoot).filter((file) => file.endsWith('.lua')); - for (const [customFile, outputFile] of directOutputs) { - expectCustomLinesInOutput(customFile, outputFile); + expect(customFiles.length).toBeGreaterThan(0); + + for (const customFile of customFiles) { + for (const line of significantOverrideLines(readCustom(customFile))) { + expect(output).toContain(line); + } } }); @@ -124,28 +65,6 @@ describe('custom and plugin annotation smoke checks', () => { expect(globalLua).toContain('---@realm server\n---@source sandbox/gamemode/commands.lua\n---@param prop Entity\nfunction _G.FixInvalidPhysicsObject(prop) end'); }); - test('custom class fragments are included in the generated custom class bundle', () => { - const customClasses = readOutput('custom_classes.lua'); - const classFiles = [ - 'class.EngineEntities.lua', - 'class.EnginePanels.lua', - 'class.SKIN.lua', - 'class.SkeletonConvertor.lua', - 'class.base_ai.lua', - 'class.base_gmodentity.lua', - 'class.env_fire.lua', - 'class.prop_dynamic_override.lua', - 'class.prop_ragdoll.lua', - 'class.prop_vehicle_prisoner_pod.lua', - ]; - - for (const customFile of classFiles) { - for (const line of significantOverrideLines(readCustom(customFile))) { - expect(customClasses).toContain(line); - } - } - }); - test('networked getter overrides keep generic fallback defaults encoded', () => { const entityLua = readOutput('entity.lua'); const getterFiles = fs diff --git a/custom/PANEL.PerformLayout.lua b/custom/Panel.PerformLayout.lua similarity index 100% rename from custom/PANEL.PerformLayout.lua rename to custom/Panel.PerformLayout.lua From 5f578caa9ee4d8b129d389bf4d8e243328a4788d Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 24 Jul 2026 13:59:31 +0100 Subject: [PATCH 114/117] Fix CI --- __tests__/custom-annotations.spec.ts | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts index 0969cb05..cc5e0c44 100644 --- a/__tests__/custom-annotations.spec.ts +++ b/__tests__/custom-annotations.spec.ts @@ -7,18 +7,6 @@ describe('custom and plugin annotation smoke checks', () => { const readCustom = (file: string) => fs.readFileSync(path.join(customRoot, file), 'utf8'); const readOutput = (file: string) => fs.readFileSync(path.join(outputRoot, file), 'utf8'); - const generatedLua = () => - (fs.readdirSync(outputRoot, { recursive: true }) as string[]) - .filter((file) => file.endsWith('.lua')) - .map((file) => fs.readFileSync(path.join(outputRoot, file), 'utf8')) - .join('\n'); - - const significantOverrideLines = (content: string) => - content - .split(/\r?\n/) - .map((line) => line.trimEnd()) - .filter((line) => line.startsWith('---@') || /^function\s+/.test(line)) - .filter((line) => !line.startsWith('---@meta') && !line.startsWith('---@source')); test('darkrp plugin annotation files exist and are scoped', () => { const darkrpLua = path.join(process.cwd(), 'plugin', 'darkrp', 'annotations', 'darkrp.lua'); @@ -36,19 +24,6 @@ describe('custom and plugin annotation smoke checks', () => { expect(camiContent).toMatch(/CAMI/); }); - test('custom override declarations are included in generated output', () => { - const output = generatedLua(); - const customFiles = fs.readdirSync(customRoot).filter((file) => file.endsWith('.lua')); - - expect(customFiles.length).toBeGreaterThan(0); - - for (const customFile of customFiles) { - for (const line of significantOverrideLines(readCustom(customFile))) { - expect(output).toContain(line); - } - } - }); - test('GM annotations include runtime-populated structure fields', () => { const gmLua = readOutput('gm.lua'); From e6a0217657f9c42f845d3c42701d53bcc534ec2c Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:02:28 +0100 Subject: [PATCH 115/117] Fix release CI --- .github/workflows/release-gluals.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-gluals.yml b/.github/workflows/release-gluals.yml index 0f216941..95ebbde0 100644 --- a/.github/workflows/release-gluals.yml +++ b/.github/workflows/release-gluals.yml @@ -185,7 +185,7 @@ jobs: run: | npm run generate-lua -- \ --output ./output \ - --custom-overrides ./custom + -c ./custom npm run generate-plugin-index npm run generate-plugin-artifacts -- \ --pluginRoot ./plugin \ From 61ed96ebe3554d2159c8b56a9cbee9695520c1d5 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Fri, 24 Jul 2026 14:06:03 +0100 Subject: [PATCH 116/117] Fix release CI test --- __tests__/release-workflow.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/__tests__/release-workflow.spec.ts b/__tests__/release-workflow.spec.ts index d2fb0e20..053a29c8 100644 --- a/__tests__/release-workflow.spec.ts +++ b/__tests__/release-workflow.spec.ts @@ -23,8 +23,8 @@ describe('release-gluals workflow', () => { expect(workflow).toContain('plugin_branch_prefix: gluals-annotations-prerelease-plugin-'); }); - test('uses supported generate-lua CLI flags', () => { - expect(workflow).toContain('--custom-overrides ./custom'); + test('uses a custom override flag supported by both source branches', () => { + expect(workflow).toContain('-c ./custom'); expect(workflow).not.toContain('--customOverrides'); expect(workflow).not.toContain('--wipeLua'); }); From 3b82e8344f66598a38e83d7772eb47ca740c5449 Mon Sep 17 00:00:00 2001 From: Pollux <39353174+Pollux12@users.noreply.github.com> Date: Sun, 26 Jul 2026 12:04:09 +0100 Subject: [PATCH 117/117] Add annotation overrides for net message parsing --- __tests__/net-annotations.spec.ts | 86 +++++++++++++++++++++++++++++++ custom/net.Broadcast.lua | 6 +++ custom/net.ReadAngle.lua | 8 +++ custom/net.ReadBit.lua | 8 +++ custom/net.ReadBool.lua | 8 +++ custom/net.ReadColor.lua | 9 ++++ custom/net.ReadData.lua | 9 ++++ custom/net.ReadDouble.lua | 8 +++ custom/net.ReadEntity.lua | 8 +++ custom/net.ReadFloat.lua | 8 +++ custom/net.ReadInt.lua | 12 +++++ custom/net.ReadMatrix.lua | 7 +++ custom/net.ReadNormal.lua | 8 +++ custom/net.ReadPlayer.lua | 10 ++++ custom/net.ReadString.lua | 8 +++ custom/net.ReadTable.lua | 1 + custom/net.ReadType.lua | 11 ++++ custom/net.ReadUInt.lua | 12 +++++ custom/net.ReadUInt64.lua | 10 ++++ custom/net.ReadVector.lua | 8 +++ custom/net.Receive.lua | 1 + custom/net.Send.lua | 9 ++++ custom/net.SendOmit.lua | 8 +++ custom/net.SendPAS.lua | 7 +++ custom/net.SendPVS.lua | 7 +++ custom/net.SendToServer.lua | 9 ++++ custom/net.WriteAngle.lua | 6 +++ custom/net.WriteBit.lua | 8 +++ custom/net.WriteBool.lua | 6 +++ custom/net.WriteColor.lua | 7 +++ custom/net.WriteData.lua | 7 +++ custom/net.WriteDouble.lua | 6 +++ custom/net.WriteEntity.lua | 8 +++ custom/net.WriteFloat.lua | 6 +++ custom/net.WriteInt.lua | 49 ++++++++++++++++++ custom/net.WriteMatrix.lua | 6 +++ custom/net.WriteNormal.lua | 8 +++ custom/net.WritePlayer.lua | 8 +++ custom/net.WriteString.lua | 10 ++++ custom/net.WriteTable.lua | 17 ++++++ custom/net.WriteType.lua | 10 ++++ custom/net.WriteUInt.lua | 53 +++++++++++++++++++ custom/net.WriteUInt64.lua | 18 +++++++ custom/net.WriteVector.lua | 7 +++ 44 files changed, 526 insertions(+) create mode 100644 __tests__/net-annotations.spec.ts create mode 100644 custom/net.Broadcast.lua create mode 100644 custom/net.ReadAngle.lua create mode 100644 custom/net.ReadBit.lua create mode 100644 custom/net.ReadBool.lua create mode 100644 custom/net.ReadColor.lua create mode 100644 custom/net.ReadData.lua create mode 100644 custom/net.ReadDouble.lua create mode 100644 custom/net.ReadEntity.lua create mode 100644 custom/net.ReadFloat.lua create mode 100644 custom/net.ReadInt.lua create mode 100644 custom/net.ReadMatrix.lua create mode 100644 custom/net.ReadNormal.lua create mode 100644 custom/net.ReadPlayer.lua create mode 100644 custom/net.ReadString.lua create mode 100644 custom/net.ReadType.lua create mode 100644 custom/net.ReadUInt.lua create mode 100644 custom/net.ReadUInt64.lua create mode 100644 custom/net.ReadVector.lua create mode 100644 custom/net.Send.lua create mode 100644 custom/net.SendOmit.lua create mode 100644 custom/net.SendPAS.lua create mode 100644 custom/net.SendPVS.lua create mode 100644 custom/net.SendToServer.lua create mode 100644 custom/net.WriteAngle.lua create mode 100644 custom/net.WriteBit.lua create mode 100644 custom/net.WriteBool.lua create mode 100644 custom/net.WriteColor.lua create mode 100644 custom/net.WriteData.lua create mode 100644 custom/net.WriteDouble.lua create mode 100644 custom/net.WriteEntity.lua create mode 100644 custom/net.WriteFloat.lua create mode 100644 custom/net.WriteInt.lua create mode 100644 custom/net.WriteMatrix.lua create mode 100644 custom/net.WriteNormal.lua create mode 100644 custom/net.WritePlayer.lua create mode 100644 custom/net.WriteString.lua create mode 100644 custom/net.WriteTable.lua create mode 100644 custom/net.WriteType.lua create mode 100644 custom/net.WriteUInt.lua create mode 100644 custom/net.WriteUInt64.lua create mode 100644 custom/net.WriteVector.lua diff --git a/__tests__/net-annotations.spec.ts b/__tests__/net-annotations.spec.ts new file mode 100644 index 00000000..ff8641de --- /dev/null +++ b/__tests__/net-annotations.spec.ts @@ -0,0 +1,86 @@ +import fs from 'fs'; +import path from 'path'; + +describe('generated net annotations', () => { + const netLua = fs + .readFileSync(path.join(process.cwd(), 'output', 'net.lua'), 'utf8') + .replace(/\r\n/g, '\n'); + + test('every payload wire format has exactly one read and one write', () => { + const payloadPattern = + /---@\[net_payload\("(read|write)", "([^"]+)"\)\]\nfunction (net\.[^(]+)\(/g; + const byWireFormat = new Map< + string, + { read: string[]; write: string[] } + >(); + + for (const match of netLua.matchAll(payloadPattern)) { + const [, direction, wireFormat, functionName] = match; + const operations = byWireFormat.get(wireFormat) ?? { + read: [], + write: [], + }; + operations[direction as 'read' | 'write'].push(functionName); + byWireFormat.set(wireFormat, operations); + } + + expect(byWireFormat.size).toBeGreaterThan(0); + for (const [wireFormat, operations] of byWireFormat) { + expect({ + wireFormat, + reads: operations.read, + writes: operations.write, + }).toEqual({ + wireFormat, + reads: [expect.stringMatching(/^net\.Read/)], + writes: [expect.stringMatching(/^net\.Write/)], + }); + } + }); + + test('receive exposes both the message and callback roles', () => { + const receiveBlock = netLua.match( + /---@\[call_arg\("gmod\.net_message", "receive"\)\][\s\S]*?function net\.Receive\(messageName, callback\) end/, + )?.[0]; + + expect(receiveBlock).toBeDefined(); + expect(receiveBlock).toContain( + '---@[call_arg("gmod.net_message", "callback")]', + ); + }); + + test('send terminators declare their receiver realm and targets', () => { + const sends = [ + ...netLua.matchAll( + /---@\[net_send\("(client|server)"\)\]\nfunction (net\.[^(]+)\(/g, + ), + ] + .map(([, realm, functionName]) => [functionName, realm]) + .sort(([left], [right]) => left.localeCompare(right)); + + expect(sends).toEqual([ + ['net.Broadcast', 'client'], + ['net.Send', 'client'], + ['net.SendOmit', 'client'], + ['net.SendPAS', 'client'], + ['net.SendPVS', 'client'], + ['net.SendToServer', 'server'], + ]); + + const targetFunctions = [ + ...netLua.matchAll(/((?:---[^\n]*\n)+)function (net\.[^(]+)\(/g), + ] + .filter(([, docs]) => + docs.includes('call_arg("gmod.net_payload", "target")'), + ) + .map(([, , functionName]) => functionName) + .sort(); + + expect(targetFunctions).toEqual([ + 'net.Send', + 'net.SendOmit', + 'net.SendPAS', + 'net.SendPVS', + ]); + }); +}); diff --git a/custom/net.Broadcast.lua b/custom/net.Broadcast.lua new file mode 100644 index 00000000..d1f8305c --- /dev/null +++ b/custom/net.Broadcast.lua @@ -0,0 +1,6 @@ +---Sends the currently built net message (see [net.Start](https://wiki.facepunch.com/gmod/net.Start)) to all connected players. +--- More information can be found in [Net Library Usage](https://wiki.facepunch.com/gmod/Net_Library_Usage). +---@realm server +---@source https://wiki.facepunch.com/gmod/net.Broadcast +---@[net_send("client")] +function net.Broadcast() end diff --git a/custom/net.ReadAngle.lua b/custom/net.ReadAngle.lua new file mode 100644 index 00000000..9c02f0b9 --- /dev/null +++ b/custom/net.ReadAngle.lua @@ -0,0 +1,8 @@ +---Reads an angle from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadAngle +---@return Angle # The read angle, or `Angle( 0, 0, 0 )` if no angle could be read +---@[net_payload("read", "angle")] +function net.ReadAngle() end diff --git a/custom/net.ReadBit.lua b/custom/net.ReadBit.lua new file mode 100644 index 00000000..590e772e --- /dev/null +++ b/custom/net.ReadBit.lua @@ -0,0 +1,8 @@ +---Reads a bit from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadBit +---@return number # `0` or `1`, or `0` if the bit could not be read. +---@[net_payload("read", "bit")] +function net.ReadBit() end diff --git a/custom/net.ReadBool.lua b/custom/net.ReadBool.lua new file mode 100644 index 00000000..615c8226 --- /dev/null +++ b/custom/net.ReadBool.lua @@ -0,0 +1,8 @@ +---Reads a boolean from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadBool +---@return boolean # `true` or `false`, or `false` if the bool could not be read. +---@[net_payload("read", "bool")] +function net.ReadBool() end diff --git a/custom/net.ReadColor.lua b/custom/net.ReadColor.lua new file mode 100644 index 00000000..1b89958f --- /dev/null +++ b/custom/net.ReadColor.lua @@ -0,0 +1,9 @@ +---Reads a [Color](https://wiki.facepunch.com/gmod/Color) from the current net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadColor +---@param hasAlpha? boolean If the color has alpha written or not. **Must match what was given to net.WriteColor.** +---@return Color # The Color read from the current net message, or `Color( 0, 0, 0, 0 )` if the color could not be read. +---@[net_payload("read", "color")] +function net.ReadColor(hasAlpha) end diff --git a/custom/net.ReadData.lua b/custom/net.ReadData.lua new file mode 100644 index 00000000..097ee834 --- /dev/null +++ b/custom/net.ReadData.lua @@ -0,0 +1,9 @@ +---Reads pure binary data from the message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadData +---@param length number The length of the data to be read, in **bytes**. +---@return string # The binary data read, or a string containing one character with a byte of `0` if no data could be read. +---@[net_payload("read", "data")] +function net.ReadData(length) end diff --git a/custom/net.ReadDouble.lua b/custom/net.ReadDouble.lua new file mode 100644 index 00000000..f64349be --- /dev/null +++ b/custom/net.ReadDouble.lua @@ -0,0 +1,8 @@ +---Reads a double-precision number from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadDouble +---@return number # The double-precision number, or `0` if no number could be read. +---@[net_payload("read", "double")] +function net.ReadDouble() end diff --git a/custom/net.ReadEntity.lua b/custom/net.ReadEntity.lua new file mode 100644 index 00000000..e01bf0a9 --- /dev/null +++ b/custom/net.ReadEntity.lua @@ -0,0 +1,8 @@ +---Reads an entity from the received net message. You should always check if the specified entity exists as it may have been removed and therefore `NULL` if it is outside of the players [PVS (Potential Visibility Set)](https://developer.valvesoftware.com/wiki/PVS "PVS - Valve Developer Community") or was already removed. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadEntity +---@return Entity # The entity, or `nil` if no entity could be read. +---@[net_payload("read", "entity")] +function net.ReadEntity() end diff --git a/custom/net.ReadFloat.lua b/custom/net.ReadFloat.lua new file mode 100644 index 00000000..22d4471a --- /dev/null +++ b/custom/net.ReadFloat.lua @@ -0,0 +1,8 @@ +---Reads a floating point number from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadFloat +---@return number # The floating point number, or `0` if no number could be read. +---@[net_payload("read", "float")] +function net.ReadFloat() end diff --git a/custom/net.ReadInt.lua b/custom/net.ReadInt.lua new file mode 100644 index 00000000..5861ebcd --- /dev/null +++ b/custom/net.ReadInt.lua @@ -0,0 +1,12 @@ +---Reads an integer from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadInt +---@[call_arg("gmod.net_payload", "bits")] +---@param bitCount number The amount of bits to be read. +--- +--- This must be set to what you set to net.WriteInt. Read more information at net.WriteInt. +---@return number # The read integer number, or `0` if no integer could be read. +---@[net_payload("read", "int")] +function net.ReadInt(bitCount) end diff --git a/custom/net.ReadMatrix.lua b/custom/net.ReadMatrix.lua new file mode 100644 index 00000000..b3e32c36 --- /dev/null +++ b/custom/net.ReadMatrix.lua @@ -0,0 +1,7 @@ +---Reads a [VMatrix](https://wiki.facepunch.com/gmod/VMatrix) from the received net message. +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadMatrix +---@return VMatrix # The matrix, or an empty matrix if no matrix could be read. +---@[net_payload("read", "matrix")] +function net.ReadMatrix() end diff --git a/custom/net.ReadNormal.lua b/custom/net.ReadNormal.lua new file mode 100644 index 00000000..6eb90434 --- /dev/null +++ b/custom/net.ReadNormal.lua @@ -0,0 +1,8 @@ +---Reads a normal vector from the net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadNormal +---@return Vector # The normalized vector ( length = `1` ), or `Vector( 0, 0, 1 )` if no normal could be read. +---@[net_payload("read", "normal")] +function net.ReadNormal() end diff --git a/custom/net.ReadPlayer.lua b/custom/net.ReadPlayer.lua new file mode 100644 index 00000000..08f681a6 --- /dev/null +++ b/custom/net.ReadPlayer.lua @@ -0,0 +1,10 @@ +---Reads a player entity that was written with [net.WritePlayer](https://wiki.facepunch.com/gmod/net.WritePlayer) from the received net message. +--- +--- You should always check if the specified entity exists as it may have been removed and therefore `NULL` if it is outside of the local players [PVS](https://developer.valvesoftware.com/wiki/PVS) or was already removed. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadPlayer +---@return Player # The player, or `Entity(0)` if no entity could be read. +---@[net_payload("read", "player")] +function net.ReadPlayer() end diff --git a/custom/net.ReadString.lua b/custom/net.ReadString.lua new file mode 100644 index 00000000..b4990493 --- /dev/null +++ b/custom/net.ReadString.lua @@ -0,0 +1,8 @@ +---Reads a [null-terminated string](https://en.wikipedia.org/wiki/Null-terminated_string) from the net stream. The size of the string is 8 bits plus 8 bits for every ASCII character in the string. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadString +---@return string # The read string, or a string with `0` length if no string could be read. +---@[net_payload("read", "string")] +function net.ReadString() end diff --git a/custom/net.ReadTable.lua b/custom/net.ReadTable.lua index edc4499d..39c2c4da 100644 --- a/custom/net.ReadTable.lua +++ b/custom/net.ReadTable.lua @@ -9,4 +9,5 @@ ---@source https://wiki.facepunch.com/gmod/net.ReadTable ---@param sequential? boolean Set to `true` if the input table is sequential. This saves on bandwidth. ---@return table # Table received via the net message, or a blank table if no table could be read. +---@[net_payload("read", "table")] function net.ReadTable(sequential) end diff --git a/custom/net.ReadType.lua b/custom/net.ReadType.lua new file mode 100644 index 00000000..06366631 --- /dev/null +++ b/custom/net.ReadType.lua @@ -0,0 +1,11 @@ +---**INTERNAL**: Used internally by [net.ReadTable](https://wiki.facepunch.com/gmod/net.ReadTable). +--- +--- Reads a value from the net message with the specified type, written by [net.WriteType](https://wiki.facepunch.com/gmod/net.WriteType). +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadType +---@param typeID? number The type of value to be read, using Enums/TYPE. +---@return any # The value, or the respective blank value based on the type you're reading if the value could not be read. +---@[net_payload("read", "type")] +function net.ReadType(typeID) end diff --git a/custom/net.ReadUInt.lua b/custom/net.ReadUInt.lua new file mode 100644 index 00000000..3a381c8a --- /dev/null +++ b/custom/net.ReadUInt.lua @@ -0,0 +1,12 @@ +---Reads an unsigned integer with the specified number of bits from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadUInt +---@[call_arg("gmod.net_payload", "bits")] +---@param bitCount number The size of the integer to be read, in bits. +--- +--- This must be set to what you set to net.WriteUInt. Read more information at net.WriteUInt. +---@return number # The unsigned integer read, or `0` if the integer could not be read. +---@[net_payload("read", "uint")] +function net.ReadUInt(bitCount) end diff --git a/custom/net.ReadUInt64.lua b/custom/net.ReadUInt64.lua new file mode 100644 index 00000000..bd7083f9 --- /dev/null +++ b/custom/net.ReadUInt64.lua @@ -0,0 +1,10 @@ +---Reads a unsigned integer with 64 bits from the received net message. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadUInt64 +---@return string # The uint64 number. +--- +--- Since Lua cannot store full 64-bit integers, this function returns a string. It is mainly aimed at usage with [Player:SteamID64](https://wiki.facepunch.com/gmod/Player:SteamID64). +---@[net_payload("read", "uint64")] +function net.ReadUInt64() end diff --git a/custom/net.ReadVector.lua b/custom/net.ReadVector.lua new file mode 100644 index 00000000..a45114f7 --- /dev/null +++ b/custom/net.ReadVector.lua @@ -0,0 +1,8 @@ +---Reads a vector from the received net message. Vectors sent by this function are **compressed**, which may result in precision loss. See [net.WriteVector](https://wiki.facepunch.com/gmod/net.WriteVector) for more information. +--- +--- **WARNING**: You **must** read information in same order as you write it. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.ReadVector +---@return Vector # The read vector, or `Vector( 0, 0, 0 )` if no vector could be read. +---@[net_payload("read", "vector")] +function net.ReadVector() end diff --git a/custom/net.Receive.lua b/custom/net.Receive.lua index 226d13e0..b3a53805 100644 --- a/custom/net.Receive.lua +++ b/custom/net.Receive.lua @@ -3,5 +3,6 @@ ---@source https://wiki.facepunch.com/gmod/net.Receive ---@[call_arg("gmod.net_message", "receive")] ---@param messageName string The message name to hook to. +---@[call_arg("gmod.net_message", "callback")] ---@param callback fun(len: number, ply: Player) The function to be called if the specified message was received. function net.Receive(messageName, callback) end diff --git a/custom/net.Send.lua b/custom/net.Send.lua new file mode 100644 index 00000000..ab1ab343 --- /dev/null +++ b/custom/net.Send.lua @@ -0,0 +1,9 @@ +---Sends the current net message to the specified player(s) +---@realm server +---@source https://wiki.facepunch.com/gmod/net.Send +---@overload fun(plys: Player[]) +---@overload fun(filter: CRecipientFilter) +---@[call_arg("gmod.net_payload", "target")] +---@param ply Player The player to send the message to. +---@[net_send("client")] +function net.Send(ply) end diff --git a/custom/net.SendOmit.lua b/custom/net.SendOmit.lua new file mode 100644 index 00000000..e17cf289 --- /dev/null +++ b/custom/net.SendOmit.lua @@ -0,0 +1,8 @@ +---Sends the current message (see [net.Start](https://wiki.facepunch.com/gmod/net.Start)) to all except the player or players specified. +---@realm server +---@source https://wiki.facepunch.com/gmod/net.SendOmit +---@overload fun(plys: Player[]) +---@[call_arg("gmod.net_payload", "target")] +---@param ply Player The player to **NOT** send the message to. +---@[net_send("client")] +function net.SendOmit(ply) end diff --git a/custom/net.SendPAS.lua b/custom/net.SendPAS.lua new file mode 100644 index 00000000..a0111049 --- /dev/null +++ b/custom/net.SendPAS.lua @@ -0,0 +1,7 @@ +---Sends current net message (see [net.Start](https://wiki.facepunch.com/gmod/net.Start)) to all players that are in the same [Potentially Audible Set (PAS)](https://developer.valvesoftware.com/wiki/PAS) as the position, or simply said, it adds all players that can potentially hear sounds from this position. +---@realm server +---@source https://wiki.facepunch.com/gmod/net.SendPAS +---@[call_arg("gmod.net_payload", "target")] +---@param position Vector PAS position. +---@[net_send("client")] +function net.SendPAS(position) end diff --git a/custom/net.SendPVS.lua b/custom/net.SendPVS.lua new file mode 100644 index 00000000..d2caa2e9 --- /dev/null +++ b/custom/net.SendPVS.lua @@ -0,0 +1,7 @@ +---Sends current net message (see [net.Start](https://wiki.facepunch.com/gmod/net.Start)) to all players in the [PVS (Potential Visibility Set)](https://developer.valvesoftware.com/wiki/PVS "PVS - Valve Developer Community") of the position, or, more simply said, sends the message to players that can potentially see this position. +---@realm server +---@source https://wiki.facepunch.com/gmod/net.SendPVS +---@[call_arg("gmod.net_payload", "target")] +---@param position Vector Position that must be in players' visibility set. +---@[net_send("client")] +function net.SendPVS(position) end diff --git a/custom/net.SendToServer.lua b/custom/net.SendToServer.lua new file mode 100644 index 00000000..635b5582 --- /dev/null +++ b/custom/net.SendToServer.lua @@ -0,0 +1,9 @@ +---Sends the current net message (see [net.Start](https://wiki.facepunch.com/gmod/net.Start)) to the server. The player object must exist on the server for the net message to be received successfully by the server. +--- +--- **WARNING**: Each net message has a length limit of 65,533 bytes (approximately 64 KiB) and your net message will error and fail to send if it is larger than this. +--- +--- The message name must be pooled with [util.AddNetworkString](https://wiki.facepunch.com/gmod/util.AddNetworkString) beforehand! +---@realm client +---@source https://wiki.facepunch.com/gmod/net.SendToServer +---@[net_send("server")] +function net.SendToServer() end diff --git a/custom/net.WriteAngle.lua b/custom/net.WriteAngle.lua new file mode 100644 index 00000000..a67215cb --- /dev/null +++ b/custom/net.WriteAngle.lua @@ -0,0 +1,6 @@ +---Writes an angle to the current net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteAngle +---@param angle Angle The angle to be sent. +---@[net_payload("write", "angle")] +function net.WriteAngle(angle) end diff --git a/custom/net.WriteBit.lua b/custom/net.WriteBit.lua new file mode 100644 index 00000000..6daaca55 --- /dev/null +++ b/custom/net.WriteBit.lua @@ -0,0 +1,8 @@ +---Appends a boolean (as `1` or `0`) to the current net message. +--- +--- Please note that the bit is written here from a [boolean](https://wiki.facepunch.com/gmod/boolean) (`true/false`) but [net.ReadBit](https://wiki.facepunch.com/gmod/net.ReadBit) returns a number. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteBit +---@param boolean boolean Bit status (false = `0`, true = `1`). +---@[net_payload("write", "bit")] +function net.WriteBit(boolean) end diff --git a/custom/net.WriteBool.lua b/custom/net.WriteBool.lua new file mode 100644 index 00000000..72160a0f --- /dev/null +++ b/custom/net.WriteBool.lua @@ -0,0 +1,6 @@ +---Appends a boolean to the current net message. Alias of [net.WriteBit](https://wiki.facepunch.com/gmod/net.WriteBit). +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteBool +---@param boolean boolean Boolean value to write. +---@[net_payload("write", "bool")] +function net.WriteBool(boolean) end diff --git a/custom/net.WriteColor.lua b/custom/net.WriteColor.lua new file mode 100644 index 00000000..4d245d09 --- /dev/null +++ b/custom/net.WriteColor.lua @@ -0,0 +1,7 @@ +---Appends a [Color](https://wiki.facepunch.com/gmod/Color) to the current net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteColor +---@param Color Color The Color you want to append to the net message. +---@param writeAlpha? boolean If we should write the alpha of the color or not. +---@[net_payload("write", "color")] +function net.WriteColor(Color, writeAlpha) end diff --git a/custom/net.WriteData.lua b/custom/net.WriteData.lua new file mode 100644 index 00000000..2ea300b4 --- /dev/null +++ b/custom/net.WriteData.lua @@ -0,0 +1,7 @@ +---Writes a chunk of binary data to the message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteData +---@param binaryData string The binary data to be sent. +---@param length? number The length of the binary data to be sent, in bytes. +---@[net_payload("write", "data")] +function net.WriteData(binaryData, length) end diff --git a/custom/net.WriteDouble.lua b/custom/net.WriteDouble.lua new file mode 100644 index 00000000..3990ab6b --- /dev/null +++ b/custom/net.WriteDouble.lua @@ -0,0 +1,6 @@ +---Appends a double-precision number to the current net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteDouble +---@param double number The double to be sent +---@[net_payload("write", "double")] +function net.WriteDouble(double) end diff --git a/custom/net.WriteEntity.lua b/custom/net.WriteEntity.lua new file mode 100644 index 00000000..0087b7e5 --- /dev/null +++ b/custom/net.WriteEntity.lua @@ -0,0 +1,8 @@ +---Appends an entity to the current net message using its [Entity:EntIndex](https://wiki.facepunch.com/gmod/Entity:EntIndex). +--- +--- See [net.ReadEntity](https://wiki.facepunch.com/gmod/net.ReadEntity) for the function to read the entity. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteEntity +---@param entity Entity The entity to be sent. +---@[net_payload("write", "entity")] +function net.WriteEntity(entity) end diff --git a/custom/net.WriteFloat.lua b/custom/net.WriteFloat.lua new file mode 100644 index 00000000..9fd27e52 --- /dev/null +++ b/custom/net.WriteFloat.lua @@ -0,0 +1,6 @@ +---Appends a float (number with decimals) to the current net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteFloat +---@param float number The float to be sent. +---@[net_payload("write", "float")] +function net.WriteFloat(float) end diff --git a/custom/net.WriteInt.lua b/custom/net.WriteInt.lua new file mode 100644 index 00000000..b9f0c84f --- /dev/null +++ b/custom/net.WriteInt.lua @@ -0,0 +1,49 @@ +---Appends a signed integer - a whole number, positive/negative - to the current net message. Can be read back with [net.ReadInt](https://wiki.facepunch.com/gmod/net.ReadInt) on the receiving end. +--- +--- Use [net.WriteUInt](https://wiki.facepunch.com/gmod/net.WriteUInt) to send an unsigned number (that you know will **never** be negative). Use [net.WriteFloat](https://wiki.facepunch.com/gmod/net.WriteFloat) for a non-whole number (e.g. `2.25`). +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteInt +---@param integer number The integer to be sent. +---@[call_arg("gmod.net_payload", "bits")] +---@param bitCount number The amount of bits the number consists of. This must be **32** or less. +--[[ + +If you are unsure what to set, just set it to `32`. + +Consult the table below to determine the bit count you need: + +| Bit Count | Minimum value | Maximum value | +|-----------|:--------------:|:--------------:| +| 3 | -4 | 3 | +| 4 | -8 | 7 | +| 5 | -16 | 15 | +| 6 | -32 | 31 | +| 7 | -64 | 63 | +| 8 | -128 | 127 | +| 9 | -256 | 255 | +| 10 | -512 | 511 | +| 11 | -1,024 | 1,023 | +| 12 | -2,048 | 2,047 | +| 13 | -4,096 | 4,095 | +| 14 | -8,192 | 8,191 | +| 15 | -16,384 | 16,383 | +| 16 | -32,768 | 32,767 | +| 17 | -65,536 | 65,535 | +| 18 | -131,072 | 131,071 | +| 19 | -262,144 | 262,143 | +| 20 | -524,288 | 524,287 | +| 21 | -1,048,576 | 1,048,575 | +| 22 | -2,097,152 | 2,097,151 | +| 23 | -4,194,304 | 4,194,303 | +| 24 | -8,388,608 | 8,388,607 | +| 25 | -16,777,216 | 16,777,215 | +| 26 | -33,554,432 | 33,554,431 | +| 27 | -67,108,864 | 67,108,863 | +| 28 | -134,217,728 | 134,217,727 | +| 29 | -268,435,456 | 268,435,455 | +| 30 | -536,870,912 | 536,870,911 | +| 31 | -1,073,741,824 | 1,073,741,823 | +| 32 | -2,147,483,648 | 2,147,483,647 | +--]] +---@[net_payload("write", "int")] +function net.WriteInt(integer, bitCount) end diff --git a/custom/net.WriteMatrix.lua b/custom/net.WriteMatrix.lua new file mode 100644 index 00000000..f20ee727 --- /dev/null +++ b/custom/net.WriteMatrix.lua @@ -0,0 +1,6 @@ +---Writes a [VMatrix](https://wiki.facepunch.com/gmod/VMatrix) to the current net message. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteMatrix +---@param matrix VMatrix The matrix to be sent. +---@[net_payload("write", "matrix")] +function net.WriteMatrix(matrix) end diff --git a/custom/net.WriteNormal.lua b/custom/net.WriteNormal.lua new file mode 100644 index 00000000..39b30e0e --- /dev/null +++ b/custom/net.WriteNormal.lua @@ -0,0 +1,8 @@ +---Writes a normalized/direction vector ( Vector with length of 1 ) to the net message. +--- +--- This function uses less bandwidth compared to [net.WriteVector](https://wiki.facepunch.com/gmod/net.WriteVector) and will not send vectors with length of > 1 properly. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteNormal +---@param normal Vector The normalized/direction vector to be send. +---@[net_payload("write", "normal")] +function net.WriteNormal(normal) end diff --git a/custom/net.WritePlayer.lua b/custom/net.WritePlayer.lua new file mode 100644 index 00000000..ced10f90 --- /dev/null +++ b/custom/net.WritePlayer.lua @@ -0,0 +1,8 @@ +---Appends a player entity to the current net message using its [Entity:EntIndex](https://wiki.facepunch.com/gmod/Entity:EntIndex). This saves a small amount of network bandwidth over [net.WriteEntity](https://wiki.facepunch.com/gmod/net.WriteEntity). +--- +--- See [net.ReadPlayer](https://wiki.facepunch.com/gmod/net.ReadPlayer) for the function to read the entity. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WritePlayer +---@param ply Player The player to be sent. +---@[net_payload("write", "player")] +function net.WritePlayer(ply) end diff --git a/custom/net.WriteString.lua b/custom/net.WriteString.lua new file mode 100644 index 00000000..9a8e3905 --- /dev/null +++ b/custom/net.WriteString.lua @@ -0,0 +1,10 @@ +---Appends a string to the current net message. The size of the written data is 8 bits for every ASCII character in the string + 8 bits for the null terminator. +--- +--- The maximum allowed length of a single written string is **65532 characters**. (aka the limit of the net message itself) +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteString +---@param string string The string to be sent. +--- +--- The input will be terminated at the first null byte if one is present. See net.WriteData if you wish to write binary data. +---@[net_payload("write", "string")] +function net.WriteString(string) end diff --git a/custom/net.WriteTable.lua b/custom/net.WriteTable.lua new file mode 100644 index 00000000..82c4be2d --- /dev/null +++ b/custom/net.WriteTable.lua @@ -0,0 +1,17 @@ +---Appends a table to the current net message. Adds **16 extra bits** per key/value pair, so you're better off writing each individual key/value as the exact type if possible. +--- +--- **WARNING**: All net messages have a **64kb** buffer. This function will not check or error when that buffer is overflown. You might want to consider using [util.TableToJSON](https://wiki.facepunch.com/gmod/util.TableToJSON) and [util.Compress](https://wiki.facepunch.com/gmod/util.Compress) and send the resulting string in **60kb** chunks, doing the opposite on the receiving end. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteTable +---@param table table The table to be sent. +--- +--- If the table contains a `nil` key the table may not be read correctly. +--- +--- Not all objects can be sent over the network. Things like functions, [IMaterial](https://wiki.facepunch.com/gmod/IMaterial)s, etc will cause errors when reading the table from a net message. +--- +--- Each element is also limited by the constraint of the `net.Write` function for the element type. +---@param sequential? boolean Set to `true` if the input table is sequential. This saves on bandwidth, adding **8 extra bits** per key/value pair instead of 16 bits. +--- +--- To read the table you need to give [net.ReadTable](https://wiki.facepunch.com/gmod/net.ReadTable) the same value! +---@[net_payload("write", "table")] +function net.WriteTable(table, sequential) end diff --git a/custom/net.WriteType.lua b/custom/net.WriteType.lua new file mode 100644 index 00000000..c607495a --- /dev/null +++ b/custom/net.WriteType.lua @@ -0,0 +1,10 @@ +---**INTERNAL**: Used internally by [net.WriteTable](https://wiki.facepunch.com/gmod/net.WriteTable). +--- +--- Appends any type of value to the current net message. +--- +--- **NOTE**: An additional 8-bit unsigned integer indicating the type will automatically be written to the packet before the value, in order to facilitate reading with [net.ReadType](https://wiki.facepunch.com/gmod/net.ReadType). If you know the data type you are writing, use a function meant for that specific data type to reduce amount of data sent. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteType +---@param Data any The data to be sent. +---@[net_payload("write", "type")] +function net.WriteType(Data) end diff --git a/custom/net.WriteUInt.lua b/custom/net.WriteUInt.lua new file mode 100644 index 00000000..e5d54f65 --- /dev/null +++ b/custom/net.WriteUInt.lua @@ -0,0 +1,53 @@ +---Appends an unsigned integer with the specified number of bits to the current net message. +--- +--- Use [net.WriteInt](https://wiki.facepunch.com/gmod/net.WriteInt) if you want to send negative and positive numbers. Use [net.WriteFloat](https://wiki.facepunch.com/gmod/net.WriteFloat) for a non-whole number (e.g. `2.25`). +--- +--- **NOTE**: Unsigned numbers **do not** support negative numbers. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteUInt +---@param unsignedInteger number The unsigned integer to be sent. +---@[call_arg("gmod.net_payload", "bits")] +---@param bitCount number The size of the integer to be sent, in bits. Acceptable values range from any number `1` to `32` inclusive. +--[[ + +For reference: `1` = bit, `4` = nibble, `8` = byte, `16` = short, `32` = long. + +Consult the table below to determine the bit count you need. The minimum value for all bit counts is `0`. + +| Bit Count | Maximum value | +|-----------|:--------------:| +| 1 | 1 | +| 2 | 3 | +| 3 | 7 | +| 4 | 15 | +| 5 | 31 | +| 6 | 63 | +| 7 | 127 | +| 8 | 255 | +| 9 | 511 | +| 10 | 1,023 | +| 11 | 2,047 | +| 12 | 4,095 | +| 13 | 8,191 | +| 14 | 16,383 | +| 15 | 32,767 | +| 16 | 65,535 | +| 17 | 131,071 | +| 18 | 262,143 | +| 19 | 524,287 | +| 20 | 1,048,575 | +| 21 | 2,097,151 | +| 22 | 4,194,303 | +| 23 | 8,388,607 | +| 24 | 16,777,215 | +| 25 | 33,554,431 | +| 26 | 67,108,863 | +| 27 | 134,217,727 | +| 28 | 268,435,455 | +| 29 | 536,870,911 | +| 30 | 1,073,741,823 | +| 31 | 2,147,483,647 | +| 32 | 4,294,967,295 | +--]] +---@[net_payload("write", "uint")] +function net.WriteUInt(unsignedInteger, bitCount) end diff --git a/custom/net.WriteUInt64.lua b/custom/net.WriteUInt64.lua new file mode 100644 index 00000000..05d3331e --- /dev/null +++ b/custom/net.WriteUInt64.lua @@ -0,0 +1,18 @@ +---Appends an unsigned integer with 64 bits to the current net message. +--- +--- The limit for an uint64 is 18'446'744'073'709'551'615. +--- Everything above the limit will be set to the limit. +--- +--- Unsigned numbers **do not** support negative numbers. +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteUInt64 +---@param uint64 string The 64 bit value to be sent. Can be a number. +--- +--- Since Lua cannot store full 64-bit integers, this function takes a string. It is mainly aimed at usage with [Player:SteamID64](https://wiki.facepunch.com/gmod/Player:SteamID64). +--- +--- If your input is a number and not a string, it won't be networked correctly as soon as it has more than 13 digits. +--- This is because Lua represents numbers over 13 digits as `1e+14`(`100 000 000 000 000`) +--- You can do something like this to convert it to a string: `string.format("%.0f", number)`. +--- If you try to use [Global.tostring](https://wiki.facepunch.com/gmod/Global.tostring) it will fail because it will create a result something like `1e+14` which doesn't work. +---@[net_payload("write", "uint64")] +function net.WriteUInt64(uint64) end diff --git a/custom/net.WriteVector.lua b/custom/net.WriteVector.lua new file mode 100644 index 00000000..a5358845 --- /dev/null +++ b/custom/net.WriteVector.lua @@ -0,0 +1,7 @@ +---Appends a vector to the current net message. +--- Vectors sent by this function are compressed, which may result in precision loss. XYZ components greater than `16384` or less than `-16384` are irrecoverably altered (most significant bits are trimmed) and precision after the decimal point is 1 digit (5 bits). +---@realm shared +---@source https://wiki.facepunch.com/gmod/net.WriteVector +---@param vector Vector The vector to be sent. +---@[net_payload("write", "vector")] +function net.WriteVector(vector) end