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,816 @@
easylua = {} local s = easylua
local _R = debug.getregistry()
local function compare(a, b)
if a == b then return true end
if a:find(b, nil, true) then return true end
if a:lower() == b:lower() then return true end
if a:lower():find(b:lower(), nil, true) then return true end
return false
end
local function comparenick(a, b)
local MatchTransliteration = GLib and GLib.UTF8 and GLib.UTF8.MatchTransliteration
if not MatchTransliteration then return compare (a, b) end
if a == b then return true end
if a:lower() == b:lower() then return true end
if MatchTransliteration(a, b) then return true end
return false
end
local function compareentity(ent, str)
if ent.GetName and compare(ent:GetName(), str) then
return true
end
if ent:GetModel() and compare(ent:GetModel(), str) then
return true
end
return false
end
local TagPrintOnServer = "elpos"
if CLIENT then
function easylua.PrintOnServer(...)
local args = {...}
local new = {}
for key, value in pairs(args) do
table.insert(new, luadata and luadata.ToString(value) or tostring(value))
end
net.Start(TagPrintOnServer)
local str = table.concat(new," ")
local max = 256
net.WriteString(str:sub(1,max))
net.WriteBool(#str>max)
net.SendToServer()
end
else
util.AddNetworkString(TagPrintOnServer)
end
function easylua.Print(...)
if CLIENT then
easylua.PrintOnServer(...)
end
if SERVER then
local args = {...}
local str = ""
Msg(string.format("[ELua %s] ", IsValid(me) and me:Nick() or "Sv"))
for key, value in pairs(args) do
str = str .. type(value) == "string" and value or luadata.ToString(value) or tostring(value)
if key ~= #args then
str = str .. ","
end
end
print(str)
end
end
if SERVER then
-- Rate limiting, still bad
local spams=setmetatable({},{__mode='k'})
local function canspam(pl,len)
local now = RealTime()
local nextspam = spams[pl] or 0
if now>nextspam then
nextspam = now + len>100 and 3 or 1
spams[pl] = nextspam
return true
else
local plstr = tostring(pl)
timer.Create("easylua_pspam",5,1,function()
Msg "[Easylua Print] Lost messages due to spam: " print(plstr)
end)
end
end
function easylua.CMDPrint(ply, cmd, args, fulln)
if not canspam(ply,#fulln) then
return
end
args = table.concat(args, ", ")
Msg(string.format("[ELua %s] ", IsValid(ply) and ply:Nick() or "Sv"))
print(args)
end
concommand.Add("easylua_print", easylua.CMDPrint)
net.Receive(TagPrintOnServer,function(len,ply)
local str = net.ReadString()
if not canspam(ply,#str) then return end
str=str:sub(1,512)
local more = net.ReadBool()
Msg(string.format("[ELua %s] ", IsValid(ply) and ply:Nick() or "Sv"))
local outstr = ('%s%s'):format(str,more and "..." or ""):gsub("[\r\n]"," ")
print(outstr)
end)
end
function easylua.FindEntity(str)
if not str then return NULL end
str = tostring(str)
if str == "#this" and IsEntity(this) and this:IsValid() then
return this
end
if str == "#owner" and IsEntity(this) and this:IsValid() then
local owner = this.CPPIGetOwner and this:CPPIGetOwner() or this:GetOwner()
return owner
end
if str == "#me" and IsEntity(me) and me:IsPlayer() then
return me
end
if str == "#all" then
return all
end
if str == "#afk" then
return afk
end
if str == "#us" then
return us
end
if str == "#them" then
return them
end
if str == "#friends" then
return friends
end
if str == "#humans" then
return humans
end
if str == "#bots" then
return bots
end
if str == "#randply" then
return table.Random(player.GetAll())
end
if str:sub(1,1) == "#" then
local str = str:sub(2)
if #str > 0 then
str = str:lower()
local found
for teamid, data in pairs(team.GetAllTeams()) do
if data.Name:lower() == str then
found = teamid
break
end
end
if found then
return CreateAllFunction(function()
local t = {}
for k,v in next,player.GetAll() do
if v:IsPlayer() and v:Team() == found then
t[#t+1] = v
end
end
return t
end)
end
for key, ent in ipairs(ents.GetAll()) do
if ent:GetClass():lower() == str then
found = str
break
end
end
if found then
return CreateAllFunction(function()
return ents.FindByClass(found)
end)
end
end
end
-- unique id
local ply = player.GetByUniqueID(str)
if ply and ply:IsPlayer() then
return ply
end
-- steam id
if str:find("STEAM") then
for key, _ply in ipairs(player.GetAll()) do
if _ply:SteamID() == str then
return _ply
end
end
end
if str:sub(1,1) == "_" and tonumber(str:sub(2)) then
str = str:sub(2)
end
if tonumber(str) then
ply = Entity(tonumber(str))
if ply:IsValid() then
return ply
end
end
-- community id
if #str == 17 and str[1] == "7" then
local ply = player.GetBySteamID64(str)
if ply and ply:IsValid() then
return ply
end
end
-- ip
if SERVER then
if str:find("%d+%.%d+%.%d+%.%d+") then
for key, _ply in ipairs(player.GetAll()) do
if _ply:IPAddress():find(str) then
return _ply
end
end
end
end
-- search in sensible order
-- search exact
for _,ply in ipairs(player.GetAll()) do
if ply:Nick()==str then
return ply
end
end
-- Search bots so we target those first
for key, ply in ipairs(player.GetBots()) do
if comparenick(ply:Nick(), str) then
return ply
end
end
-- search from beginning of nick
for _,ply in ipairs(player.GetHumans()) do
if ply:Nick():lower():find(str,1,true)==1 then
return ply
end
end
-- Search normally and search with colorcode stripped
for key, ply in ipairs(player.GetAll()) do
if comparenick(ply:Nick(), str) then
return ply
end
if _G.UndecorateNick and comparenick( UndecorateNick( ply:Nick() ), str) then
return ply
end
end
-- search RealName
if _R.Player.RealName then
for _, ply in ipairs(player.GetAll()) do
if comparenick(ply:RealNick(), str) then
return ply
end
end
end
if IsValid(me) and me:IsPlayer() then
local tr = me:GetEyeTrace()
local plpos = tr and tr.HitPos or me:GetPos()
local closest,mind = nil,math.huge
for key, ent in ipairs(ents.GetAll()) do
local d = ent:GetPos():DistToSqr(plpos)
if d < mind and compareentity(ent, str) then
closest = ent
mind = d
end
end
if closest then
return closest
end
else
for key, ent in ipairs(ents.GetAll()) do
if compareentity(ent, str) then
return ent
end
end
end
do -- class
local _str, idx = str:match("(.-)(%d+)$")
if idx then
idx = tonumber(idx)
str = _str
else
str = str
idx = (me and me.easylua_iterator) or 0
if me and isentity(me) and me:IsPlayer() then
local tr = me:GetEyeTrace()
local plpos = tr and tr.HitPos or me:GetPos()
local closest,mind = nil,math.huge
for key, ent in ipairs(ents.GetAll()) do
local d = ent:GetPos():DistToSqr(plpos)
if d < mind and compare(ent:GetClass(), str) then
closest = ent
mind = d
end
end
if closest then
return closest
end
end
end
local found = {}
for key, ent in ipairs(ents.GetAll()) do
if compare(ent:GetClass(), str) then
table.insert(found, ent)
end
end
return found[math.Clamp(idx%#found, 1, #found)] or NULL
end
end
function easylua.CreateEntity(class, callback)
local mdl = "error.mdl"
if IsEntity(class) and class:IsValid() then
this = class
elseif class:find(".mdl", nil, true) then
mdl = class
class = "prop_physics"
this = ents.Create(class)
this:SetModel(mdl)
else
this = ents.Create(class)
end
if callback and type(callback) == 'function' then
callback(this);
end
this:Spawn()
this:SetPos(there + Vector(0,0,this:BoundingRadius() * 2))
this:DropToFloor()
this:PhysWake()
undo.Create(class)
undo.SetPlayer(me)
undo.AddEntity(this)
undo.Finish()
me:AddCleanup("props", this)
return this
end
function easylua.CopyToClipboard(var, ply)
ply = ply or me
if luadata then
local str = luadata.ToString(var)
if not str and IsEntity(var) and var:IsValid() then
if var:IsPlayer() then
str = string.format("player.GetByUniqueID(--[[%s]] %q)", var:GetName(), var:UniqueID())
else
str = string.format("Entity(%i)", var:EntIndex())
end
end
if CLIENT then
SetClipboardText(str)
end
if SERVER then
local str = string.format("SetClipboardText(%q)", str)
if #str > 255 then
if luadev and luadev.RunOnClient then
luadev.RunOnClient(str, ply)
else
error("Text too long to send and luadev not found",1)
end
else
ply:SendLua(str)
end
end
end
end
local started = false
function easylua.Start(ply)
if started then
Msg"[ELua] "print("Session not ended for ",_G.me or (s.vars and s.vars.me),", restarting session for",ply)
easylua.End()
end
started = true
ply = ply or CLIENT and LocalPlayer() or nil
if not ply or not IsValid(ply) then return end
local vars = {}
local trace = util.QuickTrace(ply:EyePos(), ply:GetAimVector() * 10000, {ply, ply:GetVehicle()})
if trace.Entity:IsWorld() then
trace.Entity = NULL
end
vars.me = ply
vars.this = trace.Entity
vars.wep = ply:GetActiveWeapon()
vars.veh = ply:GetVehicle()
vars.we = {}
for k, v in ipairs(ents.FindInSphere(ply:GetPos(), 512)) do
if v:IsPlayer() then
table.insert(vars.we, v)
end
end
vars.there = trace.HitPos
vars.here = trace.StartPos
vars.dir = ply:GetAimVector()
vars.trace = trace
vars.length = trace.StartPos:Distance(trace.HitPos)
vars.copy = s.CopyToClipboard
vars.create = s.CreateEntity
vars.prints = s.PrintOnServer
if vars.this:IsValid() then
vars.phys = vars.this:GetPhysicsObject()
vars.model = vars.this:GetModel()
end
vars.E = s.FindEntity
vars.last = ply.easylua_lastvars
s.vars = vars
local old_G = {}
s.oldvars = old_G
for k, v in pairs(vars) do
old_G[k] = rawget(_G, k)
rawset(_G, k, v)
end
-- let this gc. maybe allow few more recursions.
if vars.last and istable(vars.last) then vars.last.last = nil end
ply.easylua_lastvars = vars
ply.easylua_iterator = (ply.easylua_iterator or 0) + 1
end
function easylua.End()
if not started then
Msg"[ELua] "print"Ending session without starting"
end
started = false
if s.vars then
for key, value in pairs(s.vars) do
if s.oldvars and s.oldvars[key] then
rawset(_G, key, s.oldvars[key])
else
rawset(_G, key, nil)
end
end
end
end
do -- env meta
local META = {}
local _G = _G
local easylua = easylua
local tonumber = tonumber
local nils={
["CLIENT"]=true,
["SERVER"]=true,
}
function META:__index(key)
local var = _G[key]
if var ~= nil then
return var
end
if not nils [key] then -- uh oh
var = easylua.FindEntity(key)
if var:IsValid() then
return var
end
end
return nil
end
function META:__newindex(key, value)
_G[key] = value
end
easylua.EnvMeta = setmetatable({}, META)
end
function easylua.RunLua(ply, code, env_name)
local data =
{
error = false,
args = {},
}
easylua.Start(ply)
if s.vars then
local header = ""
for key, value in next,(s.vars or {}) do
header = header .. string.format("local %s = %s ", key, key)
end
code = header .. "; " .. code
end
env_name = env_name or string.format("%s", tostring(
IsValid(ply) and ply:IsPlayer()
and "["..ply:SteamID():gsub("STEAM_","").."]"..ply:Name()
or ply))
data.env_name = env_name
local func = CompileString(code, env_name, false)
if type(func) == "function" then
setfenv(func, easylua.EnvMeta)
local args = {pcall(func)}
if args[1] == false then
data.error = args[2]
end
table.remove(args, 1)
data.args = args
else
data.error = func
end
easylua.End()
return data
end
-- legacy luadev compatibility
local STAGE_PREPROCESS=1
local STAGE_COMPILED=2
local STAGE_POST=3
local insession = false
hook.Add("LuaDevProcess","easylua",function(stage,script,info,extra,func)
if stage==STAGE_PREPROCESS then
if insession then
insession=false
easylua.End()
end
if not istable(extra) or not IsValid(extra.ply) or not script or extra.easylua==false then
return
end
insession = true
easylua.Start(extra.ply)
local t={}
for key, value in pairs(easylua.vars or {}) do
t[#t+1]=key
end
if #t>0 then
script=' local '..table.concat(t,", ")..' = '..table.concat(t,", ")..' ; '..script
end
--ErrorNoHalt(script)
return script
elseif stage==STAGE_COMPILED then
if not istable(extra) or not IsValid(extra.ply) or not isfunction(func) or extra.easylua==false then
if insession then
insession=false
easylua.End()
end
return
end
if insession then
local env = getfenv(func)
if not env or env==_G then
setfenv(func, easylua.EnvMeta)
end
end
elseif stage == STAGE_POST and insession then
insession=false
easylua.End()
end
end)
function easylua.StartWeapon(classname)
_G.SWEP = {
Primary = {},
Secondary = {},
ViewModelFlip = false,
}
SWEP.Base = "weapon_base"
SWEP.ClassName = classname
end
function easylua.EndWeapon(spawn, reinit)
if not SWEP then error"missing SWEP" end
if not SWEP.ClassName then error"missing classname" end
weapons.Register(SWEP, SWEP.ClassName)
for key, entity in ipairs(ents.FindByClass(SWEP.ClassName)) do
--if entity:GetTable() then table.Merge(entity:GetTable(), SWEP) end
if reinit then
entity:Initialize()
end
end
if SERVER and spawn then
SafeRemoveEntity(me:GetWeapon(SWEP.ClassName))
local me = me
local class = SWEP.ClassName
timer.Simple(0.2, function() if me:IsPlayer() then me:Give(class) end end)
end
SWEP = nil
end
function easylua.StartEntity(classname)
_G.ENT = {}
ENT.ClassName = classname or "no_ent_name_" .. me:Nick() .. "_" .. me:UniqueID()
end
function easylua.EndEntity(spawn, reinit)
ENT.Model = ENT.Model or Model("models/props_borealis/bluebarrel001.mdl")
if not ENT.Base then -- there can be Base without Type but no Type without base without redefining every function so um
ENT.Base = "base_anim"
ENT.Type = ENT.Type or "anim"
end
scripted_ents.Register(ENT, ENT.ClassName)
for key, entity in ipairs(ents.FindByClass(ENT.ClassName)) do
--table.Merge(entity:GetTable(), ENT)
if reinit then
entity:Initialize()
end
end
if SERVER and spawn then
create(ENT.ClassName)
end
ENT = nil
end
do -- all
include("tinylua.lua") -- Force it to load before we use it
local next = next
local type = type
local INTERNAL = {}
local META = {}
function META:__call()
error("Undefined __call")
end
function META:__index(key)
local wrapped = tinylua(self())
return wrapped[key]
end
function META:__newindex(key, value)
local wrapped = tinylua(self())
wrapped[key] = value
end
function CreateAllFunction(filter, inputTbl)
local allMeta = {}
for ind, metamethod in pairs(META)do
allMeta[ind] = metamethod
end
function allMeta:__call()
return (isfunction(filter) and filter() or filter)
end
return setmetatable(inputTbl or {}, allMeta)
end
function INTERNAL:get()
return self()
end
all = CreateAllFunction(player.GetAll)
humans = CreateAllFunction(player.GetHumans)
bots = CreateAllFunction(player.GetBots)
afk = CreateAllFunction(function()
local t = {}
for k,v in ipairs(player.GetAll()) do
if not v.IsAFK then break end
if v:IsAFK() then
t[#t+1] = v
end
end
return t
end)
us = CreateAllFunction(function()
if _G.we then return _G.we end
if _G.me then return {_G.me} end
return {}
end)
them = CreateAllFunction(function()
local me = _G.me
local we = _G.we or {}
table.RemoveByValue(we, me)
return we
end)
friends = CreateAllFunction(function()
local me = _G.me
local t = {}
for k,v in ipairs(player.GetHumans()) do
if v == me then continue end
if (me.IsFriend and me:IsFriend(v) or (CLIENT and v:GetFriendStatus() == "friend")) then
t[#t+1] = v
end
end
return t
end)
npcs = CreateAllFunction(function()
local t = {}
for _, ent in pairs(ents.GetAll())do
if ent:IsNPC() then
table.insert(t, ent)
end
end
return t
end)
allof = function(class)
if isentity(class) and IsValid(class) then
class = class:GetClass()
end
local results = ents.FindByClass(class)
return CreateAllFunction(function()
return results
end, results)
end
props = CreateAllFunction(function() return ents.FindByClass("prop_physics") end)
if SERVER then
these = CreateAllFunction(function() return constraint.GetAllConstrainedEntities(_G.this) end)
end
those = CreateAllFunction(function() return ents.FindInSphere(_G.there, 250) end)
end

View File

@@ -0,0 +1,18 @@
module("luadev",package.seeall)
-- I think I finally understood why people make these seemingly silly files with just includes
include 'luadev/luadev_sh.lua'
if SERVER then
include 'luadev/luadev_sv.lua'
end
include 'luadev/luadev.lua'
if CLIENT then
include 'luadev/socketdev.lua'
end
if SERVER then
AddCSLuaFile 'luadev/luadev_sh.lua'
AddCSLuaFile 'luadev/luadev.lua'
AddCSLuaFile 'luadev/socketdev.lua'
end

View File

@@ -0,0 +1,130 @@
hook.Add("Think","luadev_cmdsinit",function()
hook.Remove("Think","luadev_cmdsinit")
local function add(cmd,callback)
if aowl and aowl.AddCommand then
aowl.AddCommand(cmd,function(ply,script,param_a,...)
local a,b
easylua.End() -- nesting not supported
local ret,why = callback(ply,script,param_a,...)
if not ret then
if why==false then
a,b = false,why or aowl.TargetNotFound(param_a or "notarget") or "H"
elseif isstring(why) then
ply:ChatPrint("FAILED: "..tostring(why))
a,b= false,tostring(why)
end
end
easylua.Start(ply)
return a,b
end,cmd=="lm" and "players" or "developers")
end
end
local function X(ply,i) return luadev.GetPlayerIdentifier(ply,'cmd:'..i) end
add("l", function(ply, line, target)
if not line or line=="" then return false,"invalid script" end
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,"l") if not valid then return false,err end end
return luadev.RunOnServer(line, X(ply,"l"), {ply=ply})
end)
add("ls", function(ply, line, target)
if not line or line=="" then return false,"invalid script" end
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,"ls") if not valid then return false,err end end
return luadev.RunOnShared(line, X(ply,"ls"), {ply=ply})
end)
add("lc", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,"lc") if not valid then return false,err end end
return luadev.RunOnClients(line, X(ply,"lc"), {ply=ply})
end)
add("lsc", function(ply, line, target)
local script = string.sub(line, string.find(line, target, 1, true)+#target+1)
if luadev.ValidScript then local valid,err = luadev.ValidScript(script,'lsc') if not valid then return false,err end end
easylua.Start(ply) -- for _G.we -> #us
local ent = easylua.FindEntity(target)
if type(ent) == 'table' then
ent = ent.get()
end
easylua.End()
return luadev.RunOnClient(script, ent, X(ply,"lsc"), {ply=ply})
end)
local sv_allowcslua = GetConVar"sv_allowcslua"
add("lm", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,'lm') if not valid then return false,err end end
if not ply:IsAdmin() and not sv_allowcslua:GetBool() then return false,"sv_allowcslua is 0" end
luadev.RunOnClient(line, ply,X(ply,"lm"), {ply=ply})
end)
add("lb", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,'lb') if not valid then return false,err end end
luadev.RunOnClient(line, ply, X(ply,"lb"), {ply=ply})
return luadev.RunOnServer(line, X(ply,"lb"), {ply=ply})
end)
add("print", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript('x('..line..')','print') if not valid then return false,err end end
return luadev.RunOnServer("print(" .. line .. ")", X(ply,"print"), {ply=ply})
end)
add("table", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript('x('..line..')','table') if not valid then return false,err end end
return luadev.RunOnServer("PrintTable(" .. line .. ")", X(ply,"table"), {ply=ply})
end)
add("keys", function(ply, line, table, search)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript('x('..table..')','keys') if not valid then return false,err end end
search = search and search:lower() or ""
return luadev.RunOnServer(
"local t={} for k,v in pairs(" .. table .. ") do t[#t+1]=tostring(k) end table.sort(t) for k,v in pairs(t) do if string.find(v:lower(),\"" .. search .. "\",1,true) then print(v) end end",
X(ply,"keys"), {ply=ply}
)
end)
add("printc", function(ply, line, target)
if not line or line=="" then return end
line = "easylua.PrintOnServer(" .. line .. ")"
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,'printc') if not valid then return false,err end end
return luadev.RunOnClients(line, X(ply,"printc"), {ply=ply})
end)
add("printm", function(ply, line, target)
if not line or line=="" then return end
line = "easylua.PrintOnServer(" .. line .. ")"
if luadev.ValidScript then local valid,err = luadev.ValidScript(line,'printm') if not valid then return false,err end end
luadev.RunOnClient(line, ply, X(ply,"printm"), {ply=ply})
end)
add("printb", function(ply, line, target)
if not line or line=="" then return end
if luadev.ValidScript then local valid,err = luadev.ValidScript('x('..line..')','printb') if not valid then return false,err end end
luadev.RunOnClient("easylua.PrintOnServer(" .. line .. ")", ply, X(ply,"printb"), {ply=ply})
return luadev.RunOnServer("print(" .. line .. ")", X(ply,"printb"), {ply=ply})
end)
end)

View File

@@ -0,0 +1,202 @@
tinylua = setmetatable({}, { __call = function(self, ...) return self.Wrap(...) end})
local INTERNAL = {}
local META = {}
local function pack(...) -- Convenient argument packer
local len, tbl = select('#', ...), {...}
local packFuncs = {}
function packFuncs.unpack()
return unpack(tbl, 1, len)
end
return setmetatable(tbl, {
__index = function(self, index)
return packFuncs[index] or tbl[index]
end,
__call = function(...)
return len, tbl
end
})
end
local function Wrap(input)
local values = {}
local meta = {}
for ind, val in pairs(input)do
values[(tonumber(ind) and val or ind)] = val
end
for ind, val in pairs(META)do
meta[ind] = val
end
return setmetatable(values, meta)
end
local function performCall(tbl, callback)
local results = {}
local errors = {}
local calls = 0
local iKey, iVal = nil, nil
while true do
local succ, err = pcall(function()
while true do
iKey, iVal = next(tbl, iKey)
if iKey == nil then break end
calls = calls + 1
callback(results, iKey, iVal)
end
end)
if not succ then errors[iKey] = err end
if iKey == nil then break end
end
if table.Count(errors) == calls then
if calls ~= 0 then
local _, error = next(errors, nil)
MsgC(Color(235, 111, 111), "[tinylua] "..error)
else
MsgC(Color(235, 111, 111), "[tinylua] No results!\n")
return
end
end
local result = Wrap(results)
getmetatable(result)["errors"] = errors
return result
end
function META:__index(index)
if INTERNAL[index] then
return function(_, ...)
return INTERNAL[index](self, ...)
end
end
return performCall(self, function(results, source, ent)
local target = ent[index]
if isfunction(target) then
results[source] = function(fArg, ...)
return target(ent, ...)
end
else
results[source] = target
end
end)
end
function META:__newindex(index, value)
performCall(self, function(results, source, ent)
ent[index] = value
end)
end
function META:__call(...)
local args = pack(...)
return performCall(self, function(results, source, ent)
if isfunction(ent) then
local rets = pack(ent(args:unpack()))
if #rets ~= 1 then
for _, ret in pairs(rets) do
table.insert(results, ret)
end
else
results[source] = rets[1]
end
end
end)
end
-- Exposed Functions
tinylua.Wrap = Wrap
-- INTERNAL Extensions
local function makePrefix(input)
if not input:match("\n") and isfunction(CompileString("return "..input, "", false)) then
return "return "..input
end
return input
end
local function buildParser(input)
if isfunction(input) then return input end
local argStr, funcStr = input:match("(.-)->(.+)")
if argStr and funcStr then
local codeFull = string.format("return function(%s)\n%s\nend", argStr, makePrefix(funcStr))
local funcFactory = CompileString(codeFull, "funcfactory")
if getfenv(1) then
setfenv(funcFactory, getfenv(1))
end
if funcFactory then
return funcFactory()
end
end
end
function INTERNAL:map(input)
local eval = buildParser(input)
return performCall(self, function(results, source, ent)
local rets = pack(eval(ent, source))
if #rets ~= 1 then
for _, val in pairs(rets) do
table.insert(results, val)
end
else
results[source] = rets[1]
end
end)
end
function INTERNAL:filter(input)
local eval = buildParser(input)
return performCall(self, function(results, source, ent)
if eval(ent, source) then
results[source] = ent
end
end)
end
function INTERNAL:set(vars, val)
vars = (istable(vars) and vars or {vars})
return performCall(self, function(results, source, ent)
for _, var in ipairs(vars) do
ent[var] = val
end
results[source] = ent
end)
end
function INTERNAL:IsValid()
return false
end
function INTERNAL:keys()
return performCall(self, function(results, source, ent)
results[source] = source
end)
end
function INTERNAL:first()
for _, ent in pairs(self) do
return ent
end
end
function INTERNAL:errors()
return (getmetatable(self).errors or {})
end
function INTERNAL:get()
return table.ClearKeys(self)
end

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)