add sborka

This commit is contained in:
2026-03-31 10:27:04 +03:00
commit f5e5f56c84
2345 changed files with 382127 additions and 0 deletions

View File

@@ -0,0 +1,507 @@
module("luadev",package.seeall)
local function CMD(who)
return CLIENT and "CMD" or who or "CMD"
end
COMMAND('run_sv',function(ply,_,script,who)
RunOnServer(script,CMD(who),MakeExtras(ply))
end,true)
COMMAND('run_sh',function(ply,_,script,who)
RunOnShared(script,CMD(who),MakeExtras(ply))
end,true)
COMMAND('run_clients',function(ply,_,script,who)
RunOnClients(script,CMD(who),MakeExtras(ply))
end,true)
COMMAND('run_self',function(ply,_,script,who)
RunOnSelf(script,CMD(who),MakeExtras(ply))
end,true)
COMMAND('run_client',function(ply,tbl,script,who)
if !tbl[1] or !tbl[2] then Print("Syntax: lua_run_client (steamid/userid/accountid/part of name) script") return end
local cl=FindPlayer(tbl[1])
if !cl then Print("Client not found!\n") return end
if CLIENT then
Print("Running script on "..tostring(cl:Name()))
end
local _, e = script:find('^%s*"[^"]+')
if e then
script = script:sub(e+2)
else
local _, e = script:find('^%s*[^%s]+%s')
if not e then
Print("Invalid Command syntax.")
return
end
script = script:sub(e)
end
script = script:Trim()
RunOnClient(script,cl,CMD(who),MakeExtras(ply))
end)
COMMAND('send_cl',function(ply,tbl,cmd,who)
if !tbl[1] or !tbl[2] then Print("Syntax: lua_send_cl (steamid/userid/accountid/part of name) \"path\"") return end
local cl=FindPlayer(tbl[1])
if !cl then Print("Client not found!\n") return end
Print("Running script on "..tostring(cl:Name()))
table.remove(tbl,1)
local path=TableToString(tbl)
local Path,searchpath=RealFilePath(path)
if !Path then Print("Could not find the file\n") return end
local content = Path and GiveFileContent(Path,searchpath)
if !content then Print("Could not read the file\n") return end
RunOnClient(content,cl,who or CMD(who),MakeExtras(ply))
end)
COMMAND('send_sv',function(ply,c,cmd,who)
local Path,searchpath=RealFilePath(c[2] and TableToString(c) or c[1])
if !Path then Print("Could not find the file\n") return end
local content = Path and GiveFileContent(Path,searchpath)
if !content then Print("Could not read the file\n") return end
local who=string.GetFileFromFilename(Path)
RunOnServer(content,who or CMD(who),MakeExtras(ply))
end)
COMMAND('send_clients',function(ply,c,cmd,who)
local Path,searchpath=RealFilePath(c[2] and TableToString(c) or c[1])
if !Path then Print("Could not find the file\n") return end
local content = Path and GiveFileContent(Path,searchpath)
if !content then Print("Could not read the file\n") return end
local who=string.GetFileFromFilename(Path)
RunOnClients(content,who or CMD(who),MakeExtras(ply))
end)
COMMAND('send_sh',function(ply,c,cmd,who)
local Path,searchpath=RealFilePath(c[2] and TableToString(c) or c[1])
if !Path then Print("Could not find the file\n") return end
local content = Path and GiveFileContent(Path,searchpath)
if !content then Print("Could not read the file\n") return end
local who=string.GetFileFromFilename(Path)
RunOnShared(content,who or CMD(who),MakeExtras(ply))
end)
local function Guess(name,Path)
if name=="init" or name=="shared" or name=="cl_init" then
local newname = Path:gsub("\\","/"):match("^.+%/([^%/]-)/.-%.lua$")
Print("Guessing identifier: "..tostring(newname or "<Failed>"))
return newname or name
end
return name
end
local function SendEFFECT(cl,Path,ply,c,cmd,who)
local who=string.GetFileFromFilename(Path)
local effectname=string.GetFileFromFilename(Path):gsub("%.lua","")
effectname = Guess(effectname,Path)
if cl then
RunOnClients(cl,who or CMD(who),MakeExtras(ply,{effect=effectname}))
end
end
COMMAND('send_effect',function(ply,c,cmd,who)
local path = c[2] and TableToString(c) or c[1]
local Path,searchpath=RealFilePath(path)
if not Path then
Print("Could not find the file\n")
return
end
local content = GiveFileContent(Path,searchpath)
if content then
local sh = content
SendEFFECT(content,Path,ply,c,cmd,who)
return
end
local cl = GiveFileContent(Path..'/init.lua',searchpath)
if cl then
SendEFFECT(cl,Path,ply,c,cmd,who)
return
else
Print("Could not find required files from the folder\n")
end
end)
local function SendSWEP(cl,sh,sv,Path,ply,c,cmd,who)
local who=string.GetFileFromFilename(Path)
local swepname=string.GetFileFromFilename(Path):gsub("%.lua","")
swepname=Guess(swepname,Path)
if cl then
RunOnClients(cl,who or CMD(who),MakeExtras(ply,{swep=swepname}))
end
if sh then
RunOnShared(sh,who or CMD(who),MakeExtras(ply,{swep=swepname}))
end
if sv then
RunOnServer(sv,who or CMD(who),MakeExtras(ply,{swep=swepname}))
end
end
COMMAND('send_wep',function(ply,c,cmd,who)
local path = c[2] and TableToString(c) or c[1]
local Path,searchpath=RealFilePath(path)
if not Path then
Print("Could not find the file\n")
return
end
local content = GiveFileContent(Path,searchpath)
if content then
local sh = content
SendSWEP(nil,sh,nil,Path,ply,c,cmd,who)
return
end
local cl = GiveFileContent(Path..'/cl_init.lua',searchpath)
local sh = GiveFileContent(Path..'/shared.lua',searchpath)
local sv = GiveFileContent(Path..'/init.lua',searchpath)
if sv or sh or cl then
SendSWEP(cl,sh,sv,Path,ply,c,cmd,who)
return
else
Print("Could not find required files from the folder\n")
end
end)
local function SendENT(cl,sh,sv,Path,ply,c,cmd,who)
local who=string.GetFileFromFilename(Path)
local entname=string.GetFileFromFilename(Path):gsub("%.lua","")
entname = Guess(entname,Path)
if cl then
RunOnClients(cl,who or CMD(who),MakeExtras(ply,{sent=entname}))
end
if sh then
RunOnShared(sh,who or CMD(who),MakeExtras(ply,{sent=entname}))
end
if sv then
RunOnServer(sv,who or CMD(who),MakeExtras(ply,{sent=entname}))
end
end
COMMAND('send_ent',function(ply,c,cmd,who)
local path = c[2] and TableToString(c) or c[1]
local Path,searchpath=RealFilePath(path)
if not Path then
Print("Could not find the file\n")
return
end
local content = GiveFileContent(Path,searchpath)
if content then
local sh = content
SendENT(nil,sh,nil,Path,ply,c,cmd,who)
return
end
local cl = GiveFileContent(Path..'/cl_init.lua',searchpath)
local sh = GiveFileContent(Path..'/shared.lua',searchpath)
local sv = GiveFileContent(Path..'/init.lua',searchpath)
if sv or sh or cl then
SendENT(cl,sh,sv,Path,ply,c,cmd,who)
return
else
Print("Could not find required files from the folder\n")
end
end)
COMMAND('watch_kill',function(ply,c,cmd,wholeline)
local watchlist = GetWatchList()
if c[1]=="" or not c[1] then
Print"Killing all"
table.Empty(watchlist)
return
end
local t= table.remove(watchlist,tonumber(c[1]))
Print("killing",t and tostring(t.path) or "(not found)")
end,true)
COMMAND('watch',function(ply,c,cmd,wholeline)
local path_orig = c[1]
table.remove(c,1)
local fpath,searchpath=RealFilePath(path_orig,findpath)
if not fpath then Print("Could not find the file\n") return end
local content = fpath and GiveFileContent(fpath,searchpath)
local time = content and fpath and FileTime(fpath,searchpath)
if not content or not time then Print("File not readable\n") return end
local found
for k,v in next,c do
if v=="PATH" then
c[k] = path_orig
found = true
end
if v=="FILE" then
c[k] = path_orig
found = true
end
if v=="RPATH" then
c[k] = fpath
found = true
end
if v=="NOPATH" then
c[k] = false
found=true
end
end
for i=#c,1,-1 do
if c[i]==false then
table.remove(c,i)
end
end
if not c[1] then
Print"Missing command, assuming lua_send_self"
c[1] = 'lua_send_self'
end
if not found then
table.insert(c,path_orig)
end
local cmdd = {}
for k,v in next,c do
cmdd[k]=('%q'):format(tostring(v))
end
Print("Watching '"..tostring(fpath).."': ",table.concat(cmdd," "))
local entry = {
path = fpath,
searchpath = searchpath,
time = time,
cmd = c,
}
local watchlist = GetWatchList()
watchlist[#watchlist+1] = entry
end)
COMMAND('send_self',function(ply,c,cmd,who)
local Path,searchpath=RealFilePath(c[2] and TableToString(c) or c[1])
if !Path then Print("Could not find the file\n") return end
local content = GiveFileContent(Path,searchpath)
if !content then Print("Could not read the file\n") return end
local who=string.GetFileFromFilename(Path)
RunOnSelf(content,who or CMD(who),MakeExtras(ply))
end)
if SERVER then return end
net.Receive(Tag,function(...) _ReceivedData(...) end)
function _ReceivedData(len)
local script = ReadCompressed()
local decoded=net.ReadTable()
local info=decoded.info
local extra=decoded.extra
local ok,ret = Run(script,tostring(info),extra)
if not ok then
ErrorNoHalt(tostring(ret)..'\n')
end
--[[ -- Not done
if extra.retid then
net.Start(net_retdata)
net.WriteUInt(extra.retid,32)
net.WriteBool(ok)
net.WriteTable(ret)
net.SendToServer()
end --]]
end
function CheckStore(src)
if not ShouldStore() then return end
local crc = util.CRC(src)
local path = "luadev_hist/".. crc ..'.txt'
if file.Exists(path,'DATA') then return end
if not file.IsDir("luadev_hist",'DATA') then file.CreateDir("luadev_hist",'DATA') end
file.Write(path,tostring(src),'DATA')
end
function ToServer(data)
if TransmitHook(data)~=nil then return end
CheckStore(data.src)
net.Start(Tag)
WriteCompressed(data.src or "")
-- clear extra data
data.src = nil
if data.extra then
data.extra.ply = nil
if table.Count(data.extra)==0 then data.extra=nil end
end
net.WriteTable(data)
if net.BytesWritten()==65536 then
Print("Unable to send lua code (too big)\n")
return nil,"Unable to send lua code (too big)"
end
net.SendToServer()
return true
end
function RunOnClients(script,who,extra)
if not who and extra and isentity(extra) then extra = {ply=extra} end
local data={
src=script,
dst=TO_CLIENTS,
info=who,
extra=extra,
}
return ToServer(data)
end
function RunOnSelf(script,who,extra)
if not isstring(who) then who = nil end
if not who and extra and isentity(extra) then extra = {ply=extra} end
--if luadev_selftoself:GetBool() then
-- Run
--end
return RunOnClient(script,LocalPlayer(),who,extra)
end
function RunOnClient(script,targets,who,extra)
-- compat
if not targets and isentity(who) then
targets=who
who = nil
end
if extra and isentity(extra) and who==nil then extra={ply=extra} end
if (not istable(targets) and !IsValid(targets))
or (istable(targets) and table.Count(targets)==0)
then error"Invalid player(s)" end
local data={
src=script,
dst=TO_CLIENT,
dst_ply=targets,
info=who,
extra=extra,
}
return ToServer(data)
end
function RunOnServer(script,who,extra)
if not who and extra and isentity(extra) then extra = {ply=extra} end
local data={
src=script,
dst=TO_SERVER,
--dst_ply=pl
info=who,
extra=extra,
}
return ToServer(data)
end
function RunOnShared(script,who,extra)
if not who and extra and isentity(extra) then extra = {ply=extra} end
local data={
src=script,
dst=TO_SHARED,
--dst_ply=pl
info=who,
extra=extra,
}
return ToServer(data)
end

View File

@@ -0,0 +1,592 @@
module("luadev",package.seeall)
Tag=_NAME..'1'
--net_retdata = Tag..'_retdata'
if SERVER then
util.AddNetworkString(Tag)
--util.AddNetworkString(net_retdata)
end
-- Enums
local enums={
TO_CLIENTS=1,
TO_CLIENT=2,
TO_SERVER=3,
TO_SHARED=4,
}
local revenums={} -- lookup
_M.revenums=revenums
for k,v in pairs(enums) do
_M[k]=v
revenums[v]=k
end
STAGE_PREPROCESS=1
STAGE_COMPILED=2
STAGE_POST=3
STAGE_PREPROCESSING=4
-- Figure out what to put to extra table
function MakeExtras(pl,extrat)
if pl and isentity(pl) and pl:IsPlayer() then
extrat = extrat or {}
extrat.ply = pl
end
return extrat
end
-- Helpers
function TransmitHook(stage,...)
return hook.Run("LuaDevTransmit",stage,...)
end
function IsOneLiner(script)
return script and not script:find("\n",1,true)
end
function GiveFileContent(fullpath,searchpath)
--Print("Reading: "..tostring(fullpath))
if fullpath==nil or fullpath=="" then return false end
local content=file.Read(fullpath,searchpath or "MOD")
if content==0 then return false end
return content
end
function TableToString(tbl)
return string.Implode(" ",tbl)
end
function Print(...)
if metalog then
metalog.info("Luadev", SERVER and "Server" or "Client", ...)
else
Msg("[Luadev"..(SERVER and ' Server' or '').."] ")
print(...)
end
end
if CLIENT then
luadev_store = CreateClientConVar( "luadev_store", "1",true)
function ShouldStore()
return luadev_store:GetBool()
end
end
if CLIENT then
luadev_verbose = CreateClientConVar( "luadev_verbose", "1",true)
else
luadev_verbose = CreateConVar( "luadev_verbose", "1", { FCVAR_NOTIFY ,FCVAR_ARCHIVE} )
end
function Verbose(lev)
return (luadev_verbose:GetInt() or 99)>=(lev or 1)
end
function PrintX(script,...)
local oneline = IsOneLiner(script) and 2
local verb = Verbose(oneline)
if metalog then
metalog[verb and "info" or "debug"]("Luadev", SERVER and "Server" or "Client", ...)
else
local Msg=not verb and _Msg or Msg
local print=not verb and _print or print
Msg("[Luadev"..(SERVER and ' Server' or '').."] ")
print(...)
end
end
specials = {
swep = {
function(val,extra,script,info)
local SWEP=weapons.GetStored(val)
if not SWEP then
SWEP = {Primary={}, Secondary={},Base = "weapon_base",ClassName = val, Folder = 'weapons/'..val }
end
_G.SWEP = SWEP
end,
function(val,extra,script,info)
local tbl = _G.SWEP
_G.SWEP = nil
if istable(tbl) then
--local table_ForEach=table.ForEach table.ForEach=function()end timer.Simple(0,function() table.ForEach=table_ForEach end)
if Verbose() then
Print("Registering weapon "..tostring(val))
end
weapons.Register(tbl, val, true)
--table.ForEach=table_ForEach
end
end,
},
sent = {
function(val,extra,script,info)
local ENT=scripted_ents.GetStored(val)
if ENT and ENT.t then
ENT=ENT.t
else
ENT = {ClassName=val , Folder = 'entities/'..val}
end
_G.ENT = ENT
end,
function(val,extra,script,info)
local tbl = _G.ENT
_G.ENT = nil
if istable(tbl) then
tbl.Model = tbl.Model or Model("models/props_borealis/bluebarrel001.mdl")
if not tbl.Base then
tbl.Base = "base_anim"
tbl.Type = tbl.Type or "anim"
end
if Verbose() then
Print("Registering entity "..tostring(val))
end
scripted_ents.Register(tbl, val)
end
end,
},
stool = {
function(toolmode,extra,script,info)
local gmod_tool=weapons.GetStored("gmod_tool")
if gmod_tool and gmod_tool.Tool and gmod_tool.Tool[toolmode] then
_G.TOOL=gmod_tool.Tool[toolmode]
assert(_G.TOOL and _G.TOOL.Mode == toolmode)
else
assert(ToolObj,"Need ToolObj from gamemode to create new tools")
_G.TOOL = ToolObj:Create(toolmode)
_G.TOOL.Mode = toolmode
end
_G.TOOL = TOOL
end,
function(val,extra,script,info)
local tbl = _G.TOOL
_G.TOOL = nil
if not istable(tbl) then return end
Print("Registering tool "..tostring(val))
if tbl.CreateConVars then
tbl:CreateConVars()
end
local gmod_tool=weapons.GetStored("gmod_tool")
if _G.TOOL and gmod_tool and gmod_tool.Tool then
gmod_tool.Tool[val] = _G.TOOL
end
end,
},
-- TODO --
effect = {
function(val,extra,script,info)
if SERVER then return end
_G.EFFECT = {ClassName=val,Folder = 'effects/'..val }
end,
function(val,extra,script,info)
if Verbose() then
Print("Registering effect "..tostring(val))
end
if CLIENT then
local tbl = _G.EFFECT _G.EFFECT = nil
if tbl then
effects.Register(_G.EFFECT,val)
end
end
end,
},
}
local specials = specials
function ProcessSpecial(mode,script,info,extra)
if not extra then return end
for special_type,funcs in next,specials do
local val = extra[special_type]
if val then
if Verbose(10) then
Print("ProcessSpecial",mode,special_type," -> ",val)
end
local func = funcs[mode]
if func then return func(val,extra,script,info) end
return
end
end
end
function FindPlayer(plyid)
if not plyid or not isstring(plyid) then return end
local cl
for k,v in pairs(player.GetHumans()) do
if v:SteamID()==plyid or tostring(v:AccountID())==plyid or tostring(v:UserID())==plyid then
cl=v
break
end
end
if not cl then
for k,v in pairs(player.GetAll()) do
if v:Name():lower():find(plyid:lower(),1,true)==1 then
cl=v
break
end
end
end
if not cl then
for k,v in pairs(player.GetAll()) do
if string.find(v:Name(),plyid) then
cl=v
break
end
end
end
if not cl then
for k,v in pairs(player.GetAll()) do
if v:Name():lower():find(plyid:lower(),1,true) then
cl=v
break
end
end
end
if not cl and easylua and easylua.FindEntity then
cl = easylua.FindEntity(plyid)
end
return IsValid(cl) and cl:IsPlayer() and cl or nil
end
-- Watch system
function FileTime(fullpath,searchpath)
--Print("Reading: "..tostring(fullpath))
if fullpath==nil or fullpath=="" then return false end
local t=file.Time(fullpath,searchpath or "MOD")
if not t or t==0 then return false end
return t
end
local watchlist = rawget(_M,"GetWatchList") and GetWatchList() or {} function GetWatchList() return watchlist end
local i=0
hook.Add("Think",Tag.."_watchlist",function()
if not watchlist[1] then return end
i=i+1
local entry = watchlist[i]
if not entry then
i=0
entry = watchlist[1]
if not entry then return end
end
local newtime = FileTime(entry.path,entry.searchpath)
local oldtime = entry.time
if newtime and newtime~=oldtime then
entry.time = newtime
Msg"[LuaDev] Refresh " print(unpack(entry.cmd))
RunConsoleCommand(unpack(entry.cmd))
end
end)
-- compression
function Compress( data )
return util.Compress( data )
end
function Decompress(data)
return util.Decompress( data )
end
function WriteCompressed(data)
if #data==0 then
net.WriteUInt( 0, 24 )
return false
end
local compressed = Compress( data )
local len = compressed:len()
net.WriteUInt( len, 24 )
net.WriteData( compressed, len )
return compressed
end
function ReadCompressed()
local len = net.ReadUInt( 24 )
if len==0 then return "" end
return Decompress( net.ReadData( len ) )
end
-- Compiler / runner
local function ValidCode(src,who)
local ret = CompileString(src,who or "",false)
if type(ret)=='string' then
return nil,ret
end
return ret or true
end
_M.ValidScript=ValidCode
_M.ValidCode=ValidCode
function ProcessHook(stage,...)
return hook.Run("LuaDevProcess",stage,...)
end
local LuaDevProcess=ProcessHook
local LUADEV_EXECUTE_STRING=RunStringEx
local LUADEV_EXECUTE_FUNCTION=xpcall
local LUADEV_COMPILE_STRING=CompileString
local mt= {
__tostring=function(self) return self[1] end,
__index={
set=function(self,what) self[1]=what end,
get=function(self,what) return self[1] end,
},
--__newindex=function(self,what) rawset(self,1,what) end,
}
local strobj=setmetatable({""},mt)
function Run(script,info,extra)
--compat
if CLIENT and not extra and info and istable(info) then
return luadev.RunOnSelf(script,"COMPAT",{ply=info.ply})
end
info = info or "??ANONYMOUS??"
if not isstring(info) then
debug.Trace()
ErrorNoHalt("LuaDev Warning: info type mismatch: "..type(info)..': '..tostring(info))
end
-- STAGE_PREPROCESS
local ret,newinfo = LuaDevProcess(STAGE_PREPROCESS,script,info,extra,nil)
if ret == false then return end
if ret ~=nil and ret~=true then script = ret end
if newinfo then info = newinfo end
-- STAGE_PREPROCESSING
rawset(strobj,1,script)
local ret = LuaDevProcess(STAGE_PREPROCESSING,strobj,info,extra,nil)
script = rawget(strobj,1)
if not script then
return false,"no script"
end
-- Compiling
local func = LUADEV_COMPILE_STRING(script,tostring(info),false)
if not func or isstring( func ) then compileerr = func or true func = false end
local ret = LuaDevProcess(STAGE_COMPILED,script,info,extra,func)
-- replace function
if ret == false then return end
if ret ~=nil and isfunction(ret) then
func = ret
compileerr = false
end
if not func then
if compileerr then
return false,"Syntax error: "..tostring(compileerr)
end
end
lastextra = extra
lastinfo = info
lastscript = script
lastfunc = func
ProcessSpecial(1,script,info,extra)
local args = extra and extra.args and (istable(extra.args) and extra.args or {extra.args})
if not args then args=nil end
-- Run the stuff
-- because garry's runstring has social engineer sexploits and such
local errormessage
local function LUADEV_TRACEBACK(errmsg)
errormessage = errmsg
local tracestr = debug.traceback(errmsg,2)
-- Tidy up the damn long trace
local p1=tracestr:find("LUADEV_EXECUTE_FUNCTION",1,true)
if p1 then
local p2=0
while p2 and p2<p1 do
local new=tracestr:find("\n",p2+1,true)
if new>p1 then
tracestr=tracestr:sub(1,new)
break
end
p2=new
end
end
ErrorNoHalt('[ERROR] '..tracestr )-- ..'\n')
end
local LUADEV_EXECUTE_FUNCTION=xpcall
local returnvals = {LUADEV_EXECUTE_FUNCTION(func,LUADEV_TRACEBACK,args and unpack(args) or nil)}
local ok = returnvals[1] table.remove(returnvals,1)
-- STAGE_POST
local ret = LuaDevProcess(STAGE_POST,script,info,extra,func,args,ok,returnvals)
ProcessSpecial(2,script,info,extra)
if not ok then
return false,errormessage
end
return ok,returnvals
end
function RealFilePath(name)
local searchpath = "MOD"
local RelativePath='lua/'..name
if name:find("^lua/") then -- search cache
name=name:gsub("^lua/","")
RelativePath=name
searchpath = "LUA"
elseif name:find("^%.%./") then -- whole shit
name=name:gsub("^%.%./","")
RelativePath=name
elseif name:find("^data/") then -- whatever
name=name:gsub("^data/","")
RelativePath='data/'..name
end
if not file.Exists(RelativePath,searchpath) then return nil end
return RelativePath,searchpath
end
function AutoComplete(cmd,commandName,args)
local name = string.Explode(' ',args)
name=name[#name] or ""
local path = string.GetPathFromFilename(name)
local searchpath = "MOD"
local RelativePath='lua/'..(name or "")
if name:find("^lua/") then -- search cache
name=name:gsub("^lua/","")
RelativePath=name
searchpath = "LUA"
elseif name:find("^%.%./") then -- whole shit
name=name:gsub("^%.%./","")
RelativePath=name
elseif name:find("^data/") then -- whatever
name=name:gsub("^data/","")
RelativePath='data/'..name
end
local searchstr = RelativePath.."*"
local files,folders=file.Find(searchstr,searchpath or "MOD")
files=files or {}
-- Filter out any files that don't end in ".lua".
for i = #files, 1, -1 do
if not string.match(files[i], "%.lua$") then
table.remove(files, i)
end
end
folders=folders or {}
for k,v in pairs(folders) do
table.insert(files,v)
end
local candidates=files
candidates=candidates or {}
for i,_ in pairs(candidates) do
candidates[i]=commandName.." "..path..candidates[i]
end
return candidates
end
local sv_allowcslua = GetConVar 'sv_allowcslua'
function CanLuaDev(ply,script,command,target,target_ply,extra)
if SERVER and not ply:IsFullyAuthenticated() then
return false, "Your SteamID wasn't fully authenticated, try restarting Steam."
end
local ret,x = hook.Run("CanLuaDev",ply,script,command,target,target_ply,extra)
if ret~=nil then return ret,x end
local ret,x = hook.Run("LuaDevIsPlayerAllowed", ply, script or "")
if ret~=nil then return ret,x end
if ply:IsSuperAdmin() then return true end
if target == TO_CLIENT and
(target_ply == ply
or (target_ply
and istable(target_ply)
and target_ply[1]==ply
and table.Count(target_ply)==1))
then
if sv_allowcslua:GetBool() then return true end
end
end
local luadev_show_access_attempt = SERVER and CreateConVar("luadev_show_access_attempt", '1', {FCVAR_ARCHIVE})
function RejectCommand(pl, msg)
if msg == true or msg == "" then return end -- suppress error in case we want to process luadev command ourselves in a hook
if SERVER and luadev_show_access_attempt:GetBool() and not pl.luadevaccessfail then
pl.luadevaccessfail = true
Msg"[LuaDev] " print(pl, "was rejected luadev access", msg)
end
S2C(pl, "No Access" .. (msg and (": " .. tostring(msg)) or ""))
end
function COMMAND(str,func,complete)
if SERVER then
concommand.Add('lua_'..str,function(pl,command,cmds,strcmd)
local id=pl
if IsValid(pl) then
local ok,err = CanLuaDev(pl,strcmd,command,nil,nil,nil)
if not ok then
return RejectCommand (pl,err or command)
end
id = GetPlayerIdentifier(pl,str) or pl
else
pl = "Console"
id = pl
end
func(pl,cmds,strcmd,id)
end)
else
concommand.Add('lua_'..str,function(_,_,cmds,strcmd)
func(pl,cmds,strcmd,str)
end,(not complete and function(...) return AutoComplete(str,...) end) or nil)
end
end

View File

@@ -0,0 +1,187 @@
module("luadev",package.seeall)
-- inform the client of the version
_luadev_version = CreateConVar( "_luadev_version", "1.6", FCVAR_NOTIFY )
function S2C(cl,msg)
if cl and cl:IsValid() and cl:IsPlayer() then
cl:ChatPrint("[LuaDev] "..tostring(msg))
end
end
function RunOnClients(script,who,extra)
if not who and extra and isentity(extra) then extra = {ply=extra} end
local data={
--src=script,
info=who,
extra=extra,
}
if Verbose() then
PrintX(script,tostring(who).." running on clients")
end
net.Start(Tag)
WriteCompressed(script)
net.WriteTable(data)
if net.BytesWritten()==65536 then
return nil,"too big"
end
net.Broadcast()
return true
end
local function ClearTargets(targets)
local i=1
local target=targets[i]
while target do
if not IsValid(target) then
table.remove(targets,i)
i=i-1
end
i=i+1
target=targets[i]
end
end
function RunOnClient(script,targets,who,extra)
-- compat
if not targets and isentity(who) then
targets=who
who = nil
end
if extra and isentity(extra) and who==nil then
extra={ply=extra}
who="COMPAT"
end
local data={
--src=script,
info=who,
extra=extra,
}
if not istable(targets) then
targets = {targets}
end
ClearTargets(targets)
if table.Count(targets)==0 then return nil,"no players" end
local targetslist
for _,target in pairs(targets) do
local pre = targetslist and ", " or ""
targetslist=(targetslist or "")..pre..tostring(target)
end
if Verbose() then
if type(who) == "string" and #who > 50 then
who = who:sub(1,50).."...>"
end
PrintX(script,tostring(who).." running on "..tostring(targetslist or "NONE"))
end
net.Start(Tag)
WriteCompressed(script)
net.WriteTable(data)
if net.BytesWritten()==65536 then
return nil,"too big"
end
net.Send(targets)
return #targets
end
function RunOnServer(script,who,extra)
if not who and extra and isentity(extra) then extra = {ply=extra} end
if Verbose() then
PrintX(script,tostring(who).." running on server")
end
return Run(script,tostring(who),extra)
end
function RunOnSelf(script,who,extra)
if not isstring(who) then who = nil end
if not who and extra and isentity(extra) then extra = {ply=extra} end
return RunOnServer(script,who,extra)
end
function RunOnShared(...)
RunOnClients(...)
return RunOnServer(...)
end
function GetPlayerIdentifier(ply,extrainfo)
if type(ply)=="Player" then
local info=ply:Name()
if Verbose(3) then
local sid=ply:SteamID():gsub("^STEAM_","")
info=('<%s|%s>'):format(sid,info:sub(1,24))
elseif Verbose(2) then
info=ply:SteamID():gsub("^STEAM_","")
end
if extrainfo then
info=('%s<%s>'):format(info,tostring(extrainfo))
end
info = info:gsub("%]","}"):gsub("%[","{"):gsub("%z","_") -- GMod bug
return info
else
return "??"..tostring(ply)
end
end
function _ReceivedData(len, ply)
local script = ReadCompressed() -- WriteCompressed(data)
local decoded=net.ReadTable()
decoded.src=script
local target=decoded.dst
local info = decoded.info
local target_ply=decoded.dst_ply
local extra=decoded.extra or {}
if not istable(extra) then
return RejectCommand(ply,"bad extra table")
end
extra.ply=ply
local can, msg = CanLuaDev(ply,script,nil,target,target_ply,extra)
if not can then
return RejectCommand(ply,msg)
end
if TransmitHook(data)~=nil then return end
local identifier = GetPlayerIdentifier(ply,info)
local ok,err
if target==TO_SERVER then ok,err=RunOnServer (script, identifier,extra)
elseif target==TO_CLIENT then ok,err=RunOnClient (script,target_ply, identifier,extra)
elseif target==TO_CLIENTS then ok,err=RunOnClients(script, identifier,extra)
elseif target==TO_SHARED then ok,err=RunOnShared (script, identifier,extra)
else S2C(ply,"Unknown target")
end
-- no callback system yet
if not ok then
ErrorNoHalt(tostring(err)..'\n')
end
end
net.Receive(Tag, function(...) _ReceivedData(...) end)

View File

@@ -0,0 +1,146 @@
-- luacheck: globals luadev socket easylua chatbox
local function requireExists(moduleName)
local osSuffix = assert(
(system.IsWindows() and (jit.arch~="x64" and "win32" or "win64"))
or (system.IsLinux() and "linux")
or (system.IsOSX() and "osx"),
"couldn't determine system type?"
)
local dllFiles = file.Find(string.format("lua/bin/gmcl_%s_%s.dll", moduleName, osSuffix), "GAME")
local luaFileExists = file.Exists(string.format("includes/modules/%s.lua", moduleName), "LCL")
return #dllFiles > 0 or luaFileExists
end
local function luadevPrint(...)
Msg"[LuaDev] "
print(...)
end
local moduleLoaded = false
for _, moduleName in ipairs({ "socket", "luasocket" }) do
if requireExists(moduleName) then
local ok, err = pcall(require, moduleName)
if not ok then
luadevPrint(
string.format("Unable to load module %s: %s", moduleName, err)
)
else
if not socket then
luadevPrint(string.format("_G.socket not found, but module %s loaded?", moduleName))
else
moduleLoaded = true
break
end
end
end
end
if not moduleLoaded then
luadevPrint("No socket module found")
return
end
local methods = {
self = function(sock)
local who = sock:receive("*l")
luadev.RunOnSelf(sock:receive("*a"), who)
system.FlashWindow()
end,
sv = function(sock)
local who = sock:receive("*l")
luadev.RunOnServer(sock:receive("*a"), who)
system.FlashWindow()
end,
sh = function(sock)
local who = sock:receive("*l")
luadev.RunOnShared(sock:receive("*a"), who)
system.FlashWindow()
end,
cl = function(sock)
local who = sock:receive("*l")
luadev.RunOnClients(sock:receive("*a"), who)
system.FlashWindow()
end,
ent = function(sock)
local who = sock:receive("*l")
local contents = string.format("ENT = {}; local ENT=ENT; %s; scripted_ents.Register(ENT, '%s')", sock:receive("*a"), who:sub(0, -5))
luadev.RunOnShared(contents, who)
system.FlashWindow()
end,
wep = function(sock)
local who = sock:receive("*l")
local contents = string.format("SWEP = {}; local SWEP=SWEP; %s; weapons.Register(SWEP, '%s')", sock:receive("*a"), who:sub(0, -5))
luadev.RunOnShared(contents, who)
system.FlashWindow()
end,
client = function(sock)
local who = sock:receive("*l")
local to = sock:receive("*l")
to = easylua and easylua.FindEntity(to) or player.GetByID(tonumber(to))
to = { to }
luadev.RunOnClient(sock:receive("*a"), to, who)
system.FlashWindow()
end,
chatTextChanged = function(sock)
local contents = sock:receive("*a")
if not contents then return end
if chatbox then
chatbox.StartChat_override = true
end
hook.Run("StartChat")
if chatbox then
chatbox.StartChat_override = false
end
hook.Run("ChatTextChanged", contents, true)
end,
finishChat = function(sock)
hook.Run("FinishChat")
end,
requestPlayers = function(sock)
local plys = {}
for _, ply in next, player.GetAll() do
table.insert(plys, ply:Nick())
end
sock:send(table.concat(plys, "\n"))
end
}
local sock = assert(socket.tcp())
assert(sock:bind("127.0.0.1", 27099))
sock:settimeout(0)
sock:setoption("reuseaddr", true)
assert(sock:listen(0))
hook.Add("Think", "LuaDev-Socket", function()
local cl = sock:accept()
if not cl then return end
if cl:getpeername() ~= "127.0.0.1" then
luadevPrint("Refused", cl:getpeername())
cl:shutdown()
return
end
cl:settimeout(0)
local protocol = cl:receive("*l")
local method
if protocol == "extension" then
method = cl:receive("*l")
else
method = protocol
end
if method and methods[method] then
methods[method](cl)
end
cl:shutdown()
end)