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 \
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
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__/cli-generate-lua.spec.ts b/__tests__/cli-generate-lua.spec.ts
index 9a87f689..15e27e35 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(),
@@ -36,6 +36,148 @@ 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('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');
@@ -65,11 +207,15 @@ 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';
const result = spawnSync(
- `${command} run generate-lua -- --output "${outputPath}" --customOverrides ./custom`,
+ `${command} run generate-lua -- --output "${outputPath}" --custom-overrides ./custom`,
[],
{
cwd: process.cwd(),
@@ -80,18 +226,156 @@ 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 });
}
});
+
+ 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/__tests__/custom-annotations.spec.ts b/__tests__/custom-annotations.spec.ts
index c46671f3..cc5e0c44 100644
--- a/__tests__/custom-annotations.spec.ts
+++ b/__tests__/custom-annotations.spec.ts
@@ -2,6 +2,12 @@ import fs from 'fs';
import path from 'path';
describe('custom and plugin annotation smoke checks', () => {
+ 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');
+
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');
@@ -18,106 +24,304 @@ 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 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 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 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');
-
- 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(dCheckBoxLabel).toMatch(/---@class DCheckBoxLabel : Panel/);
- expect(dCheckBoxLabel).toMatch(/---@field Button DCheckBox/);
- expect(dCheckBoxLabel).toMatch(/---@field Label DLabel/);
-
- expect(dHtmlControls).toMatch(/---@class DHTMLControls : Panel/);
- expect(dHtmlControls).toMatch(/---@field AddressBar DTextEntry/);
-
- expect(dPanelList).toMatch(/---@class DPanelList : DPanel/);
- expect(dPanelList).toMatch(/---@field Items Panel\[]/);
-
- 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(/---@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(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(tableCopy).toMatch(/---@generic T : table/);
- expect(tableCopy).toMatch(/---@param originalTable T/);
- expect(tableCopy).toMatch(/---@return T/);
-
- expect(contentContainer).toMatch(/---@class ContentContainer : DIconLayout/);
- 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/);
- });
-
- 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('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('sandbox overrides preserve their realm-specific declarations', () => {
+ 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');
});
+ 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/);
+ });
+
+ test('unsupported menu-only custom overrides stay absent', () => {
+ const removedFiles = [
+ 'UGCPublishWindow.DoPublish.lua',
+ 'steamworks.SetFavorite.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('function WorkshopFileBase:Arm');
+ expect(workshopFileBaseOutput).not.toContain('function WorkshopFileBase:DownloadAndArm');
+ });
+
+ 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)');
+ });
+
+ 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('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) 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 panelLua = readOutput('panel.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 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];
+ 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(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(
+ 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(
+ /---@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('base registries expose their runtime call shapes', () => {
+ const duplicatorLua = readOutput('duplicator.lua');
+
+ expect(duplicatorLua).toContain(
+ '---@type table',
+ );
+ 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');
+
+ 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');
+
+ 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/__tests__/custom-class-override.spec.ts b/__tests__/custom-class-override.spec.ts
index 01887370..fdde7735 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,192 @@ 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);
+ });
+
+ 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/__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/__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');
});
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/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/ConVar.GetString.lua b/custom/ConVar.GetString.lua
new file mode 100644
index 00000000..955dfe9d
--- /dev/null
+++ b/custom/ConVar.GetString.lua
@@ -0,0 +1,6 @@
+---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
+---@return string # The current console variable value as a string.
+function ConVar:GetString() end
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/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/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/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/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/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/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/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/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/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/DForm.TextEntry.lua b/custom/DForm.TextEntry.lua
new file mode 100644
index 00000000..52b2fd74
--- /dev/null
+++ b/custom/DForm.TextEntry.lua
@@ -0,0 +1,8 @@
+---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
+---@return DLabel # The label created for the text entry.
+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/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/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/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
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/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.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/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/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
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
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
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/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/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/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/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/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
new file mode 100644
index 00000000..ca10b488
--- /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), or nil if no active tab is set.
+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/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
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/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/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
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.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.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.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/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/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/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.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.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_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..6475b588
--- /dev/null
+++ b/custom/DTree_Node.OnNodeSelected.lua
@@ -0,0 +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 https://wiki.facepunch.com/gmod/DTree_Node:OnNodeSelected
+---@param node DTree_Node
+function DTree_Node:OnNodeSelected(node) 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/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/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.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/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/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.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/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/Entity.NetworkVar.lua b/custom/Entity.NetworkVar.lua
new file mode 100644
index 00000000..cc386a21
--- /dev/null
+++ b/custom/Entity.NetworkVar.lua
@@ -0,0 +1,13 @@
+---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.
+---@[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
new file mode 100644
index 00000000..5ec1707e
--- /dev/null
+++ b/custom/Entity.NetworkVarElement.lua
@@ -0,0 +1,14 @@
+---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 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.
+function Entity:NetworkVarElement(type, slot, element, name, extended) 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.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/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/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/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.Color.lua b/custom/Global.Color.lua
new file mode 100644
index 00000000..b82b777f
--- /dev/null
+++ b/custom/Global.Color.lua
@@ -0,0 +1,14 @@
+---Creates a new Color.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/Global.Color
+---@[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.ColorToHSL.lua b/custom/Global.ColorToHSL.lua
new file mode 100644
index 00000000..39fe5e5b
--- /dev/null
+++ b/custom/Global.ColorToHSL.lua
@@ -0,0 +1,9 @@
+---Converts a Color into HSL color space.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/Global.ColorToHSL
+---@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..2c440585
--- /dev/null
+++ b/custom/Global.ColorToHSV.lua
@@ -0,0 +1,9 @@
+---Converts a Color into HSV color space.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/Global.ColorToHSV
+---@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/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.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.CreateClientConVar.lua b/custom/Global.CreateClientConVar.lua
new file mode 100644
index 00000000..9fc30dfc
--- /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
+---@[call_arg("gmod.convar", "define_client")]
+---@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..5416bcc7 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
+---@[call_arg("gmod.convar", "define_server")]
---@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..dc51d60f
--- /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
+---@[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
new file mode 100644
index 00000000..d3fa3a5e
--- /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
+---@[call_arg("gmod.gamemode", "reference")]
+---@param base string Base gamemode folder name.
+function _G.DeriveGamemode(base) end
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
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/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.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
diff --git a/custom/Global.GetConVar.lua b/custom/Global.GetConVar.lua
new file mode 100644
index 00000000..0dcd3018
--- /dev/null
+++ b/custom/Global.GetConVar.lua
@@ -0,0 +1,11 @@
+---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
+---@[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
diff --git a/custom/Global.HSLToColor.lua b/custom/Global.HSLToColor.lua
new file mode 100644
index 00000000..46ca4557
--- /dev/null
+++ b/custom/Global.HSLToColor.lua
@@ -0,0 +1,6 @@
+---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L76-L105
+---@return Color
+---@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..04f39710
--- /dev/null
+++ b/custom/Global.HSVToColor.lua
@@ -0,0 +1,6 @@
+---@source https://github.com/Facepunch/garrysmod/blob/master/garrysmod/lua/includes/util/color.lua#L45-L74
+---@return Color
+---@param h number
+---@param s number
+---@param v number
+function _G.HSVToColor(h, s, v) end
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.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.isentity.lua b/custom/Global.IsEntity.lua
similarity index 100%
rename from custom/Global.isentity.lua
rename to custom/Global.IsEntity.lua
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.IsValid.lua b/custom/Global.IsValid.lua
new file mode 100644
index 00000000..34061b72
--- /dev/null
+++ b/custom/Global.IsValid.lua
@@ -0,0 +1,11 @@
+---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 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(object) end
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
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/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.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/Global.error(lowercase).lua b/custom/Global.error(lowercase).lua
new file mode 100644
index 00000000..fae0237a
--- /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 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/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.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
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
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/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
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/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/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 = {}
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/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 = {}
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/Panel.Add.lua b/custom/Panel.Add.lua
index 0e60e0a0..f1784cf2 100644
--- a/custom/Panel.Add.lua
+++ b/custom/Panel.Add.lua
@@ -3,7 +3,11 @@
---@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.
----@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
+---@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/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.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.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/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/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/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/Panel.SetParent.lua b/custom/Panel.SetParent.lua
new file mode 100644
index 00000000..ee889042
--- /dev/null
+++ b/custom/Panel.SetParent.lua
@@ -0,0 +1,8 @@
+---Sets the parent of the panel.
+---@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/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
diff --git a/custom/Panel.SetSkin.lua b/custom/Panel.SetSkin.lua
new file mode 100644
index 00000000..e156c8bd
--- /dev/null
+++ b/custom/Panel.SetSkin.lua
@@ -0,0 +1,7 @@
+---Sets the derma skin that the panel object will use.
+---@realm client
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/Panel:SetSkin
+---@[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/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/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
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/PropertyAdd.lua b/custom/PropertyAdd.lua
new file mode 100644
index 00000000..9b58faaf
--- /dev/null
+++ b/custom/PropertyAdd.lua
@@ -0,0 +1,24 @@
+---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 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 = {}
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/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/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/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/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
new file mode 100644
index 00000000..6d032e18
--- /dev/null
+++ b/custom/TOOL.LeftClick.lua
@@ -0,0 +1,7 @@
+---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
+---@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.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/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.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
new file mode 100644
index 00000000..fe5e2628
--- /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 gmod_tool # The tool gun weapon. Always valid after Init.
+function Tool:GetWeapon() end
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 = {}
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 = {}
diff --git a/custom/WEAPON.AdjustMouseSensitivity.lua b/custom/WEAPON.AdjustMouseSensitivity.lua
new file mode 100644
index 00000000..5164b8cf
--- /dev/null
+++ b/custom/WEAPON.AdjustMouseSensitivity.lua
@@ -0,0 +1,10 @@
+---Called to adjust player mouse sensitivity while this weapon is active.
+---@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(defaultSensitivity, localFOV, defaultFOV) end
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.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
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
diff --git a/custom/Weapon.CheckLimit.lua b/custom/Weapon.CheckLimit.lua
new file mode 100644
index 00000000..851cd476
--- /dev/null
+++ b/custom/Weapon.CheckLimit.lua
@@ -0,0 +1,12 @@
+---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
+
+---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/Weapon.GetToolObject.lua b/custom/Weapon.GetToolObject.lua
new file mode 100644
index 00000000..e3d6f126
--- /dev/null
+++ b/custom/Weapon.GetToolObject.lua
@@ -0,0 +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|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/_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..604c969c 100644
--- a/custom/class.ContentContainer.lua
+++ b/custom/class.ContentContainer.lua
@@ -1,5 +1,12 @@
----@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
function ContentContainer:SetTriggerSpawnlistChange(trigger) end
+
+---@param pnl Panel
+function ContentContainer:Add(pnl) end
diff --git a/custom/class.ContentSidebar.lua b/custom/class.ContentSidebar.lua
new file mode 100644
index 00000000..04578c28
--- /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="PopulateContent" The hook name to populate content.
+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.ContextBase.lua b/custom/class.ContextBase.lua
new file mode 100644
index 00000000..8c84c4b6
--- /dev/null
+++ b/custom/class.ContextBase.lua
@@ -0,0 +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.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.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.DColorCube.lua b/custom/class.DColorCube.lua
new file mode 100644
index 00000000..fc6cb6c3
--- /dev/null
+++ b/custom/class.DColorCube.lua
@@ -0,0 +1,8 @@
+---@class DColorCube : DSlider
+---@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.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..3b83f6ef 100644
--- a/custom/class.DFileBrowser.lua
+++ b/custom/class.DFileBrowser.lua
@@ -1,4 +1,26 @@
----@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.
+---@class DFileBrowser : Panel
+--- The horizontal divider separating the tree and file list.
+---@field Divider DHorizontalDivider
+--- The directory tree panel.
+---@field Tree DTree
+--- 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.
+---@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
+--- 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.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.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.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..17cbb9fe 100644
--- a/custom/class.DHTMLControls.lua
+++ b/custom/class.DHTMLControls.lua
@@ -1,7 +1,25 @@
---@class DHTMLControls : Panel
+--- The back navigation button.
+---@field BackButton DImageButton
+--- The forward navigation button.
+---@field ForwardButton DImageButton
+--- The refresh/reload button.
+---@field RefreshButton DImageButton
+--- The home button.
+---@field HomeButton DImageButton
+--- The stop button.
+---@field StopButton DImageButton
+--- The address bar text entry.
---@field AddressBar DTextEntry
----@field BackButton DButton
----@field ForwardButton DButton
----@field RefreshButton DButton
----@field StopButton DButton
+--- 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 History table
+---@field BorderSize number
+---@field BackgroundColor Color
local DHTMLControls = {}
diff --git a/custom/class.DHorizontalScroller.lua b/custom/class.DHorizontalScroller.lua
new file mode 100644
index 00000000..71474458
--- /dev/null
+++ b/custom/class.DHorizontalScroller.lua
@@ -0,0 +1,17 @@
+---@class DHorizontalScroller : Panel
+---@field Panels Panel[]
+---@field OffsetX number
+---@field FrameTime number
+---@field pnlCanvas DDragBase
+---@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/class.DImage.lua b/custom/class.DImage.lua
new file mode 100644
index 00000000..54e5cdcb
--- /dev/null
+++ b/custom/class.DImage.lua
@@ -0,0 +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
new file mode 100644
index 00000000..42b8cb05
--- /dev/null
+++ b/custom/class.DListView.lua
@@ -0,0 +1,13 @@
+---@class DListView : DPanel
+---@field Columns DListView_Column[]
+---@field Lines DListView_Line[]
+---@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
new file mode 100644
index 00000000..c1c34ddc
--- /dev/null
+++ b/custom/class.DMenuBar.lua
@@ -0,0 +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
new file mode 100644
index 00000000..7f8bbeab
--- /dev/null
+++ b/custom/class.DMenuOption.lua
@@ -0,0 +1,9 @@
+---@class DMenuOption : DButton
+---@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/class.DModelPanel.lua b/custom/class.DModelPanel.lua
new file mode 100644
index 00000000..aac5bb65
--- /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.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..c2dac523
--- /dev/null
+++ b/custom/class.DNotify.lua
@@ -0,0 +1,6 @@
+---@class DNotify : Panel
+---@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.DNumPad.lua b/custom/class.DNumPad.lua
new file mode 100644
index 00000000..f0a249a5
--- /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 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.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.DPanelList.lua b/custom/class.DPanelList.lua
index 92a3a27b..b7caa9e3 100644
--- a/custom/class.DPanelList.lua
+++ b/custom/class.DPanelList.lua
@@ -1,4 +1,16 @@
---@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 = {}
+
+---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
new file mode 100644
index 00000000..10e842c6
--- /dev/null
+++ b/custom/class.DPanelSelect.lua
@@ -0,0 +1,16 @@
+---@class DPanelSelect : DPanelList
+---@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
+
+---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
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.DPropertySheet.lua b/custom/class.DPropertySheet.lua
index e15b4506..80f6c8ad 100644
--- a/custom/class.DPropertySheet.lua
+++ b/custom/class.DPropertySheet.lua
@@ -1,4 +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.
-local DPropertySheet = {}
\ No newline at end of file
+---@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.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.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.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..21cc06cd
--- /dev/null
+++ b/custom/class.DTree_Node.lua
@@ -0,0 +1,21 @@
+---@class DTree_Node : DPanel
+---@field Label DTree_Node_Button
+---@field Expander DExpandButton
+---@field Icon DImage
+---@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 = {}
+
+---Returns the child node at the given index.
+---@realm client
+---@realm menu
+---@param num number The zero-based child node index.
+---@return Panel? # The child panel, if any.
+function DTree_Node:GetChildNode(num) end
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 = {}
diff --git a/custom/class.DermaAnimation.lua b/custom/class.DermaAnimation.lua
new file mode 100644
index 00000000..2786e307
--- /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.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/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.EngineEntities.lua b/custom/class.EngineEntities.lua
new file mode 100644
index 00000000..8b94207d
--- /dev/null
+++ b/custom/class.EngineEntities.lua
@@ -0,0 +1,29 @@
+---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_axis_arrow : Entity
+---@class widget_axis_disc : Entity
+---@class widget_bone : Entity
+---@class widget_bones : 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 = {}
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.GM.lua b/custom/class.GM.lua
new file mode 100644
index 00000000..713ddebe
--- /dev/null
+++ b/custom/class.GM.lua
@@ -0,0 +1,26 @@
+--- 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.
+---@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
+---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.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.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.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.SKIN.lua b/custom/class.SKIN.lua
index 98f2c7c3..63194ef0 100644
--- a/custom/class.SKIN.lua
+++ b/custom/class.SKIN.lua
@@ -2,8 +2,187 @@
--- 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
+
+---@class SKINTexScroller
+---@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.
---@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.SkeletonConvertor.lua b/custom/class.SkeletonConvertor.lua
new file mode 100644
index 00000000..875933e5
--- /dev/null
+++ b/custom/class.SkeletonConvertor.lua
@@ -0,0 +1,12 @@
+---@meta
+
+---@class ModelEntity
+---@field GetModel fun(self: ModelEntity): string
+
+---@class SkeletonConvertor
+---@field IsApplicable fun(self: SkeletonConvertor, ent: ModelEntity): boolean
+---@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)
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
new file mode 100644
index 00000000..58697387
--- /dev/null
+++ b/custom/class.SpawnMenu.lua
@@ -0,0 +1,25 @@
+---@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? 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 = {}
+
+---@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.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..73dc2e18
--- /dev/null
+++ b/custom/class.Tool.lua
@@ -0,0 +1,64 @@
+---
+--- 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. 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.
+---@alias ToolObjects table
+
+---@class Tool
+---@field Mode string The tool mode string (e.g. "weld", "balloon").
+---@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.
+---@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, ...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".
+---@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 gmod_tool # The tool gun weapon.
+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.ToolObj.lua b/custom/class.ToolObj.lua
new file mode 100644
index 00000000..a5b13763
--- /dev/null
+++ b/custom/class.ToolObj.lua
@@ -0,0 +1,21 @@
+---@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.
+---@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
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
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/class.Weapon.lua b/custom/class.Weapon.lua
new file mode 100644
index 00000000..b4ee5ec1
--- /dev/null
+++ b/custom/class.Weapon.lua
@@ -0,0 +1,33 @@
+---@class Weapon : Entity
+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
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_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_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
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 = {}
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.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_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_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
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/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/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_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 = {}
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/concommand.Add.lua b/custom/concommand.Add.lua
new file mode 100644
index 00000000..19b81695
--- /dev/null
+++ b/custom/concommand.Add.lua
@@ -0,0 +1,12 @@
+---Creates a console command that runs the supplied callback.
+---@realm shared
+---@realm menu
+---@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
+---@param flags? FCVAR|number[]
+function concommand.Add(name, callback, autoComplete, helpText, flags) end
diff --git a/custom/constraint.Elastic.lua b/custom/constraint.Elastic.lua
new file mode 100644
index 00000000..38a8fece
--- /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|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.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.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/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.Weld.lua b/custom/constraint.Weld.lua
new file mode 100644
index 00000000..481ea8df
--- /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|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
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
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/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/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
+
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
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
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
diff --git a/custom/derma.DefineControl.lua b/custom/derma.DefineControl.lua
new file mode 100644
index 00000000..1660641a
--- /dev/null
+++ b/custom/derma.DefineControl.lua
@@ -0,0 +1,14 @@
+---Defines a new Derma control with an optional base.
+---@realm client
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/derma.DefineControl
+---@generic T: Panel
+---@[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")]
+---@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
new file mode 100644
index 00000000..3fd35b3d
--- /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
+---@[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.
+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..c317eb63
--- /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
+---@[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/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/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
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/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
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
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 = {}
+
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.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/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/ents.Create.lua b/custom/ents.Create.lua
index 74bbe988..5c1345aa 100644
--- a/custom/ents.Create.lua
+++ b/custom/ents.Create.lua
@@ -3,6 +3,35 @@
--- 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_cameraprop"
+---| "gmod_wheel"
+---| "gmod_winch_controller"
+---| "hunter_flechette"
+---| "keyframe_rope"
+---| "logic_collision_pair"
+---| "phys_ballsocket"
+---| "phys_bone_follower"
+---| "phys_constraint"
+---| "phys_constraintsystem"
+---| "phys_hinge"
+---| "phys_keepupright"
+---| "phys_lengthconstraint"
+---| "phys_magnet"
+---| "phys_pulleyconstraint"
+---| "phys_ragdollconstraint"
+---| "phys_slideconstraint"
+---| "phys_spring"
+---| "phys_torque"
+---| "point_viewcontrol"
+---| "ragdoll_motion"
+---| "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.
---@return (instance) T|NULL # The created entity, or `NULL` if failed.
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
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/hook.Add.lua b/custom/hook.Add.lua
index 3843590e..9a38735c 100644
--- a/custom/hook.Add.lua
+++ b/custom/hook.Add.lua
@@ -2,7 +2,9 @@
---@realm shared
---@realm menu
---@source https://wiki.facepunch.com/gmod/hook.Add
+---@[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.Call.lua b/custom/hook.Call.lua
new file mode 100644
index 00000000..80a71a72
--- /dev/null
+++ b/custom/hook.Call.lua
@@ -0,0 +1,11 @@
+---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
+---@[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
+function hook.Call(eventName, gamemodeTable, ...) 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
diff --git a/custom/hook.Run.lua b/custom/hook.Run.lua
new file mode 100644
index 00000000..c0e6c025
--- /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
+---@[call_arg("gmod.hook", "emit")]
+---@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/list.Set.lua b/custom/list.Set.lua
new file mode 100644
index 00000000..51da0203
--- /dev/null
+++ b/custom/list.Set.lua
@@ -0,0 +1,17 @@
+---@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.
+---@param item any The value to set.
+function list.Set(identifier, key, item) end
diff --git a/custom/math.max.lua b/custom/math.max.lua
new file mode 100644
index 00000000..9887c3ff
--- /dev/null
+++ b/custom/math.max.lua
@@ -0,0 +1,7 @@
+---Returns the largest value of all arguments.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/math.max
+---@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..f4773dc7
--- /dev/null
+++ b/custom/math.min.lua
@@ -0,0 +1,7 @@
+---Returns the smallest value of all arguments.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/math.min
+---@param ... number Numbers to get the smallest from.
+---@return number # The smallest number.
+function math.min(...) 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/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
new file mode 100644
index 00000000..b3a53805
--- /dev/null
+++ b/custom/net.Receive.lua
@@ -0,0 +1,8 @@
+---Registers a callback for a network message.
+---@realm shared
+---@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.Start.lua b/custom/net.Start.lua
new file mode 100644
index 00000000..1bc40b2e
--- /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
+---@[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.
+function net.Start(messageName, unreliable) 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
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
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
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/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
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/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
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
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
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/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/string.FormattedTime.lua b/custom/string.FormattedTime.lua
new file mode 100644
index 00000000..2ec584a8
--- /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(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/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
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/timer.Create.lua b/custom/timer.Create.lua
new file mode 100644
index 00000000..4b3bfa03
--- /dev/null
+++ b/custom/timer.Create.lua
@@ -0,0 +1,11 @@
+---Creates a new named timer.
+---@realm shared
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/timer.Create
+---@[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.
+---@[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
new file mode 100644
index 00000000..c265c14f
--- /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
+---@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
diff --git a/custom/util.AddNetworkString.lua b/custom/util.AddNetworkString.lua
new file mode 100644
index 00000000..3523c774
--- /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
+---@[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/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
diff --git a/custom/vgui.Create.lua b/custom/vgui.Create.lua
index 713d9327..1fe59ba3 100644
--- a/custom/vgui.Create.lua
+++ b/custom/vgui.Create.lua
@@ -4,13 +4,16 @@
---@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.
---
--- Default panel classnames can be found on the VGUI Element List.
---
--- 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.
+---@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 1ff486ef..fe6defbf 100644
--- a/custom/vgui.CreateFromTable.lua
+++ b/custom/vgui.CreateFromTable.lua
@@ -2,8 +2,11 @@
---@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.
+---@return (instance) T? # The created panel, or `nil` if creation failed for whatever reason.
function vgui.CreateFromTable(metatable, parent, name) end
diff --git a/custom/vgui.CreateX.lua b/custom/vgui.CreateX.lua
index 3ee13eda..18ad3192 100644
--- a/custom/vgui.CreateX.lua
+++ b/custom/vgui.CreateX.lua
@@ -4,7 +4,9 @@
---@realm menu
---@source https://wiki.facepunch.com/gmod/vgui.CreateX
---@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
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/vgui.Register.lua b/custom/vgui.Register.lua
new file mode 100644
index 00000000..859b3f8b
--- /dev/null
+++ b/custom/vgui.Register.lua
@@ -0,0 +1,13 @@
+---Registers a panel for later creation via vgui.Create.
+---@realm client
+---@realm menu
+---@source https://wiki.facepunch.com/gmod/vgui.Register
+---@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.
+---@return T # The given panel table from second argument.
+function vgui.Register(classname, panelTable, baseName) end
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
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
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
diff --git a/custom/workshopfilebase.FillFileInfo.lua b/custom/workshopfilebase.FillFileInfo.lua
new file mode 100644
index 00000000..6cf01dcc
--- /dev/null
+++ b/custom/workshopfilebase.FillFileInfo.lua
@@ -0,0 +1,36 @@
+---@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.
+---@field file? string Local addon file path when available.
+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/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
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/api-writer/glua-api-writer.ts b/src/api-writer/glua-api-writer.ts
index a9b0a553..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, 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';
@@ -50,10 +51,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 +126,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.
*/
@@ -170,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))
@@ -181,24 +227,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}`;
@@ -309,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;
@@ -490,8 +543,127 @@ 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) || isPanelHookFunction(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) || isPanelHookFunction(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 => {
+ const parent = 'parent' in page ? page.parent : undefined;
+ return parent && this.resolveToCanonicalClassName(parent) !== canonicalClassName
+ ? 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);
@@ -516,15 +688,73 @@ export class GluaApiWriter {
}
public writeToDisk() {
+ 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')) {
+ moduleFileByName.set(baseName.slice(0, -4).toLowerCase(), 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) => {
- 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();
+
+ 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].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`);
+ 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.
@@ -776,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/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}`);
}
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');
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 });
}