Initial commit
This commit is contained in:
210
gamemodes/darkrp/gamemode/libraries/interfaceloader.lua
Normal file
210
gamemodes/darkrp/gamemode/libraries/interfaceloader.lua
Normal file
@@ -0,0 +1,210 @@
|
||||
module("DarkRP", package.seeall)
|
||||
|
||||
MetaName = "DarkRP"
|
||||
|
||||
-- Variables that maintain the existing stubs and hooks
|
||||
local stubs = {}
|
||||
local hookStubs = {}
|
||||
|
||||
-- Contains the functions that the hooks call by default
|
||||
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 _, 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 _, 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
|
||||
table.insert(delayedCalls, {name = name, args = args})
|
||||
|
||||
return nil -- no return value because the method is not implemented
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Generate a stub
|
||||
---------------------------------------------------------------------------]]
|
||||
function 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 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 getStubs()
|
||||
return table.Copy(stubs)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Retrieve the hooks
|
||||
---------------------------------------------------------------------------]]
|
||||
function getHooks()
|
||||
return table.Copy(hookStubs)
|
||||
end
|
||||
|
||||
--[[---------------------------------------------------------------------------
|
||||
Call the cached methods
|
||||
---------------------------------------------------------------------------]]
|
||||
function finish()
|
||||
local calls = table.Copy(delayedCalls) -- Loop through a copy, so the notImplemented function doesn't get called again
|
||||
for _, tbl in ipairs(calls) do
|
||||
local name = tbl.name
|
||||
|
||||
if not stubs[name] then ErrorNoHalt("Calling non-existing stub \"" .. name .. "\"") continue end
|
||||
|
||||
stubs[name].metatable[name](unpack(tbl.args))
|
||||
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()
|
||||
Reference in New Issue
Block a user