213 lines
6 KiB
Lua
213 lines
6 KiB
Lua
DarkRP = DarkRP or {}
|
|
DarkRP.PLAYER = FindMetaTable 'Player'
|
|
DarkRP.VECTOR = FindMetaTable 'Vector'
|
|
|
|
DarkRP.MetaName = "DarkRP"
|
|
|
|
-- Variables that maintain the existing stubs and hooks
|
|
local stubs = {}
|
|
local hookStubs = {}
|
|
|
|
-- Contains the functions that the hooks call by default
|
|
DarkRP.hooks = {}
|
|
|
|
-- Delay the calling of methods until the functions are implemented
|
|
local delayedCalls = {}
|
|
|
|
local returnsLayout, isreturns
|
|
local parameterLayout, isparameters
|
|
local isdeprecated
|
|
local checkStub
|
|
|
|
local hookLayout
|
|
|
|
local realm -- State variable to manage the realm of the stubs
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Methods that check whether certain fields are valid
|
|
---------------------------------------------------------------------------*/
|
|
isreturns = function(tbl)
|
|
if not istable(tbl) then return false end
|
|
for k,v in pairs(tbl) do
|
|
if not checkStub(v, returnsLayout) then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
isparameters = function(tbl)
|
|
if not istable(tbl) then return false end
|
|
for k,v in pairs(tbl) do
|
|
if not checkStub(v, parameterLayout) then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
isdeprecated = function(val)
|
|
return val == nil or isstring(val)
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
The layouts of stubs
|
|
---------------------------------------------------------------------------*/
|
|
local stubLayout = {
|
|
name = isstring,
|
|
description = isstring,
|
|
deprecated = isdeprecated,
|
|
parameters = isparameters, -- the parameters of a method
|
|
returns = isreturns, -- the return values of a method
|
|
metatable = istable -- DarkRP, Player, Entity, Vector, ...
|
|
}
|
|
|
|
hookLayout = {
|
|
name = isstring,
|
|
description = isstring,
|
|
deprecated = isdeprecated,
|
|
parameters = isreturns, -- doesn't have the 'optional' field
|
|
returns = isreturns,
|
|
}
|
|
|
|
returnsLayout = {
|
|
name = isstring,
|
|
description = isstring,
|
|
type = isstring
|
|
}
|
|
|
|
parameterLayout = {
|
|
name = isstring,
|
|
description = isstring,
|
|
type = isstring,
|
|
optional = isbool
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Check the validity of a stub
|
|
---------------------------------------------------------------------------*/
|
|
checkStub = function(tbl, stub)
|
|
if not istable(tbl) then return false, "table" end
|
|
|
|
for name, check in pairs(stub) do
|
|
if not check(tbl[name]) then
|
|
return false, name
|
|
end
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
When a stub is called, the calling of the method is delayed
|
|
---------------------------------------------------------------------------*/
|
|
local function notImplemented(name, args, thisFunc)
|
|
if stubs[name] and stubs[name].metatable[name] ~= thisFunc then -- when calling the not implemented function after the function was implemented
|
|
return stubs[name].metatable[name](unpack(args))
|
|
end
|
|
delayedCalls[name] = delayedCalls[name] or {}
|
|
table.insert(delayedCalls[name], args)
|
|
|
|
return nil -- no return value because the method is not implemented
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Generate a stub
|
|
---------------------------------------------------------------------------*/
|
|
function DarkRP.stub(tbl)
|
|
local isStub, field = checkStub(tbl, stubLayout)
|
|
if not isStub then
|
|
error("Invalid DarkRP method stub! Field \"" .. field .. "\" is invalid!", 2)
|
|
end
|
|
|
|
tbl.realm = tbl.realm or realm
|
|
stubs[tbl.name] = tbl
|
|
|
|
local function retNotImpl(...)
|
|
return notImplemented(tbl.name, {...}, retNotImpl)
|
|
end
|
|
|
|
return retNotImpl
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Generate a hook stub
|
|
---------------------------------------------------------------------------*/
|
|
function DarkRP.hookStub(tbl)
|
|
local isStub, field = checkStub(tbl, hookLayout)
|
|
if not isStub then
|
|
error("Invalid DarkRP hook! Field \"" .. field .. "\" is invalid!", 2)
|
|
end
|
|
|
|
tbl.realm = tbl.realm or realm
|
|
hookStubs[tbl.name] = tbl
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Retrieve the stubs
|
|
---------------------------------------------------------------------------*/
|
|
function DarkRP.getStubs()
|
|
return table.Copy(stubs)
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Retrieve the hooks
|
|
---------------------------------------------------------------------------*/
|
|
function DarkRP.getHooks()
|
|
return table.Copy(hookStubs)
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Call the cached methods
|
|
---------------------------------------------------------------------------*/
|
|
function DarkRP.finish()
|
|
local calls = table.Copy(delayedCalls) -- Loop through a copy, so the notImplemented function doesn't get called again
|
|
for name, log in pairs(calls) do
|
|
if not stubs[name] then ErrorNoHalt("Calling non-existing stub \"" .. name .. "\"") continue end
|
|
|
|
for _, args in pairs(log) do
|
|
stubs[name].metatable[name](unpack(args))
|
|
end
|
|
end
|
|
|
|
delayedCalls = {}
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Load the interface files
|
|
---------------------------------------------------------------------------*/
|
|
local function loadInterfaces()
|
|
local root = GM.FolderName .. "/gamemode/modules"
|
|
|
|
local _, folders = file.Find(root .. "/*", "LUA")
|
|
|
|
ENTITY = FindMetaTable("Entity")
|
|
PLAYER = FindMetaTable("Player")
|
|
VECTOR = FindMetaTable("Vector")
|
|
|
|
for _, folder in SortedPairs(folders, true) do
|
|
local interfacefile = string.format("%s/%s/%s_interface.lua", root, folder, "%s")
|
|
local client = string.format(interfacefile, "cl")
|
|
local shared = string.format(interfacefile, "sh")
|
|
local server = string.format(interfacefile, "sv")
|
|
|
|
if file.Exists(shared, "LUA") then
|
|
if SERVER then AddCSLuaFile(shared) end
|
|
realm = "Shared"
|
|
include(shared)
|
|
end
|
|
|
|
if SERVER and file.Exists(client, "LUA") then
|
|
AddCSLuaFile(client)
|
|
end
|
|
|
|
if SERVER and file.Exists(server, "LUA") then
|
|
realm = "Server"
|
|
include(server)
|
|
end
|
|
|
|
if CLIENT and file.Exists(client, "LUA") then
|
|
realm = "Client"
|
|
include(client)
|
|
end
|
|
end
|
|
|
|
ENTITY, PLAYER, VECTOR = nil, nil, nil
|
|
end
|
|
loadInterfaces()
|