dobrograd-13-06-2022/garrysmod/addons/util-luadev/lua/autorun/easylua.lua
Jonny_Bro (Nikita) e4d5311906 first commit
2023-11-16 15:01:19 +05:00

745 lines
15 KiB
Lua

easylua = {} local s = easylua
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
function easylua.CMDPrint(ply, cmd, args)
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)
-- if not luadev.CanLuaDev(ply) then return end
-- local str = net.ReadString()
-- 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 == "#us" then
return us
end
if str == "#them" then
return them
end
if str == "#friends" then
return friends
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 pairs(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 pairs(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 then
end
-- ip
if SERVER then
if str:find("%d+%.%d+%.%d+%.%d+") then
for key, _ply in pairs(player.GetAll()) do
if _ply:IPAddress():find(str) then
return _ply
end
end
end
end
-- search in sensible order
-- search exact
for _,ply in pairs(player.GetAll()) do
if ply:Nick()==str then
return ply
end
end
-- Search bots so we target those first
for key, ply in pairs(player.GetBots()) do
if comparenick(ply:Nick(), str) then
return ply
end
end
-- search from beginning of nick
for _,ply in pairs(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 pairs(player.GetAll()) do
if comparenick(ply:Nick(), str) then
return ply
end
if comparenick(ply:Nick():gsub("%^%d+", ""), str) then
return ply
end
end
if not me or not isentity(me) or not me:IsPlayer() then
for key, ent in pairs(ents.GetAll()) do
if compareentity(ent, str) then
return ent
end
end
else
local tr = me:GetEyeTrace()
local plpos = tr and tr.HitPos or me:GetPos()
local closest,mind = nil,math.huge
for key, ent in pairs(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
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 pairs(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 pairs(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 pairs(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]=_G[k] _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
_G[key] = s.oldvars[key]
else
_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 pairs(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 pairs(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
local next = next
local type = type
local rawget = rawget
local META = {}
function META:__call()
return rawget(self, 'get')()
end
function META:__index(key)
if type(key) == 'number' then
return rawget(self, 'get')()[key]
end
return function(_, ...)
local args = {}
for _, ent in next, rawget(self, 'get')() do
if type(ent[key]) == "function" or ent[key] == "table" and type(ent[key].__call) == "function" and getmetatable(ent[key]) then
local rets = {ent[key](ent, ...)}
if select('#', unpack(rets)) > 1 then
args[ent] = {rets}
else
args[ent] = rets[1]
end
else
ErrorNoHalt("attempt to call field '" .. key .. "' on ".. tostring(ent) .." a " .. type(ent[key]) .. " value\n")
end
end
return args
end
end
function META:__newindex(key, value)
if type(key) == 'number' then error'setting number index on entity' end
for _, ent in next, rawget(self, 'get')() do
ent[key] = value
end
end
function CreateAllFunction(filter)
return setmetatable({
get = filter,
}, META)
end
all = CreateAllFunction(player.GetAll)
humans = CreateAllFunction(player.GetHumans)
bots = CreateAllFunction(player.GetBots)
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 next,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)
props = CreateAllFunction(function() return ents.FindByClass'prop_physics' end)
these = CreateAllFunction(function() return constraint.GetAllConstrainedEntities(_G.this) end)
end