From f8d0c1d6d267b01052b64d456c905e81eb6a07ab Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sat, 15 Jun 2024 00:00:00 +0300 Subject: [PATCH 1/9] sentSpawn functionality --- .../core/custom/cl_prop.lua | 15 + .../core/custom/prop.lua | 571 +++++++- lua/entities/gmod_wire_interactiveprop.lua | 5 + lua/entities/sents_default_params.lua | 1197 +++++++++++++++++ 4 files changed, 1785 insertions(+), 3 deletions(-) create mode 100644 lua/entities/sents_default_params.lua diff --git a/lua/entities/gmod_wire_expression2/core/custom/cl_prop.lua b/lua/entities/gmod_wire_expression2/core/custom/cl_prop.lua index be90bca2d9..2e7e0767ce 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/cl_prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/cl_prop.lua @@ -9,6 +9,21 @@ E2Helper.Descriptions["propSpawn(san)"] = "Model path, Rotation, Frozen Spawns a E2Helper.Descriptions["propSpawn(ean)"] = "Rotation, Frozen Spawns a prop with the model of the template entity and rotated to the angle given. If frozen is 0, then it will spawn unfrozen." E2Helper.Descriptions["propSpawn(svan)"] = "Model path, Position, Rotation, Frozen Spawns a prop with the model denoted by the string file path, at the position denoted by the vector, and rotated to the angle given. If frozen is 0, then it will spawn unfrozen." E2Helper.Descriptions["propSpawn(evan)"] = "Position, Rotation, Frozen Spawns a prop with the model of the template entity, at the position denoted by the vector, and rotated to the angle given. If frozen is 0, then it will spawn unfrozen." +E2Helper.Descriptions["sentSpawn(s)"] = "Sent class - Spawns a SENT with no parameters. (Attempts to default the required values). (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(sv)"] = "Sent class, Position - Spawns a SENT with no parameters. (Attempts to default the required values). (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(st)"] = "Sent class, Sent data - Spawns a SENT with provided data as parameters. (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(sva)"] = "Sent class, Position, Rotation - Spawns a SENT with no parameters. (Attempts to default the required values). (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(svat)"] = "Sent class, Position, Rotation, Sent data - Spawns a SENT with provided data as parameters. (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(svan)"] = "Sent class, Position, Rotation, Frozen - Spawns a SENT with no parameters. (Attempts to default the required values). (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentSpawn(svant)"] = "Sent class, Position, Rotation, Frozen, Sent data - Spawns a SENT with provided data as parameters. (Requires wire_expression2_propcore_sents_whitelist 0 to spawn sents from entity tab!)" +E2Helper.Descriptions["sentGetWhitelisted()"] = "Returns an array of classes, which is registered to the whitelist (can be spawned regardless of wire_expression2_propcore_sents_whitelist). (Can be used to make a lookup table)" +E2Helper.Descriptions["sentGetData(s)"] = "Returns a table, where keys are parameter names (key sensitive!) and values as a table, where first value is the (lua-)type and second value is the default value. (When spawning a sent values is being turned from E2 to Lua types. For example - vec(255,0,0) or vec4(255,0,0,255) is being turned to type Color (red))" +E2Helper.Descriptions["sentGetDataTypes(s)"] = "Returns a table, where keys are parameter names (key sensitive!) and values as (lua-)types. (When spawning a sent values is being turned from E2 to Lua types. For example - vec(255,0,0) or vec4(255,0,0,255) is being turned to type Color (red))" +E2Helper.Descriptions["sentGetDataDefaultValues(s)"] = "Returns a table, where keys are parameter names (key sensitive!) and values as default values, which will be applied if none would be provided." +E2Helper.Descriptions["sentCanCreate()"] = "Returns 1 if you can spawn a SENT, 0 otherwise. (Complete alias of propCanCreate())" +E2Helper.Descriptions["sentCanCreate(s)"] = "Returns 1 if you can spawn a provided class(type) SENT, 0 otherwise. (Accounts both for antispam and whitelist)" +E2Helper.Descriptions["sentIsWhitelist()"] = "Returns 1 if the whitelist is enabled, 0 otherwise." +E2Helper.Descriptions["sentIsEnabled()"] = "Returns 1 if server allows spawning sents, 0 otherwise." E2Helper.Descriptions["seatSpawn(sn)"] = "Model path, Frozen Spawns a prop with the model denoted by the string filepath. If frozen is 0, then it will spawn unfrozen." E2Helper.Descriptions["seatSpawn(svan)"] = E2Helper.Descriptions["seatSpawn(sn)"] E2Helper.Descriptions["seatSpawn(svans)"] = E2Helper.Descriptions["seatSpawn(sn)"] .. " String seatType, determines what animations the seat will have. For example phx_seat2 and phx_seat3 will have Jeep and Airboat animations." diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 7fcacca71d..702f075cdb 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -9,7 +9,8 @@ local sbox_E2_maxProps = CreateConVar( "sbox_E2_maxProps", "-1", FCVAR_ARCHIVE ) local sbox_E2_maxPropsPerSecond = CreateConVar( "sbox_E2_maxPropsPerSecond", "4", FCVAR_ARCHIVE ) local sbox_E2_PropCore = CreateConVar( "sbox_E2_PropCore", "2", FCVAR_ARCHIVE ) -- 2: Players can affect their own props, 1: Only admins, 0: Disabled local sbox_E2_canMakeStatue = CreateConVar("sbox_E2_canMakeStatue", "1", FCVAR_ARCHIVE) - +local wire_expression2_propcore_sents_whitelist = CreateConVar("wire_expression2_propcore_sents_whitelist", 1, FCVAR_ARCHIVE, "If 1 - players can spawn sents only that are added to the default sent list, if 0 - players can spawn sents both from registered list AND from entity tab.", 0, 1) +local wire_expression2_propcore_sents_enabled = CreateConVar("wire_expression2_propcore_sents_enabled", 1, FCVAR_ARCHIVE, "If 1 - allows to spawn sents. (Doesn't affect sentSpawn whitelist), if 0 - prevents sentSpawn to be uset at all.", 0, 1) local isOwner = E2Lib.isOwner local GetBones = E2Lib.GetBones local isValidBone = E2Lib.isValidBone @@ -110,7 +111,6 @@ function PropCore.CreateProp(self, model, pos, angles, freeze, vehicleType) local cleanupCategory = "props" local undoCategory = "e2_spawned_prop" local undoName = "E2 Prop" - if vehicleType then local entry = list.Get("Vehicles")[vehicleType] if not entry or entry.Class ~= "prop_vehicle_prisoner_pod" then @@ -194,8 +194,402 @@ local function boneVerify(self, bone) return ent, index end --------------------------------------------------------------------------------- +-- Silly function to make printout on errors more userfriendly. +local luaTypeIDToString = { + [TYPE_NONE] = "none", + [TYPE_NIL] = "nil", + [TYPE_BOOL] = "boolean", + [TYPE_LIGHTUSERDATA] = "lightuserdata", + [TYPE_NUMBER] = "number", + [TYPE_STRING] = "string", + [TYPE_TABLE] = "table", + [TYPE_FUNCTION] = "function", + [TYPE_USERDATA] = "userdata", + [TYPE_THREAD] = "thread", + [TYPE_ENTITY] = "entity", + [TYPE_VECTOR] = "vector", + [TYPE_ANGLE] = "angle", + [TYPE_PHYSOBJ] = "physobj", + [TYPE_SAVE] = "save", + [TYPE_RESTORE] = "restore", + [TYPE_DAMAGEINFO] = "damageinfo", + [TYPE_EFFECTDATA] = "effectdata", + [TYPE_MOVEDATA] = "movedata", + [TYPE_RECIPIENTFILTER] = "recipientfilter", + [TYPE_USERCMD] = "usercmd", + [TYPE_SCRIPTEDVEHICLE] = "scriptedvehicle", + [TYPE_MATERIAL] = "material", + [TYPE_PANEL] = "panel", + [TYPE_PARTICLE] = "particle", + [TYPE_PARTICLEEMITTER] = "particleemitter", + [TYPE_TEXTURE] = "texture", + [TYPE_USERMSG] = "usermsg", + [TYPE_CONVAR] = "convar", + [TYPE_IMESH] = "imesh", + [TYPE_MATERIAL] = "matrix", + [TYPE_SOUND] = "sound", + [TYPE_PIXELVISHANDLE] = "pixelvishandle", + [TYPE_DLIGHT] = "dlight", + [TYPE_VIDEO] = "video", + [TYPE_FILE] = "file", + [TYPE_LOCOMOTION] = "locomotion", + [TYPE_PATH] = "path", + [TYPE_NAVAREA] = "navarea", + [TYPE_SOUNDHANDLE] = "soundhandle", + [TYPE_NAVLADDER] = "navladder", + [TYPE_PARTICLESYSTEM] = "particlesystem", + [TYPE_PROJECTEDTEXTURE] = "projectedtexture", + [TYPE_PHYSCOLLIDE] = "physcollide", + [TYPE_SURFACEINFO] = "surfaceinfo", + [TYPE_COUNT] = "count", + [TYPE_COLOR] = "color", +} + +-- Only data types that can be directly casted, or already are in the same category. All other +-- E2 types are either need to be transformed, or can't be casted at anything except for table. +local e2TypeNameToLuaTypeIDTable = { + ["none"] = TYPE_NONE, + ["void"] = TYPE_NONE, + [""] = TYPE_NONE, + ["number"] = TYPE_NUMBER, + ["n"] = TYPE_NUMBER, + ["string"] = TYPE_STRING, + ["s"] = TYPE_STRING, + ["entity"] = TYPE_ENTITY, + ["e"] = TYPE_ENTITY, + ["vector"] = TYPE_VECTOR, + ["v"] = TYPE_VECTOR, + ["angle"] = TYPE_ANGLE, + ["a"] = TYPE_ANGLE, +} + +local function e2TypeNameToLuaTypeID(TypeName) + return e2TypeNameToLuaTypeIDTable[string.lower(TypeName)] or TYPE_TABLE +end + +-- Lua type -> E2 to lua casting function. No way to implement default behaviour, so use castE2ValueToLuaValue function instead of table. +-- (It's forward declaration(to make recursive table unpacking possible). Real table is beneath castE2ValueToLuaValue) +local castE2ValueToLuaValueTable = {} + +local function castE2ValueToLuaValue(targetTypeID, e2Value) + if castE2ValueToLuaValueTable[targetTypeID] then + return castE2ValueToLuaValueTable[targetTypeID](e2Value) + end + + return nil +end + +-- Well, most of it is a nobrainer, but still helpful when you're just iterating and casting everything. +castE2ValueToLuaValueTable = { + [TYPE_NIL] = function(e2Value) -- TYPE_NIL from whatever :) + return nil + end, + [TYPE_BOOL] = function(e2Value) -- TYPE_BOOL from 'number' + if TypeID(e2Value)==TYPE_NUMBER then + return e2Value > 0 + end + + return nil + end, + [TYPE_NUMBER] = function(e2Value) -- TYPE_NUMBER from 'number' or 'string' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_NUMBER then return e2Value end + if e2TypeID == TYPE_STRING then return tonumber(e2Value) end + + return nil + end, + [TYPE_STRING] = function(e2Value) -- TYPE_STRING from 'string' or 'number' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_STRING then return e2Value end + if e2TypeID == TYPE_NUMBER then return tostring(e2Value) end + + return nil + end, + [TYPE_TABLE] = function(e2Value) -- TYPE_TABLE from 'table, array, ranger, quaternions, and a most other types that aren't present in other casts' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_TABLE then + if e2Value.ntypes or e2Value.stypes then -- Is it an E2 table? Unpack it correctly then. + local res = {} + + -- Handle 'n' field + for i, value in pairs(e2Value["n"]) do + res[i] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["ntypes"][i]), value) -- recursively unpacks any tables, or just returns the value. + end + + -- Handle 's' field + for key, value in pairs(e2Value["s"]) do + res[key] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["stypes"][key]), value) -- recursively unpacks any tables, or just returns the value. + end + + return res + end + + return e2Value -- It's not? Just return it then. + end + + if e2TypeID == TYPE_ANGLE or e2TypeID == TYPE_COLOR or e2TypeID == TYPE_VECTOR or e2TypeID == TYPE_MATRIX then return e2Value:ToTable() end + + return nil + end, + [TYPE_ENTITY] = function(e2Value) -- TYPE_ENTITY from 'entity' + if TypeID(e2Value) == TYPE_ENTITY then return e2Value end + + return nil + end, + [TYPE_VECTOR] = function(e2Value) -- TYPE_VECTOR from 'vector' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_VECTOR then return e2Value end + if e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Vector(e2Value[1], e2Value[2], e2Value[3]) end + + return nil + end, + [TYPE_ANGLE] = function(e2Value) -- TYPE_ANGLE from 'angle' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_ANGLE then return e2Value + elseif e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Angle(e2Value[1], e2Value[2], e2Value[3]) end + + return nil + end, + [TYPE_DAMAGEINFO] = function(e2Value) -- TYPE_DAMAGEINFO from 'damageinfo' + if TypeID(e2Value) == TYPE_DAMAGEINFO then return e2Value end + end, + [TYPE_EFFECTDATA] = function(e2Value) -- TYPE_EFFECTDATA from 'effectdata' + if TypeID(e2Value) == TYPE_EFFECTDATA then return e2Value end + end, + [TYPE_MATERIAL] = function(e2Value) -- TYPE_MATERIAL from 'string' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_STRING then return Material(e2Value) end + if e2TypeID == TYPE_TABLE then -- Png parameters support + if #e2Value != 2 then return nil end + + if TypeID(e2Value[1]) != TYPE_STRING then return nil end + if TypeID(e2Value[2]) != TYPE_STRING then return nil end + + return Material(e2Value[1], e2Value[2]) + end + + return nil + end, + [TYPE_MATRIX] = function(e2Value) -- TYPE_MATRIX from 'matrix4' + if TypeID(e2Value) ~= TYPE_TABLE then return nil end + + if #e2Value == 16 then + for i = 1, 16 do + if not isnumber(e2Value[i]) then return nil end + end + + return Matrix({e2Value[1], e2Value[2], e2Value[3], e2Value[4]}, {e2Value[5], e2Value[6], e2Value[7], e2Value[8]}, {e2Value[9], e2Value[10], e2Value[11], e2Value[12]}, {e2Value[13], e2Value[14], e2Value[15], e2Value[16]}) + end + + return nil + end, + [TYPE_COLOR] = function(e2Value) -- TYPE_COLOR from 'vector' or 'vector4' or 'itable' or 'table' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_VECTOR then + return Color(e2Value[1], e2Value[2], e2Value[3]) + elseif e2TypeID == TYPE_TABLE then -- vector4 support + direct table support + if isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) and isnumber(e2Value[4]) then + return Color(e2Value[1], e2Value[2], e2Value[3], e2Value[4]) + elseif e2Value.r and e2Value.g and e2Value.b then + if e2Value.a then return Color(e2Value.r, e2Value.g, e2Value.b, e2Value.a) end + return Color(e2Value.r, e2Value.g, e2Value.b) + end + end + + return nil + end, +} + +-- Separate from PropCore.CreateProp, to add some additional checks, and don't make PropCore.ValidAction check sent cases each time anything else is attempted to be spawned (microopt). +function PropCore.CreateSent(self, class, pos, angles, freeze, data) + if not wire_expression2_propcore_sents_enabled:GetBool() then return self:throw("Sent spawning is disabled by server! (wire_expression2_propcore_sents_enabled)", NULL) end + if hook.Run( "Expression2_CanSpawnSent", class, self ) == false then return self:throw("A hook prevented this sent to be spawned!", nil) end + if not WithinPropcoreLimits() then return self:throw("Prop limit reached! (cooldown or max)", NULL) end + -- Same logic as in PropCore.ValidSpawn + -- Decided not to put it in a function, as it's only used twice, and abstraction may lead to problems for future devs. + local limithit = playerMeta.LimitHit + playerMeta.LimitHit = function() end + if gamemode.Call( "PlayerSpawnSENT", self.player, class ) == false then + playerMeta.LimitHit = limithit + return NULL + end + playerMeta.LimitHit = limithit + + pos = WireLib.clampPos( pos ) + + local entity + + local whitelist_sent, sent = list.Get("wire_spawnable_ents_whitelist")[class], list.Get("SpawnableEntities")[class] + + local isWhitelist = wire_expression2_propcore_sents_whitelist:GetBool() + if isWhitelist and not whitelist_sent and sent then + return self:throw("Spawning entity '" .. class .. "' is not allowed! wire_expression2_propcore_sents_whitelist is enabled", NULL) + elseif not whitelist_sent and not sent then + return self:throw("Sent class '" .. class .. "' is not registered nor in entity tab!", NULL) + --elseif isWhitelist and sent then + --whitelist_sent = sent + end + + data = castE2ValueToLuaValue(TYPE_TABLE, data) + if whitelist_sent then + local sentParams = whitelist_sent or {} + + if data.Model and isstring(data.Model) then + if #data.Model == 0 and sentParams.Model[2] and isstring(sentParams.Model[2]) then data.Model = sentParams.Model[2] end -- Let's try being forgiving (defaulting the model, if provided empty model path). + + if not util.IsValidProp( data.Model ) then return self:throw("'" .. data.Model .. "' is not a valid model!", NULL) end + if not WireLib.CanModel( self.player, data.Model ) then return self:throw("You are not allowed to spawn model '" .. data.Model .. "'", NULL) end + end + + -- Not sure if we should check for invalid parameters, as it's not really a problem if the user provides more parameters than needed (they will be ignored), but the check + -- against pre/post factories injections is still required. If you want to validate that all parameters are valid, uncomment the following code instead. + -- ( Although I'm not sure that compiler would allow injection(I couldn't make it), some smart lads could still find a workaround, so it's better to be safe than sorry :) ) + --for k, v in pairs(data) do + --if not sentParams[k] then return self:throw("Invalid parameter name '" .. tostring(k).."'", NULL) end + --end + + -- And comment that instead to save cpu time. + if data._preFactory then return self:throw("Invalid parameter name '_preFactory'", NULL) end + if data._postFactory then return self:throw("Invalid parameter name '_postFactory'", NULL) end + + local entityData = {} + + -- Apply data and cast types. + for param, org in pairs( sentParams ) do + if TypeID(org)==TYPE_FUNCTION then continue end -- Skipping pre/post factories. + + local value = data[param] + + if value~=nil then -- Attempting to set provided value (need to cast from E2 to Lua type). + local res = castE2ValueToLuaValue(org[1], value) + if res==nil then return self:throw("Incorrect parameter '".. param .. "' type during spawning '" .. class .. "'. Expected '" .. luaTypeIDToString[org[1]] .. "'. Received '" .. string.lower(type(value)) .. "'", NULL) end + + entityData[param] = res + elseif org[2]~=nil then -- Attempting to set default value if none provided. + entityData[param] = org[2] + else + self:throw("Missing parameter '" .. param .. "' and no default value is registered.", NULL) + end + end + + -- Constructing an entity table + local enttbl = entityData + enttbl.Name = "" + enttbl.Data = entityData + enttbl.Class = class + enttbl.Pos = pos + enttbl.Angle = angles + + -- Better be safe, pcall this to ensure we continue running our code, in case these external functions cause an error... + local factoryErrMessage + local isOk, errMessage = pcall(function() + if whitelist_sent._preFactory then + factoryErrMessage = whitelist_sent._preFactory(self.player, enttbl) + if factoryErrMessage then error() end + end + + entity = duplicator.CreateEntityFromTable(self.player, enttbl) + + if not isentity(entity) then + factoryErrMessage = "(Either corrupted data, or internal error)" + error("") + end + + if whitelist_sent._postFactory then + factoryErrMessage = whitelist_sent._postFactory(self.player, entity, enttbl) + if factoryErrMessage then error() end + end + + if entity.PreEntityCopy then + entity:PreEntityCopy() -- To build dupe modifiers + end + if entity.PostEntityCopy then + entity:PostEntityCopy() + end + if entity.PostEntityPaste then + entity:PostEntityPaste(self.player, entity, {[entity:EntIndex()] = entity}) + end + end) + + if not isOk then + if IsValid(entity) then entity:Remove() end + if factoryErrMessage then + return self:throw("Failed to spawn '" .. class .. "'. " .. tostring(factoryErrMessage) .. " (Is your data valid?)", NULL) + end + + return self:throw("Failed to spawn '" .. class .. "'. (Internal error). Traceback: " .. tostring(errMessage), NULL) -- Not sure, if we should provide tracebacks to scare people. + end + elseif sent then -- Spawning an entity from entity tab. + if sent.AdminOnly and not self.player:IsAdmin() then return self:throw("You do not have permission to spawn '" .. class .. "' (admin-only)!", NULL) end + + local mockTrace = { + FractionLeftSolid = 0, + HitNonWorld = true, + Fraction = 0, + Entity = NULL, + HitPos = Vector(pos), + HitNormal = Vector(0, 0, 0), + HitBox = 0, + Normal = Vector(1, 0, 0), + Hit = true, + HitGroup = 0, + MatType = 0, + StartPos = Vector(0, 0, 0), + PhysicsBone = 0, + WorldToLocal = Vector(0, 0, 0), + } + if sent.t and sent.t.SpawnFunction then + entity = sent.t.SpawnFunction( sent.t, ply, mockTrace, class ) + else + entity = ents.Create( class ) + if IsValid(entity) then + entity:SetPos(pos) + entity:SetAngles(angles) + entity:Spawn() + entity:Activate() + end + end + + gamemode.Call("PlayerSpawnedSENT", self.player, entity) + end + + if not IsValid( entity ) then return NULL end + + entity:Activate() + + local phys = entity:GetPhysicsObject() + if IsValid( phys ) then + if angles ~= nil then setAng( phys, angles ) end + phys:Wake() + if freeze > 0 then phys:EnableMotion( false ) end + end + + self.player:AddCleanup( "e2_spawned_sents", entity ) + + if self.data.propSpawnUndo then + undo.Create( "e2_spawned_sent" ) + undo.AddEntity( entity ) + undo.SetPlayer( self.player ) + undo.Finish( "E2 Sent" .. " (" .. class .. ")" ) + end + + entity:CallOnRemove( "wire_expression2_propcore_remove", + function( entity ) + self.data.spawnedProps[ entity ] = nil + E2totalspawnedprops = E2totalspawnedprops - 1 + end + ) + + self.data.spawnedProps[ entity ] = self.data.propSpawnUndo + E2totalspawnedprops = E2totalspawnedprops + 1 + E2tempSpawnedProps = E2tempSpawnedProps + 1 + return entity +end + +local CreateSent = PropCore.CreateSent + +-------------------------------------------------------------------------------- __e2setcost(40) e2function entity propSpawn(string model, number frozen) if not ValidAction(self, nil, "spawn") then return NULL end @@ -243,6 +637,177 @@ end -------------------------------------------------------------------------------- +__e2setcost(150) +e2function entity sentSpawn(string class) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, self.entity:GetPos()+self.entity:GetUp()*25, self.entity:GetAngles(), 1, {}) +end + +e2function entity sentSpawn(string class, vector pos) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), self.entity:GetAngles(), 1, {}) +end + +e2function entity sentSpawn(string class, table data) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, self.entity:GetPos()+self.entity:GetUp()*25, self.entity:GetAngles(), 1, data) +end + +e2function entity sentSpawn(string class, vector pos, table data) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), self.entity:GetAngles(), 1, data) +end + +e2function entity sentSpawn(string class, vector pos, angle rot) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), Angle(rot[1],rot[2],rot[3]), 1, {}) +end + +e2function entity sentSpawn(string class, vector pos, angle rot, table data) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), Angle(rot[1],rot[2],rot[3]), 1, data) +end + +e2function entity sentSpawn(string class, vector pos, angle rot, number frozen) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), Angle(rot[1],rot[2],rot[3]), frozen, {}) +end + +e2function entity sentSpawn(string class, vector pos, angle rot, number frozen, table data) + if not ValidAction(self, nil, "spawn") then return NULL end + return CreateSent(self, class, Vector(pos[1],pos[2],pos[3]), Angle(rot[1],rot[2],rot[3]), frozen, data) +end + +-------------------------------------------------------------------------------- + +__e2setcost(25) +[nodiscard] +e2function array sentGetWhitelisted() + local res = {} + + local sents = list.Get("wire_spawnable_ents_whitelist") + + for classname, tbl in pairs( sents ) do + res[#res+1] = classname + end + + return res +end + +-------------------------------------------------------------------------------- + +__e2setcost(30) +[nodiscard] +e2function table sentGetData(string class) + local res = E2Lib.newE2Table() + + local sent = list.Get("wire_spawnable_ents_whitelist")[class] + if not sent then return res end + + local size = 0 + for key, tbl in pairs( sent ) do + res.s[key] = E2Lib.newE2Table() + res.s[key].size = 2 + res.s[key].n[1] = luaTypeIDToString[tbl[1]] + res.s[key].n[2] = TypeID(tbl[2])==TYPE_BOOL and (tbl[2]==true and "1" or "0") or tostring(tbl[2]) + res.s[key].ntypes[1] = "s" + res.s[key].ntypes[2] = "s" + res.stypes[key] = "t" + + size = size + 1 + end + res.size = size + + return res +end + +-------------------------------------------------------------------------------- + +__e2setcost(20) +[nodiscard] +e2function table sentGetDataTypes(string class) + local res = E2Lib.newE2Table() + + local sent = list.Get("wire_spawnable_ents_whitelist")[class] + if not sent then return res end + + local size = 0 + for key, tbl in pairs( sent ) do + res.s[key] = E2Lib.newE2Table() + res.s[key].size = 1 + res.s[key].n[1] = luaTypeIDToString[tbl[1]] + res.s[key].ntypes[1] = "s" + res.stypes[key] = "t" + + size = size + 1 + end + res.size = size + + return res +end + +-------------------------------------------------------------------------------- + +__e2setcost(20) +[nodiscard] +e2function table sentGetDataDefaultValues(string class) + local res = E2Lib.newE2Table() + + local sent = list.Get("wire_spawnable_ents_whitelist")[class] + if not sent then return res end + + local size = 0 + for key, tbl in pairs( sent ) do + res.s[key] = E2Lib.newE2Table() + res.s[key].size = 1 + res.s[key].n[2] = TypeID(tbl[2])==TYPE_BOOL and (tbl[2]==true and "1" or "0") or tostring(tbl[2]) + res.s[key].ntypes[2] = "s" + res.stypes[key] = "t" + + size = size + 1 + end + res.size = size + + return res +end + +-------------------------------------------------------------------------------- + +__e2setcost(5) +[nodiscard] +e2function number sentCanCreate() + return WithinPropcoreLimits() and 1 or 0 +end + +[nodiscard] +e2function number sentCanCreate(string class) + if not WithinPropcoreLimits() then return 0 end + + local whitelist_sent, sent = list.GetForEdit("wire_spawnable_ents_whitelist")[class], list.Get("SpawnableEntities")[class] + if whitelist_sent then return 1 + elseif sent and not wire_expression2_propcore_sents_whitelist:GetBool() then return 1 end + + return 0 +end + +-------------------------------------------------------------------------------- + +__e2setcost(1) +[nodiscard] +e2function number sentIsWhitelist() + return wire_expression2_propcore_sents_whitelist:GetBool() and 1 or 0 +end + +-------------------------------------------------------------------------------- + +__e2setcost(1) +[nodiscard] +e2function number sentIsEnabled() + return wire_expression2_propcore_sents_enabled:GetBool() and 1 or 0 +end + +-------------------------------------------------------------------------------- + local offset = Vector(0, 0, 25) __e2setcost(50) diff --git a/lua/entities/gmod_wire_interactiveprop.lua b/lua/entities/gmod_wire_interactiveprop.lua index 8adb3ffd0c..749c56bc94 100644 --- a/lua/entities/gmod_wire_interactiveprop.lua +++ b/lua/entities/gmod_wire_interactiveprop.lua @@ -198,6 +198,11 @@ InteractiveModels = { } +-- To let other parts of code get the valid model, and to prevent write to the table. +function WireLib.IsValidInteractiveModel( model ) + return InteractiveModels[model] ~= nil +end + InteractiveModels["models/props_c17/furnituresink001a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Furniture Sink" ) InteractiveModels["models/props_interiors/sinkkitchen01a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Kitchen Sink" ) InteractiveModels["models/props_wasteland/prison_sink001a.mdl"] = copyPropUI( "models/props_interiors/bathtub01a.mdl", "Prison Sink" ) diff --git a/lua/entities/sents_default_params.lua b/lua/entities/sents_default_params.lua new file mode 100644 index 0000000000..438dc172c9 --- /dev/null +++ b/lua/entities/sents_default_params.lua @@ -0,0 +1,1197 @@ +-- Structure to register components for propcore's sentSpawn function. +-- Most of the code is 'stolen' from Sevii77/starfall (https://github.com/Sevii77 / https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_sv/prop_sent.lua). +-- Thanks for not making my life easier :) + +-- To register - just use register(string classname, table data) in this file, or E2Lib.SentSpawn.WhitelistAdd(classname, data) global function. +-- Data is a table with keys as parameter names (case sensitive) and table, where [1] is lua type, and [2] is default value (can be nil, or not declared, if you don't want no default values). + +-- WARNING: All your data that you want to get from user HAVE TO BE AT THE TOP SCOPE of the data table (second parameter to register function). +-- E.g. You CAN NOT have something like that: +-- +-- register("gmod_foo_sent", { +-- ["Model"] = {TYPE_STRING, "models/maxofs2d/button_05.mdl"}, +-- ["FooTable"] = { +-- ["FooBool"] = {TYPE_BOOL, true}, ["FooNumber"] = {TYPE_NUMBER, 1} +-- } +-- }) +-- +-- You can later on organize it however you want either in _preFactory or _postFactory. +-- (Although it's possible to implement, I see no need in that, and that would introduce additional unneeded complexity and computation time) + +-- WARNING: You have to validate table structures by yourself! +-- (Either you expect numerical table, ranger data, or any other kind of table.) + +-- TIP: If you want to blacklist an entity, you can either use E2Lib.SentSpawn.WhitelistRemove(classname), or just comment it out here, or +-- return false on Expression2_CanSpawnSent hook. (First argument - classname. Second argument - E2's runtime context.) + +-- TIP: You can use "_preFactory" and "_postFactory" keys to register a callbacks, that will be called before, and after the entity was spawned. +-- Parameters are: _preFactory(playerThatTriesToSpawn, entityTable) and _postFactory(playerThatSpawned, spawnedEntity, DataTable) + +-- TIP: Most of basic casting is being handles by propcore's sentSpawn function, so you don't have to worry about that. +-- (E2 Vectors/Vectors4 to Color, E2 Strings to Material, etc.) + +-- Supported types (in which can propcore's sentSpawn cast E2 types): +-- TYPE_STRING, TYPE_NUMBER, TYPE_BOOL, TYPE_ENTITY, TYPE_VECTOR, +-- TYPE_COLOR, TYPE_TABLE, TYPE_USERDATA, TYPE_ANGLE, TYPE_DAMAGEINFO, +-- TYPE_MATERIAL, TYPE_EFFECTDATA, TYPE_MATRIX, TYPE_NIL( :) ) + +-- Even tho you can manually append ents here, you may as well just delete them from below ;/ + +local SentSpawn = E2Lib.SENTSPAWN +if not SentSpawn then + SentSpawn = {} + E2Lib.SentSpawn = SentSpawn +end + +-- Registers new class to be able to be spawned using sentSpawn, and appends it to 'wire_spawnable_ents_whitelist' list. +-- Even tho it can be used outside of this file, I would recommend appending your entities here. (To keep everything tidy.) +-- This is made mainly to support thirdparty addons. +---@param class string +---@param data table +function SentSpawn.WhitelistAdd(class, data) + if TypeID(class) ~= TYPE_STRING then ErrorNoHaltWithStack("Class type must be TYPE_STRING!") return end + if TypeID(data) ~= TYPE_TABLE then ErrorNoHaltWithStack("Data type must be TYPE_TABLE") return end + + list.Set("wire_spawnable_ents_whitelist", class, data) +end + +-- Deletes registered sent class and it's data from 'wire_spawnable_ents_whitelist' list. (So it can no longer be spawned with sentSpawn.) +-- Even tho it can be used outside of this file, I would recommend removing your entities here. (To keep everything tidy.) +-- This is made mainly to support thirdparty addons. +---@param class string +function SentSpawn.WhitelistRemove(class) + if not list.HasEntry("wire_spawnable_ents_whitelist", class) then ErrorNoHaltWithStack("Trying to remove entity that is not registered!") return end + + local whitelist = list.GetForEdit("wire_spawnable_ents_whitelist") + table.remove(whitelist, class) +end + +local register = SentSpawn.WhitelistAdd + +local SocketPlugPairs = {} +for socket, tbl in pairs(list.Get("Wire_Socket_Models")) do + SocketPlugPairs[socket] = tbl.plug +end +local PlugSocketPairs = table.Flip(SocketPlugPairs) +local gmod_wire_rt_screen_validScreenEffects = { + normal = true, + ep1_projector = true, + ep1_projector_noisy = true, + flicker1 = true, + flicker2 = true, + hl2_combinedisplay1 = true, + hl2_combinedisplay2 = true, + hl2_combineholo = true, + noisy1 = true, + noisy2 = true, + scanlines = true +} + +-- Sent registering -- + +---------------------------------------- + +-- Basic Gmod sents + +register("gmod_balloon", { + ["Model"] = {TYPE_STRING, "models/maxofs2d/balloon_classic.mdl"}, + ["force"] = {TYPE_NUMBER, -50}, + ["r"] = {TYPE_NUMBER, 255}, + ["g"] = {TYPE_NUMBER, 255}, + ["b"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_button", { + ["Model"] = {TYPE_STRING, "models/maxofs2d/button_05.mdl"}, + ["description"] = {TYPE_STRING, ""}, + ["key"] = {TYPE_NUMBER, KEY_NONE}, + ["toggle"] = {TYPE_BOOL, true}, +}) + +-- register("gmod_cameraprop", { +-- ["Model"] = {TYPE_STRING, "models/dav0r/camera.mdl"}, +-- ["controlkey"] = {TYPE_NUMBER, KEY_NONE}, +-- ["locked"] = {TYPE_BOOL, false}, +-- ["toggle"] = {TYPE_BOOL, true}, +-- }) + +register("gmod_dynamite", { + ["Model"] = {TYPE_STRING, "models/dav0r/tnt/tnt.mdl"}, + ["key"] = {TYPE_NUMBER, KEY_NONE}, + ["Damage"] = {TYPE_NUMBER, 200}, + ["delay"] = {TYPE_NUMBER, 0}, + ["remove"] = {TYPE_BOOL, false}, +}) + +-- register("gmod_emitter", { +-- ["Model"] = {TYPE_STRING, "models/props_lab/tpplug.mdl"}, +-- ["effect"] = {TYPE_STRING, "ManhackSparks"}, +-- ["key"] = {TYPE_NUMBER, KEY_NONE}, +-- ["delay"] = {TYPE_NUMBER, 1}, +-- ["scale"] = {TYPE_NUMBER, 1}, +-- ["toggle"] = {TYPE_BOOL, true}, +-- ["starton"] = {TYPE_BOOL, false}, +-- }) + +register("gmod_hoverball", { + ["Model"] = {TYPE_STRING, "models/dav0r/hoverball.mdl"}, + ["key_u"] = {TYPE_NUMBER, KEY_NONE}, + ["key_d"] = {TYPE_NUMBER, KEY_NONE}, + ["speed"] = {TYPE_NUMBER, 1}, + ["resistance"] = {TYPE_NUMBER, 0}, + ["strength"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_lamp", { + ["Model"] = {TYPE_STRING, "models/lamps/torch.mdl"}, + ["Texture"] = {TYPE_STRING, "effects/flashlight001"}, + ["KeyDown"] = {TYPE_NUMBER, KEY_NONE}, + ["fov"] = {TYPE_NUMBER, 90}, + ["distance"] = {TYPE_NUMBER, 1024}, + ["brightness"] = {TYPE_NUMBER, 4}, + ["toggle"] = {TYPE_BOOL, true}, + ["on"] = {TYPE_BOOL, false}, + ["r"] = {TYPE_NUMBER, 255}, + ["g"] = {TYPE_NUMBER, 255}, + ["b"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_light", { + ["Model"] = {TYPE_STRING, "models/maxofs2d/light_tubular.mdl"}, + ["KeyDown"] = {TYPE_NUMBER, KEY_NONE}, + ["Size"] = {TYPE_NUMBER, 256}, + ["Brightness"] = {TYPE_NUMBER, 2}, + ["toggle"] = {TYPE_BOOL, true}, + ["on"] = {TYPE_BOOL, false}, + ["lightr"] = {TYPE_NUMBER, 255}, + ["lightg"] = {TYPE_NUMBER, 255}, + ["lightb"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_thruster", { + ["Model"] = {TYPE_STRING, "models/props_phx2/garbage_metalcan001a.mdl"}, + ["effect"] = {TYPE_STRING, "fire"}, + ["soundname"] = {TYPE_STRING, "PhysicsCannister.ThrusterLoop"}, + ["key"] = {TYPE_NUMBER, KEY_NONE}, + ["key_bck"] = {TYPE_NUMBER, -1}, + ["force"] = {TYPE_NUMBER, 1500}, + ["toggle"] = {TYPE_BOOL, false}, + ["damageable"] = {TYPE_BOOL, false}, +}) + +---------------------------------------- + +-- Wiremod sents + +register("gmod_wire_plug", { + _preFactory = function(ply, self) + if not PlugSocketPairs[self.Model] then return "Invalid plug model! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/props_lab/tpplug.mdl"}, + ["ArrayInput"] = {TYPE_BOOL, false} +}) + +register("gmod_wire_socket", { + _preFactory = function(ply, self) + if not SocketPlugPairs[self.Model] then return "Invalid socket model! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/props_lab/tpplugholder_single.mdl"}, + ["ArrayInput"] = {TYPE_BOOL, false}, + ["WeldForce"] = {TYPE_NUMBER, 5000}, + ["AttachRange"] = {TYPE_NUMBER, 5} +}) + +register("gmod_wire_rt_camera", { + _preFactory = function(ply, self) + self.CamFOV = math.Clamp(self.CamFOV, 10, 120) + end, + + ["Model"] = {TYPE_STRING, "models/maxofs2d/camera.mdl"}, + ["CamFOV"] = {TYPE_NUMBER, 90} +}) + +register("gmod_wire_rt_screen", { + _preFactory = function(ply, self) + self.ScreenMaterial = string.lower(self.ScreenMaterial) + if not gmod_wire_rt_screen_validScreenEffects[self.ScreenMaterial] then return "Invalid screen material! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/kobilica/wiremonitorbig.mdl"}, + ["ScreenMaterial"] = {TYPE_STRING, "normal"}, +}) + +register("gmod_wire_interactiveprop", { + _preFactory = function(ply, self) + if not WireLib.IsValidInteractiveModel(self.Model) then return "Invalid interactive prop model! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/props_lab/reciever01a.mdl"}, +}) + +register("gmod_wire_dataplug", { + _preFactory = function(ply, self) + if not PlugSocketPairs[self.Model] then return "Invalid plug model! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/hammy/pci_card.mdl"} +}) + +register("gmod_wire_datasocket", { + _preFactory = function(ply, self) + if not SocketPlugPairs[self.Model] then return "Invalid socket model! (Use only those that are shown on toolgun menu)" end + end, + + ["Model"] = {TYPE_STRING, "models/hammy/pci_slot.mdl"}, + ["WeldForce"] = {TYPE_NUMBER, 5000}, + ["AttachRange"] = {TYPE_NUMBER, 5} + +}) + +register("gmod_wire_spawner", { + ["Model"] = {TYPE_STRING}, + ["delay"] = {TYPE_NUMBER, 0}, + ["undo_delay"] = {TYPE_NUMBER, 0}, + ["spawn_effect"] = {TYPE_NUMBER, 0}, + ["mat"] = {TYPE_STRING, ""}, + ["skin"] = {TYPE_NUMBER, 0}, + ["r"] = {TYPE_NUMBER, 255}, + ["g"] = {TYPE_NUMBER, 255}, + ["b"] = {TYPE_NUMBER, 255}, + ["a"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_emarker", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_forcer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["Force"] = {TYPE_NUMBER, 1}, + ["Length"] = {TYPE_NUMBER, 100}, + ["ShowBeam"] = {TYPE_BOOL, true}, + ["Reaction"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_adv_input", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/numpad.mdl"}, + ["keymore"] = {TYPE_NUMBER, 3}, + ["keyless"] = {TYPE_NUMBER, 1}, + ["toggle"] = {TYPE_BOOL, false}, + ["value_min"] = {TYPE_NUMBER, 0}, + ["value_max"] = {TYPE_NUMBER, 10}, + ["value_start"] = {TYPE_NUMBER, 5}, + ["speed"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_oscilloscope", { + ["Model"] = {TYPE_STRING, "models/props_lab/monitor01b.mdl"}, +}) + +register("gmod_wire_dhdd", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, +}) + +register("gmod_wire_friendslist", { + _preFactory = function(ply, self) + for k, steamid in pairs(self.steamids) do + if TypeID(steamid) ~= TYPE_STRING then return "Incorrect 'steamids' entry #"..k.." type! Expected string. Got: "..type( steamid ) end + end + end, + + ["Model"] = {TYPE_STRING, "models/kobilica/value.mdl"}, + ["save_on_entity"] = {TYPE_BOOL, false}, + ["steamids"] = {TYPE_TABLE, {}} +}) + +register("gmod_wire_nailer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["Flim"] = {TYPE_NUMBER, 0}, + ["Range"] = {TYPE_NUMBER, 100}, + ["ShowBeam"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_grabber", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, + ["Range"] = {TYPE_NUMBER, 100}, + ["Gravity"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_weight", { + ["Model"] = {TYPE_STRING, "models/props_interiors/pot01a.mdl"}, +}) + +register("gmod_wire_exit_point", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, +}) + +register("gmod_wire_latch", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_dataport", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, +}) + +register("gmod_wire_colorer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["outColor"] = {TYPE_BOOL, false}, + ["Range"] = {TYPE_NUMBER, 2000}, +}) + +register("gmod_wire_addressbus", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, + ["Mem1st"] = {TYPE_NUMBER, 0}, + ["Mem2st"] = {TYPE_NUMBER, 0}, + ["Mem3st"] = {TYPE_NUMBER, 0}, + ["Mem4st"] = {TYPE_NUMBER, 0}, + ["Mem1sz"] = {TYPE_NUMBER, 0}, + ["Mem2sz"] = {TYPE_NUMBER, 0}, + ["Mem3sz"] = {TYPE_NUMBER, 0}, + ["Mem4sz"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_cd_disk", { + ["Model"] = {TYPE_STRING, "models/venompapa/wirecd_medium.mdl"}, + ["Precision"] = {TYPE_NUMBER, 4}, + ["IRadius"] = {TYPE_NUMBER, 10}, + ["Skin"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_las_receiver", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, +}) + +register("gmod_wire_lever", { + _preFactory = function(ply, self) + self.Model = "models/props_wasteland/tram_leverbase01.mdl" + end, + + ["Min"] = {TYPE_NUMBER, 0}, + ["Max"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_waypoint", { + ["Model"] = {TYPE_STRING, "models/props_lab/powerbox02d.mdl"}, + ["range"] = {TYPE_NUMBER, 150}, +}) + +register("gmod_wire_vehicle", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_vectorthruster", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_speed.mdl"}, + ["force"] = {TYPE_NUMBER, 1500}, + ["force_min"] = {TYPE_NUMBER, 0}, + ["force_max"] = {TYPE_NUMBER, 10000}, + ["oweffect"] = {TYPE_STRING, "fire"}, + ["uweffect"] = {TYPE_STRING, "same"}, + ["owater"] = {TYPE_BOOL, true}, + ["uwater"] = {TYPE_BOOL, true}, + ["bidir"] = {TYPE_BOOL, true}, + ["soundname"] = {TYPE_STRING, ""}, + ["mode"] = {TYPE_NUMBER, 0}, + ["angleinputs"] = {TYPE_BOOL, false}, + ["lengthismul"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_user", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["Range"] = {TYPE_NUMBER, 200}, +}) + +register("gmod_wire_twoway_radio", { + ["Model"] = {TYPE_STRING, "models/props_lab/binderblue.mdl"}, +}) + +register("gmod_wire_numpad", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/numpad.mdl"}, + ["toggle"] = {TYPE_BOOL, false}, + ["value_off"] = {TYPE_NUMBER, 0}, + ["value_on"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_turret", { + ["Model"] = {TYPE_STRING, "models/weapons/w_smg1.mdl"}, + ["delay"] = {TYPE_NUMBER, 0.05}, + ["damage"] = {TYPE_NUMBER, 10}, + ["force"] = {TYPE_NUMBER, 1}, + ["sound"] = {TYPE_STRING, "0"}, + ["numbullets"] = {TYPE_NUMBER, 1}, + ["spread"] = {TYPE_NUMBER, 0}, + ["tracer"] = {TYPE_STRING, "Tracer"}, + ["tracernum"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_soundemitter", { + ["Model"] = {TYPE_STRING, "models/cheeze/wires/speaker.mdl"}, + ["sound"] = {TYPE_STRING, "synth/square.wav"}, +}) + +register("gmod_wire_textscreen", { + ["Model"] = {TYPE_STRING, "models/kobilica/wiremonitorbig.mdl"}, + ["text"] = {TYPE_STRING, ""}, + ["chrPerLine"] = {TYPE_NUMBER, 6}, + ["textJust"] = {TYPE_NUMBER, 1}, + ["valign"] = {TYPE_NUMBER, 0}, + ["tfont"] = {TYPE_STRING, "Arial"}, + ["fgcolor"] = {TYPE_COLOR, Color(255, 255, 255)}, + ["bgcolor"] = {TYPE_COLOR, Color(0, 0, 0)}, +}) + +register("gmod_wire_holoemitter", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, +}) + +register("gmod_wire_textreceiver", { + _preFactory = function(ply, self) + for k, v in ipairs(self.Matches) do + if TypeID(v) ~= TYPE_STRING then return "Incorrect 'Matches' entry #"..k.." type! Expected string. Got: "..type( v ) end + end + end, + + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, + ["UseLuaPatterns"] = {TYPE_BOOL, false}, + ["Matches"] = {TYPE_TABLE, {"Hello World"}}, + ["CaseInsensitive"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_textentry", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/keyboard.mdl"}, +}) + +register("gmod_wire_teleporter", { + ["Model"] = {TYPE_STRING, "models/props_c17/utilityconducter001.mdl"}, + ["UseSounds"] = {TYPE_BOOL, true}, + ["UseEffects"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_target_finder", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/targetfinder.mdl"}, + ["range"] = {TYPE_NUMBER, 1000}, + ["players"] = {TYPE_BOOL, false}, + ["npcs"] = {TYPE_BOOL, true}, + ["npcname"] = {TYPE_STRING, ""}, + ["beacons"] = {TYPE_BOOL, false}, + ["hoverballs"] = {TYPE_BOOL, false}, + ["thrusters"] = {TYPE_BOOL, false}, + ["props"] = {TYPE_BOOL, false}, + ["propmodel"] = {TYPE_STRING, ""}, + ["vehicles"] = {TYPE_BOOL, false}, + ["playername"] = {TYPE_STRING, ""}, + ["casesen"] = {TYPE_BOOL, false}, + ["rpgs"] = {TYPE_BOOL, false}, + ["painttarget"] = {TYPE_BOOL, true}, + ["minrange"] = {TYPE_NUMBER, 1}, + ["maxtargets"] = {TYPE_NUMBER, 1}, + ["maxbogeys"] = {TYPE_NUMBER, 1}, + ["notargetowner"] = {TYPE_BOOL, false}, + ["entity"] = {TYPE_STRING, ""}, + ["notownersstuff"] = {TYPE_BOOL, false}, + ["steamname"] = {TYPE_STRING, ""}, + ["colorcheck"] = {TYPE_BOOL, false}, + ["colortarget"] = {TYPE_BOOL, false}, + ["checkbuddylist"] = {TYPE_BOOL, false}, + ["onbuddylist"] = {TYPE_BOOL, false}, + ["pcolR"] = {TYPE_NUMBER, 255}, + ["pcolG"] = {TYPE_NUMBER, 255}, + ["pcolB"] = {TYPE_NUMBER, 255}, + ["pcolA"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_digitalscreen", { + ["Model"] = {TYPE_STRING, "models/props_lab/monitor01b.mdl"}, + ["ScreenWidth"] = {TYPE_NUMBER, 32}, + ["ScreenHeight"] = {TYPE_NUMBER, 32}, +}) + +register("gmod_wire_trail", { + _preFactory = function(ply, self) + self.Trail = { + Color = self.Color, + Length = self.Length, + StartSize = self.StartSize, + EndSize = self.EndSize, + Material = self.Material + } + end, + + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, + ["Color"] = {TYPE_COLOR, Color(255, 255, 255)}, + ["Length"] = {TYPE_NUMBER, 5}, + ["StartSize"] = {TYPE_NUMBER, 32}, + ["EndSize"] = {TYPE_NUMBER, 0}, + ["Material"] = {TYPE_STRING, "trails/smoke"}, +}) + +register("gmod_wire_egp", { + _preFactory = function(ply, self) + self.model = self.Model + end, + + ["Model"] = {TYPE_STRING, "models/kobilica/wiremonitorbig.mdl"}, +}) + +register("gmod_wire_egp_hud", { + ["Model"] = {TYPE_STRING, "models/bull/dynamicbutton.mdl"}, +}) + +register("gmod_wire_egp_emitter", { + ["Model"] = {TYPE_STRING, "models/bull/dynamicbutton.mdl"}, +}) + +register("gmod_wire_speedometer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_speed.mdl"}, + ["z_only"] = {TYPE_BOOL, false}, + ["AngVel"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_trigger", { + _preFactory = function(ply, self) + self.model = self.Model + end, + + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["filter"] = {TYPE_NUMBER, 0}, + ["owneronly"] = {TYPE_BOOL, false}, + ["sizex"] = {TYPE_NUMBER, 64}, + ["sizey"] = {TYPE_NUMBER, 64}, + ["sizez"] = {TYPE_NUMBER, 64}, + ["offsetx"] = {TYPE_NUMBER, 0}, + ["offsety"] = {TYPE_NUMBER, 0}, + ["offsetz"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_socket", { + ["Model"] = {TYPE_STRING, "models/props_lab/tpplugholder_single.mdl"}, + ["ArrayInput"] = {TYPE_BOOL, false}, + ["WeldForce"] = {TYPE_NUMBER, 5000}, + ["AttachRange"] = {TYPE_NUMBER, 5}, +}) + +register("gmod_wire_simple_explosive", { + ["Model"] = {TYPE_STRING, "models/props_c17/oildrum001_explosive.mdl"}, + ["key"] = {TYPE_NUMBER, 1}, + ["damage"] = {TYPE_NUMBER, 200}, + ["removeafter"] = {TYPE_BOOL, false}, + ["radius"] = {TYPE_NUMBER, 300}, +}) + +register("gmod_wire_sensor", { + ["Model"] = {TYPE_STRING, "models/props_lab/huladoll.mdl"}, + ["xyz_mode"] = {TYPE_BOOL, false}, + ["outdist"] = {TYPE_BOOL, true}, + ["outbrng"] = {TYPE_BOOL, false}, + ["gpscord"] = {TYPE_BOOL, false}, + ["direction_vector"] = {TYPE_BOOL, false}, + ["direction_normalized"] = {TYPE_BOOL, false}, + ["target_velocity"] = {TYPE_BOOL, false}, + ["velocity_normalized"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_screen", { + ["Model"] = {TYPE_STRING, "models/props_lab/monitor01b.mdl"}, + ["SingleValue"] = {TYPE_BOOL, false}, + ["SingleBigFont"] = {TYPE_BOOL, true}, + ["TextA"] = {TYPE_STRING, "Value A"}, + ["TextB"] = {TYPE_STRING, "Value B"}, + ["LeftAlign"] = {TYPE_BOOL, false}, + ["Floor"] = {TYPE_BOOL, false}, + ["FormatNumber"] = {TYPE_BOOL, false}, + ["FormatTime"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_detonator", { + ["Model"] = {TYPE_STRING, "models/props_combine/breenclock.mdl"}, + ["damage"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_relay", { + ["Model"] = {TYPE_STRING, "models/kobilica/relay.mdl"}, + ["keygroup1"] = {TYPE_NUMBER, 1}, + ["keygroup2"] = {TYPE_NUMBER, 2}, + ["keygroup3"] = {TYPE_NUMBER, 3}, + ["keygroup4"] = {TYPE_NUMBER, 4}, + ["keygroup5"] = {TYPE_NUMBER, 5}, + ["keygroupoff"] = {TYPE_NUMBER, 0}, + ["toggle"] = {TYPE_BOOL, true}, + ["normclose"] = {TYPE_NUMBER, 0}, + ["poles"] = {TYPE_NUMBER, 1}, + ["throws"] = {TYPE_NUMBER, 2}, + ["nokey"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_ranger", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, + ["range"] = {TYPE_NUMBER, 1500}, + ["default_zero"] = {TYPE_BOOL, true}, + ["show_beam"] = {TYPE_BOOL, true}, + ["ignore_world"] = {TYPE_BOOL, false}, + ["trace_water"] = {TYPE_BOOL, false}, + ["out_dist"] = {TYPE_BOOL, true}, + ["out_pos"] = {TYPE_BOOL, false}, + ["out_vel"] = {TYPE_BOOL, false}, + ["out_ang"] = {TYPE_BOOL, false}, + ["out_col"] = {TYPE_BOOL, false}, + ["out_val"] = {TYPE_BOOL, false}, + ["out_sid"] = {TYPE_BOOL, false}, + ["out_uid"] = {TYPE_BOOL, false}, + ["out_eid"] = {TYPE_BOOL, false}, + ["out_hnrm"] = {TYPE_BOOL, false}, + ["hires"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_radio", { + ["Model"] = {TYPE_STRING, "models/props_lab/binderblue.mdl"}, + ["Channel"] = {TYPE_STRING, "1"}, + ["values"] = {TYPE_NUMBER, 4}, + ["Secure"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_thruster", { + ["Model"] = {TYPE_STRING, "models/props_c17/lampShade001a.mdl"}, + ["force"] = {TYPE_NUMBER, 1500}, + ["force_min"] = {TYPE_NUMBER, 0}, + ["force_max"] = {TYPE_NUMBER, 10000}, + ["oweffect"] = {TYPE_STRING, "fire"}, + ["uweffect"] = {TYPE_STRING, "same"}, + ["owater"] = {TYPE_BOOL, true}, + ["uwater"] = {TYPE_BOOL, true}, + ["bidir"] = {TYPE_BOOL, true}, + ["soundname"] = {TYPE_STRING, ""}, +}) + +register("gmod_wire_pod", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_data_satellitedish", { + ["Model"] = {TYPE_STRING, "models/props_wasteland/prison_lamp001c.mdl"}, +}) + +register("gmod_wire_consolescreen", { + ["Model"] = {TYPE_STRING, "models/props_lab/monitor01b.mdl"}, +}) + +register("gmod_wire_pixel", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_output", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/numpad.mdl"}, + ["key"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_motor", { + _preFactory = function(ply, self) + if not IsValid(self.Ent1) then return "Ent1 is invalid entity!" end + if not IsValid(self.Ent2) then return "Ent2 is invalid entity!" end + + self.model = self.Model + self.MyId = "starfall_createsent" + end, + + _postFactory = function(ply, self, enttbl) + MakeWireMotor( + ply, + enttbl.Ent1, + enttbl.Ent2, + enttbl.Bone1, + enttbl.Bone2, + enttbl.LPos1, + enttbl.LPos2, + enttbl.friction, + enttbl.torque, + 0, + enttbl.torque, + enttbl.MyId + ) + end, + + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["Ent1"] = {TYPE_ENTITY, nil}, + ["Ent2"] = {TYPE_ENTITY, nil}, + ["Bone1"] = {TYPE_NUMBER, 0}, + ["Bone2"] = {TYPE_NUMBER, 0}, + ["LPos1"] = {TYPE_VECTOR, Vector()}, + ["LPos2"] = {TYPE_VECTOR, Vector()}, + ["friction"] = {TYPE_NUMBER, 1}, + ["torque"] = {TYPE_NUMBER, 500}, + ["forcelimit"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_explosive", { + ["Model"] = {TYPE_STRING, "models/props_c17/oildrum001_explosive.mdl"}, + ["key"] = {TYPE_NUMBER, 1}, + ["damage"] = {TYPE_NUMBER, 200}, + ["delaytime"] = {TYPE_NUMBER, 0}, + ["removeafter"] = {TYPE_BOOL, false}, + ["radius"] = {TYPE_NUMBER, 300}, + ["affectother"] = {TYPE_BOOL, false}, + ["notaffected"] = {TYPE_BOOL, false}, + ["delayreloadtime"] = {TYPE_NUMBER, 0}, + ["maxhealth"] = {TYPE_NUMBER, 100}, + ["bulletproof"] = {TYPE_BOOL, false}, + ["explosionproof"] = {TYPE_BOOL, false}, + ["fallproof"] = {TYPE_BOOL, false}, + ["explodeatzero"] = {TYPE_BOOL, true}, + ["resetatexplode"] = {TYPE_BOOL, true}, + ["fireeffect"] = {TYPE_BOOL, true}, + ["coloreffect"] = {TYPE_BOOL, true}, + ["invisibleatzero"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_light", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["directional"] = {TYPE_BOOL, false}, + ["radiant"] = {TYPE_BOOL, false}, + ["glow"] = {TYPE_BOOL, false}, + ["brightness"] = {TYPE_NUMBER, 2}, + ["size"] = {TYPE_NUMBER, 256}, + ["R"] = {TYPE_NUMBER, 255}, + ["G"] = {TYPE_NUMBER, 255}, + ["B"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_lamp", { + ["Model"] = {TYPE_STRING, "models/lamps/torch.mdl"}, + ["Texture"] = {TYPE_STRING, "effects/flashlight001"}, + ["FOV"] = {TYPE_NUMBER, 90}, + ["Dist"] = {TYPE_NUMBER, 1024}, + ["Brightness"] = {TYPE_NUMBER, 8}, + ["on"] = {TYPE_BOOL, false}, + ["r"] = {TYPE_NUMBER, 255}, + ["g"] = {TYPE_NUMBER, 255}, + ["b"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_keypad", { + _preFactory = function(ply, self) + self.Password = util.CRC(self.Password) + end, + + ["Model"] = {TYPE_STRING, "models/props_lab/keypad.mdl"}, + ["Password"] = {TYPE_STRING}, + ["Secure"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_data_store", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_range.mdl"}, +}) + +register("gmod_wire_gpulib_controller", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_clutch", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_input", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/numpad.mdl"}, + ["keygroup"] = {TYPE_NUMBER, 7}, + ["toggle"] = {TYPE_BOOL, false}, + ["value_off"] = {TYPE_NUMBER, 0}, + ["value_on"] = {TYPE_NUMBER, 1}, +}) + +register("gmod_wire_indicator", { + ["Model"] = {TYPE_STRING, "models/segment.mdl"}, + ["a"] = {TYPE_NUMBER, 0}, + ["b"] = {TYPE_NUMBER, 1}, + ["ar"] = {TYPE_NUMBER, 255}, + ["ag"] = {TYPE_NUMBER, 0}, + ["ab"] = {TYPE_NUMBER, 0}, + ["aa"] = {TYPE_NUMBER, 255}, + ["br"] = {TYPE_NUMBER, 0}, + ["bg"] = {TYPE_NUMBER, 255}, + ["bb"] = {TYPE_NUMBER, 0}, + ["ba"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_igniter", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["TargetPlayers"] = {TYPE_BOOL, false}, + ["Range"] = {TYPE_NUMBER, 2048}, +}) + +register("gmod_wire_hydraulic", { + _preFactory = function(ply, self) + if not IsValid(self.Ent1) then return "Ent1 is invalid entity!" end + if not IsValid(self.Ent2) then return "Ent2 is invalid entity!" end + + self.model = self.Model + self.MyId = "starfall_createsent" + end, + + _postFactory = function(ply, self, enttbl) + MakeWireHydraulic( + ply, + enttbl.Ent1, + enttbl.Ent2, + enttbl.Bone1, + enttbl.Bone2, + enttbl.LPos1, + enttbl.LPos2, + enttbl.width, + enttbl.material, + enttbl.speed, + enttbl.fixed, + enttbl.stretchonly, + enttbl.MyId + ) + end, + + ["Model"] = {TYPE_STRING, "models/beer/wiremod/hydraulic.mdl"}, + ["Ent1"] = {TYPE_ENTITY, nil}, + ["Ent2"] = {TYPE_ENTITY, nil}, + ["Bone1"] = {TYPE_NUMBER, 0}, + ["Bone2"] = {TYPE_NUMBER, 0}, + ["LPos1"] = {TYPE_VECTOR, Vector()}, + ["LPos2"] = {TYPE_VECTOR, Vector()}, + ["width"] = {TYPE_NUMBER, 3}, + ["material"] = {TYPE_STRING, "cable/rope"}, + ["speed"] = {TYPE_NUMBER, 16}, + ["fixed"] = {TYPE_NUMBER, 0}, + ["stretchonly"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_hudindicator", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["a"] = {TYPE_NUMBER, 0}, + ["b"] = {TYPE_NUMBER, 1}, + ["material"] = {TYPE_STRING, "models/debug/debugwhite"}, + ["showinhud"] = {TYPE_BOOL, false}, + ["huddesc"] = {TYPE_STRING, ""}, + ["hudaddname"] = {TYPE_BOOL, false}, + ["hudshowvalue"] = {TYPE_NUMBER, 0}, + ["hudstyle"] = {TYPE_NUMBER, 0}, + ["allowhook"] = {TYPE_BOOL, true}, + ["fullcircleangle"] = {TYPE_NUMBER, 0}, + ["ar"] = {TYPE_NUMBER, 255}, + ["ag"] = {TYPE_NUMBER, 0}, + ["ab"] = {TYPE_NUMBER, 0}, + ["aa"] = {TYPE_NUMBER, 255}, + ["br"] = {TYPE_NUMBER, 0}, + ["bg"] = {TYPE_NUMBER, 255}, + ["bb"] = {TYPE_NUMBER, 0}, + ["ba"] = {TYPE_NUMBER, 255}, +}) + +register("gmod_wire_hoverball", { + ["Model"] = {TYPE_STRING, "models/dav0r/hoverball.mdl"}, + ["speed"] = {TYPE_NUMBER, 1}, + ["resistance"] = {TYPE_NUMBER, 0}, + ["strength"] = {TYPE_NUMBER, 1}, + ["starton"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_fx_emitter", { + _preFactory = function(ply, self) + if not ComboBox_Wire_FX_Emitter_Options[self.effect] then return "Invalid effect name" end + self.effect = ComboBox_Wire_FX_Emitter_Options[self.effect] + end, + + ["Model"] = {TYPE_STRING, "models/props_lab/tpplug.mdl"}, + ["delay"] = {TYPE_NUMBER, 0.07}, + ["effect"] = {TYPE_STRING, "sparks"}, +}) + +register("gmod_wire_hologrid", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["usegps"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_data_transferer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["Range"] = {TYPE_NUMBER, 25000}, + ["DefaultZero"] = {TYPE_BOOL, false}, + ["IgnoreZero"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_graphics_tablet", { + ["Model"] = {TYPE_STRING, "models/kobilica/wiremonitorbig.mdl"}, + ["gmode"] = {TYPE_BOOL, false}, + ["draw_background"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_gps", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/gps.mdl"}, +}) + +register("gmod_wire_gimbal", { + ["Model"] = {TYPE_STRING, "models/props_c17/canister01a.mdl"}, +}) + +register("gmod_wire_button", { + ["Model"] = {TYPE_STRING, "models/props_c17/clock01.mdl"}, + ["toggle"] = {TYPE_BOOL, false}, + ["value_off"] = {TYPE_NUMBER, 0}, + ["value_on"] = {TYPE_NUMBER, 1}, + ["description"] = {TYPE_STRING, ""}, + ["entityout"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_extbus", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, +}) + +register("gmod_wire_locator", { + ["Model"] = {TYPE_STRING, "models/props_lab/powerbox02d.mdl"}, +}) + +register("gmod_wire_cameracontroller", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["ParentLocal"] = {TYPE_BOOL, false}, + ["AutoMove"] = {TYPE_BOOL, false}, + ["FreeMove"] = {TYPE_BOOL, false}, + ["LocalMove"] = {TYPE_BOOL, false}, + ["AllowZoom"] = {TYPE_BOOL, false}, + ["AutoUnclip"] = {TYPE_BOOL, false}, + ["DrawPlayer"] = {TYPE_BOOL, true}, + ["AutoUnclip_IgnoreWater"] = {TYPE_BOOL, false}, + ["DrawParent"] = {TYPE_BOOL, true}, +}) + +register("gmod_wire_dual_input", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/numpad.mdl"}, + ["keygroup"] = {TYPE_NUMBER, 7}, + ["keygroup2"] = {TYPE_NUMBER, 4}, + ["toggle"] = {TYPE_BOOL, false}, + ["value_off"] = {TYPE_NUMBER, 0}, + ["value_on"] = {TYPE_NUMBER, 1}, + ["value_on2"] = {TYPE_NUMBER, -1}, +}) + +register("gmod_wire_cd_ray", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_beamcaster.mdl"}, + ["Range"] = {TYPE_NUMBER, 64}, + ["DefaultZero"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_datarate", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, +}) + +register("gmod_wire_keyboard", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_input.mdl"}, + ["AutoBuffer"] = {TYPE_BOOL, true}, + ["Synchronous"] = {TYPE_BOOL, true}, + ["EnterKeyAscii"] = {TYPE_BOOL, true}, +}) + +-- No idea why it's broken. Starfall can't spawn it either. (duplicator.CreateEntityFromTable(self.player, enttbl) returns invalid entity) +-- register("gmod_wire_dynamic_button", { +-- ["Model"] = {TYPE_STRING, "models/bull/ranger.mdl"}, +-- ["toggle"] = {TYPE_BOOL, false}, +-- ["value_on"] = {TYPE_NUMBER, 1}, +-- ["value_off"] = {TYPE_NUMBER, 0}, +-- ["description"] = {TYPE_STRING, ""}, +-- ["entityout"] = {TYPE_BOOL, false}, +-- ["material_on"] = {TYPE_STRING, "bull/dynamic_button_1"}, +-- ["material_off"] = {TYPE_STRING, "bull/dynamic_button_0"}, +-- ["on_r"] = {TYPE_NUMBER, 255}, +-- ["on_g"] = {TYPE_NUMBER, 255}, +-- ["on_b"] = {TYPE_NUMBER, 255}, +-- ["off_r"] = {TYPE_NUMBER, 255}, +-- ["off_g"] = {TYPE_NUMBER, 255}, +-- ["off_b"] = {TYPE_NUMBER, 255}, +-- }) + +register("gmod_wire_damage_detector", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["includeconstrained"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_hdd", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, + ["DriveID"] = {TYPE_NUMBER, 0}, + ["DriveCap"] = {TYPE_NUMBER, 128}, +}) + +register("gmod_wire_watersensor", { + ["Model"] = {TYPE_STRING, "models/beer/wiremod/watersensor.mdl"}, +}) + +register("gmod_wire_value", { + _preFactory = function(ply, self) + local castE2TypeToWireValueType = { + NORMAL = function(val, e2TypeID) + if e2TypeID == TYPE_NUMBER then return tostring(val) end + + if TypeIDe2TypeID == TYPE_STRING then return val end + + return nil + end, + VECTOR = function(val, e2TypeID) + if e2TypeID == TYPE_STRING then + local x,y,z = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) + if x and y and z then return x..", "..y..", "..z end + end + + if e2TypeID == TYPE_VECTOR then return val[1]..", "..val[2]..", "..val[3] end + + if e2TypeID == TYPE_TABLE then + if #val >= 3 and isnumber(val[1]) and isnumber(val[2]) and isnumber(val[3]) then + return val[1]..", "..val[2]..", "..val[3] + end + end + + return nil + end, + VECTOR2 = function(val, e2TypeID) + if e2TypeID == TYPE_TABLE and #val >= 2 and isnumber(val[1]) and isnumber(val[2]) then return val[1]..", "..val[2] end + + if e2TypeID == TYPE_STRING then + local x,y,z = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *$" ) + if x and y and z then return x..", "..y..", "..z end + end + + return nil + end, + VECTOR4 = function(val, e2TypeID) + if e2TypeID == TYPE_TABLE and #val >= 4 and isnumber(val[1]) and isnumber(val[2]) and isnumber(val[3]) and isnumber(val[4]) then + return val[1]..", "..val[2]..", "..val[3]..", "..val[4] + end + + if e2TypeID == TYPE_STRING then + local x,y,z,a = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) + if x and y and z and a then return x..", "..y..", "..z..", "..a end + end + + return nil + end, + STRING = function(val, e2TypeID) + return tostring(val) + end, + ANGLE = function(val, e2TypeID) + if e2TypeID == TYPE_ANGLE then return val[1]..", "..val[2]..", "..val[3] end + + if e2TypeID == TYPE_STRING then + local p,y,r = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) + if p and y and r then return p..", "..y..", "..r end + end + end + } + + local additionalDataNames = { + V = "VECTOR", + XV2 = "VECTOR2", + XV4 = "VECTOR4", + A = "ANGLE", + S = "STRING", + N = "NORMAL", + NUMBER = "NORMAL", + } + + local value = {} + if #self.value == 0 then self.value = {{"NORMAL", 0}} end + for i, val in ipairs(self.value) do + local e2TypeID = TypeID(val) + + if e2TypeID ~= TYPE_TABLE then -- No default value provided. Let's try to find it. + if e2TypeID == TYPE_NUMBER then val = {"NORMAL", castE2TypeToWireValueType["NORMAL"](val, e2TypeID)} + elseif e2TypeID == TYPE_VECTOR then val = {"VECTOR", castE2TypeToWireValueType["VECTOR"](val, e2TypeID)} + elseif e2TypeID == TYPE_ANGLE then val = {"ANGLE", castE2TypeToWireValueType["ANGLE"](val, e2TypeID)} + elseif e2TypeID == TYPE_STRING then val = {"STRING", castE2TypeToWireValueType["STRING"](val, e2TypeID)} + else return "Incorrect 'value' parameter #"..i.." type! Expected table (Ex. table(\"normal\", 0)). Got: "..type( steamid ) end + elseif not isnumber(val[1]) then -- Plain table + if TypeID(val[1])~=TYPE_STRING then return "Incorrect 'value' parameter #"..i.."[1] type! Expected string ('NORMAL/VECTOR/VECTOR2/VECTOR4/ANGLE/STRING'). Got: "..type( val ) end + + local wireValueType = string.upper(tostring(val[1])) + local CastFunc = castE2TypeToWireValueType[wireValueType] + if not CastFunc then + if not additionalDataNames[wireValueType] then return "Incorrect value[" .. i .. "] value! Expected 'NORMAL/VECTOR/VECTOR2/VECTOR4/ANGLE/STRING'. Got '"..wireValueType.."'" end + wireValueType = additionalDataNames[wireValueType] + CastFunc = castE2TypeToWireValueType[wireValueType] + end + val = {wireValueType, CastFunc(val[2], TypeID(val[2]))} + elseif #val == 2 then -- vector2 + local tempVal = castE2TypeToWireValueType["VECTOR2"](val[2], typeID(val[2])) + if tempVal ~= nil then + val = {"VECTOR2", tempVal} + else + return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) + end + elseif #val==4 then -- vector4 + local tempVal = castE2TypeToWireValueType["VECTOR2"](val[2], typeID(val[2])) + if tempVal == nil then return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) end + val = {"VECTOR4", tempVal} + else -- table("normal", 0) support + return "Corrupted 'value' parameter data." + end + + if not isstring(val[1]) or not isstring(val[2]) then return "Corrupted 'value' parameter data." end + + value[i] = { + DataType = val[1], + Value = val[2] + } + end + self.value = value + end, + + ["Model"] = {TYPE_STRING, "models/kobilica/value.mdl"}, + ["value"] = {TYPE_TABLE, {}}, +}) + +register("gmod_wire_adv_emarker", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) + +register("gmod_wire_wheel", { + _preFactory = function(ply, self) + if not IsValid(self.Base) then return "'Base' value is not valid entity!" end + end, + + _postFactory = function(ply, self, enttbl) + local motor, axis = constraint.Motor(self, enttbl.Base, 0, enttbl.Bone, Vector(), enttbl.LPos, enttbl.friction, 1000, 0, 0, false, ply, enttbl.forcelimit) + self:SetWheelBase(enttbl.Base) + self:SetMotor(motor) + self:SetDirection(motor.direction) + local axis = Vector(enttbl.LAxis[1], enttbl.LAxis[2], enttbl.LAxis[3]) + axis:Rotate(self:GetAngles()) + self:SetAxis(axis) + self:DoDirectionEffect() + end, + + ["Model"] = {TYPE_STRING, "models/props_vehicles/carparts_wheel01a.mdl"}, + ["Base"] = {TYPE_ENTITY, nil}, + ["Bone"] = {TYPE_NUMBER, 0}, + ["LPos"] = {TYPE_VECTOR, Vector()}, + ["LAxis"] = {TYPE_VECTOR, Vector(0, 1, 0)}, + ["fwd"] = {TYPE_NUMBER, 1}, + ["bck"] = {TYPE_NUMBER, -1}, + ["stop"] = {TYPE_NUMBER, 0}, + ["BaseTorque"] = {TYPE_NUMBER, 3000}, + ["friction"] = {TYPE_NUMBER, 1}, + ["forcelimit"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_gyroscope", { + ["Model"] = {TYPE_STRING, "models/bull/various/gyroscope.mdl"}, + ["out180"] = {TYPE_BOOL, false}, +}) + +register("gmod_wire_eyepod", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, + ["DefaultToZero"] = {TYPE_NUMBER, 1}, + ["ShowRateOfChange"] = {TYPE_NUMBER, 1}, + ["ClampXMin"] = {TYPE_NUMBER, 0}, + ["ClampXMax"] = {TYPE_NUMBER, 0}, + ["ClampYMin"] = {TYPE_NUMBER, 0}, + ["ClampYMax"] = {TYPE_NUMBER, 0}, + ["ClampX"] = {TYPE_NUMBER, 0}, + ["ClampY"] = {TYPE_NUMBER, 0}, +}) + +register("gmod_wire_gate", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_gate.mdl"}, + ["action"] = {TYPE_STRING, "+"}, +}) + +register("gmod_wire_freezer", { + ["Model"] = {TYPE_STRING, "models/jaanus/wiretool/wiretool_siren.mdl"}, +}) From 908b94b229ff7fafa997efd46e2aafe69ca20ae6 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sat, 15 Jun 2024 00:04:46 +0300 Subject: [PATCH 2/9] Comply with linter --- .../core/custom/prop.lua | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 702f075cdb..7413fb6590 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -244,7 +244,7 @@ local luaTypeIDToString = { [TYPE_COUNT] = "count", [TYPE_COLOR] = "color", } - + -- Only data types that can be directly casted, or already are in the same category. All other -- E2 types are either need to be transformed, or can't be casted at anything except for table. local e2TypeNameToLuaTypeIDTable = { @@ -310,17 +310,17 @@ castE2ValueToLuaValueTable = { if e2TypeID == TYPE_TABLE then if e2Value.ntypes or e2Value.stypes then -- Is it an E2 table? Unpack it correctly then. local res = {} - + -- Handle 'n' field for i, value in pairs(e2Value["n"]) do res[i] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["ntypes"][i]), value) -- recursively unpacks any tables, or just returns the value. end - + -- Handle 's' field for key, value in pairs(e2Value["s"]) do res[key] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["stypes"][key]), value) -- recursively unpacks any tables, or just returns the value. end - + return res end @@ -328,7 +328,7 @@ castE2ValueToLuaValueTable = { end if e2TypeID == TYPE_ANGLE or e2TypeID == TYPE_COLOR or e2TypeID == TYPE_VECTOR or e2TypeID == TYPE_MATRIX then return e2Value:ToTable() end - + return nil end, [TYPE_ENTITY] = function(e2Value) -- TYPE_ENTITY from 'entity' @@ -360,10 +360,10 @@ castE2ValueToLuaValueTable = { local e2TypeID = TypeID(e2Value) if e2TypeID == TYPE_STRING then return Material(e2Value) end if e2TypeID == TYPE_TABLE then -- Png parameters support - if #e2Value != 2 then return nil end - - if TypeID(e2Value[1]) != TYPE_STRING then return nil end - if TypeID(e2Value[2]) != TYPE_STRING then return nil end + if #e2Value ~= 2 then return nil end + + if TypeID(e2Value[1]) ~= TYPE_STRING then return nil end + if TypeID(e2Value[2]) ~= TYPE_STRING then return nil end return Material(e2Value[1], e2Value[2]) end @@ -420,7 +420,7 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) local entity local whitelist_sent, sent = list.Get("wire_spawnable_ents_whitelist")[class], list.Get("SpawnableEntities")[class] - + local isWhitelist = wire_expression2_propcore_sents_whitelist:GetBool() if isWhitelist and not whitelist_sent and sent then return self:throw("Spawning entity '" .. class .. "' is not allowed! wire_expression2_propcore_sents_whitelist is enabled", NULL) @@ -565,7 +565,7 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) end self.player:AddCleanup( "e2_spawned_sents", entity ) - + if self.data.propSpawnUndo then undo.Create( "e2_spawned_sent" ) undo.AddEntity( entity ) @@ -686,7 +686,7 @@ e2function array sentGetWhitelisted() local res = {} local sents = list.Get("wire_spawnable_ents_whitelist") - + for classname, tbl in pairs( sents ) do res[#res+1] = classname end @@ -703,7 +703,7 @@ e2function table sentGetData(string class) local sent = list.Get("wire_spawnable_ents_whitelist")[class] if not sent then return res end - + local size = 0 for key, tbl in pairs( sent ) do res.s[key] = E2Lib.newE2Table() @@ -730,7 +730,7 @@ e2function table sentGetDataTypes(string class) local sent = list.Get("wire_spawnable_ents_whitelist")[class] if not sent then return res end - + local size = 0 for key, tbl in pairs( sent ) do res.s[key] = E2Lib.newE2Table() @@ -755,7 +755,7 @@ e2function table sentGetDataDefaultValues(string class) local sent = list.Get("wire_spawnable_ents_whitelist")[class] if not sent then return res end - + local size = 0 for key, tbl in pairs( sent ) do res.s[key] = E2Lib.newE2Table() From 5b77cefc031c7d061dc652dfe101eaed37fe97b8 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sat, 15 Jun 2024 17:27:57 +0300 Subject: [PATCH 3/9] Move castings to E2/Wire lib Moved casting and typeIdToString to E2Lib and WireLib. Fixed major bug, where registered sents didn't check for prop protection, when creating constrains. Changed comments a bit --- .../core/custom/prop.lua | 214 +----------------- .../gmod_wire_expression2/core/e2lib.lua | 156 +++++++++++++ lua/entities/sents_default_params.lua | 57 +++-- lua/wire/wireshared.lua | 55 +++++ 4 files changed, 259 insertions(+), 223 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 7413fb6590..9283e52bf3 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -16,6 +16,8 @@ local GetBones = E2Lib.GetBones local isValidBone = E2Lib.isValidBone local setPos = WireLib.setPos local setAng = WireLib.setAng +local typeIDToString = WireLib.typeIDToString +local castE2ValueToLuaValue = E2Lib.castE2ValueToLuaValue local E2totalspawnedprops = 0 local E2tempSpawnedProps = 0 @@ -194,212 +196,6 @@ local function boneVerify(self, bone) return ent, index end --- Silly function to make printout on errors more userfriendly. -local luaTypeIDToString = { - [TYPE_NONE] = "none", - [TYPE_NIL] = "nil", - [TYPE_BOOL] = "boolean", - [TYPE_LIGHTUSERDATA] = "lightuserdata", - [TYPE_NUMBER] = "number", - [TYPE_STRING] = "string", - [TYPE_TABLE] = "table", - [TYPE_FUNCTION] = "function", - [TYPE_USERDATA] = "userdata", - [TYPE_THREAD] = "thread", - [TYPE_ENTITY] = "entity", - [TYPE_VECTOR] = "vector", - [TYPE_ANGLE] = "angle", - [TYPE_PHYSOBJ] = "physobj", - [TYPE_SAVE] = "save", - [TYPE_RESTORE] = "restore", - [TYPE_DAMAGEINFO] = "damageinfo", - [TYPE_EFFECTDATA] = "effectdata", - [TYPE_MOVEDATA] = "movedata", - [TYPE_RECIPIENTFILTER] = "recipientfilter", - [TYPE_USERCMD] = "usercmd", - [TYPE_SCRIPTEDVEHICLE] = "scriptedvehicle", - [TYPE_MATERIAL] = "material", - [TYPE_PANEL] = "panel", - [TYPE_PARTICLE] = "particle", - [TYPE_PARTICLEEMITTER] = "particleemitter", - [TYPE_TEXTURE] = "texture", - [TYPE_USERMSG] = "usermsg", - [TYPE_CONVAR] = "convar", - [TYPE_IMESH] = "imesh", - [TYPE_MATERIAL] = "matrix", - [TYPE_SOUND] = "sound", - [TYPE_PIXELVISHANDLE] = "pixelvishandle", - [TYPE_DLIGHT] = "dlight", - [TYPE_VIDEO] = "video", - [TYPE_FILE] = "file", - [TYPE_LOCOMOTION] = "locomotion", - [TYPE_PATH] = "path", - [TYPE_NAVAREA] = "navarea", - [TYPE_SOUNDHANDLE] = "soundhandle", - [TYPE_NAVLADDER] = "navladder", - [TYPE_PARTICLESYSTEM] = "particlesystem", - [TYPE_PROJECTEDTEXTURE] = "projectedtexture", - [TYPE_PHYSCOLLIDE] = "physcollide", - [TYPE_SURFACEINFO] = "surfaceinfo", - [TYPE_COUNT] = "count", - [TYPE_COLOR] = "color", -} - --- Only data types that can be directly casted, or already are in the same category. All other --- E2 types are either need to be transformed, or can't be casted at anything except for table. -local e2TypeNameToLuaTypeIDTable = { - ["none"] = TYPE_NONE, - ["void"] = TYPE_NONE, - [""] = TYPE_NONE, - ["number"] = TYPE_NUMBER, - ["n"] = TYPE_NUMBER, - ["string"] = TYPE_STRING, - ["s"] = TYPE_STRING, - ["entity"] = TYPE_ENTITY, - ["e"] = TYPE_ENTITY, - ["vector"] = TYPE_VECTOR, - ["v"] = TYPE_VECTOR, - ["angle"] = TYPE_ANGLE, - ["a"] = TYPE_ANGLE, -} - -local function e2TypeNameToLuaTypeID(TypeName) - return e2TypeNameToLuaTypeIDTable[string.lower(TypeName)] or TYPE_TABLE -end - --- Lua type -> E2 to lua casting function. No way to implement default behaviour, so use castE2ValueToLuaValue function instead of table. --- (It's forward declaration(to make recursive table unpacking possible). Real table is beneath castE2ValueToLuaValue) -local castE2ValueToLuaValueTable = {} - -local function castE2ValueToLuaValue(targetTypeID, e2Value) - if castE2ValueToLuaValueTable[targetTypeID] then - return castE2ValueToLuaValueTable[targetTypeID](e2Value) - end - - return nil -end - --- Well, most of it is a nobrainer, but still helpful when you're just iterating and casting everything. -castE2ValueToLuaValueTable = { - [TYPE_NIL] = function(e2Value) -- TYPE_NIL from whatever :) - return nil - end, - [TYPE_BOOL] = function(e2Value) -- TYPE_BOOL from 'number' - if TypeID(e2Value)==TYPE_NUMBER then - return e2Value > 0 - end - - return nil - end, - [TYPE_NUMBER] = function(e2Value) -- TYPE_NUMBER from 'number' or 'string' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_NUMBER then return e2Value end - if e2TypeID == TYPE_STRING then return tonumber(e2Value) end - - return nil - end, - [TYPE_STRING] = function(e2Value) -- TYPE_STRING from 'string' or 'number' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_STRING then return e2Value end - if e2TypeID == TYPE_NUMBER then return tostring(e2Value) end - - return nil - end, - [TYPE_TABLE] = function(e2Value) -- TYPE_TABLE from 'table, array, ranger, quaternions, and a most other types that aren't present in other casts' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_TABLE then - if e2Value.ntypes or e2Value.stypes then -- Is it an E2 table? Unpack it correctly then. - local res = {} - - -- Handle 'n' field - for i, value in pairs(e2Value["n"]) do - res[i] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["ntypes"][i]), value) -- recursively unpacks any tables, or just returns the value. - end - - -- Handle 's' field - for key, value in pairs(e2Value["s"]) do - res[key] = castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["stypes"][key]), value) -- recursively unpacks any tables, or just returns the value. - end - - return res - end - - return e2Value -- It's not? Just return it then. - end - - if e2TypeID == TYPE_ANGLE or e2TypeID == TYPE_COLOR or e2TypeID == TYPE_VECTOR or e2TypeID == TYPE_MATRIX then return e2Value:ToTable() end - - return nil - end, - [TYPE_ENTITY] = function(e2Value) -- TYPE_ENTITY from 'entity' - if TypeID(e2Value) == TYPE_ENTITY then return e2Value end - - return nil - end, - [TYPE_VECTOR] = function(e2Value) -- TYPE_VECTOR from 'vector' or 'itable' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_VECTOR then return e2Value end - if e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Vector(e2Value[1], e2Value[2], e2Value[3]) end - - return nil - end, - [TYPE_ANGLE] = function(e2Value) -- TYPE_ANGLE from 'angle' or 'itable' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_ANGLE then return e2Value - elseif e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Angle(e2Value[1], e2Value[2], e2Value[3]) end - - return nil - end, - [TYPE_DAMAGEINFO] = function(e2Value) -- TYPE_DAMAGEINFO from 'damageinfo' - if TypeID(e2Value) == TYPE_DAMAGEINFO then return e2Value end - end, - [TYPE_EFFECTDATA] = function(e2Value) -- TYPE_EFFECTDATA from 'effectdata' - if TypeID(e2Value) == TYPE_EFFECTDATA then return e2Value end - end, - [TYPE_MATERIAL] = function(e2Value) -- TYPE_MATERIAL from 'string' or 'itable' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_STRING then return Material(e2Value) end - if e2TypeID == TYPE_TABLE then -- Png parameters support - if #e2Value ~= 2 then return nil end - - if TypeID(e2Value[1]) ~= TYPE_STRING then return nil end - if TypeID(e2Value[2]) ~= TYPE_STRING then return nil end - - return Material(e2Value[1], e2Value[2]) - end - - return nil - end, - [TYPE_MATRIX] = function(e2Value) -- TYPE_MATRIX from 'matrix4' - if TypeID(e2Value) ~= TYPE_TABLE then return nil end - - if #e2Value == 16 then - for i = 1, 16 do - if not isnumber(e2Value[i]) then return nil end - end - - return Matrix({e2Value[1], e2Value[2], e2Value[3], e2Value[4]}, {e2Value[5], e2Value[6], e2Value[7], e2Value[8]}, {e2Value[9], e2Value[10], e2Value[11], e2Value[12]}, {e2Value[13], e2Value[14], e2Value[15], e2Value[16]}) - end - - return nil - end, - [TYPE_COLOR] = function(e2Value) -- TYPE_COLOR from 'vector' or 'vector4' or 'itable' or 'table' - local e2TypeID = TypeID(e2Value) - if e2TypeID == TYPE_VECTOR then - return Color(e2Value[1], e2Value[2], e2Value[3]) - elseif e2TypeID == TYPE_TABLE then -- vector4 support + direct table support - if isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) and isnumber(e2Value[4]) then - return Color(e2Value[1], e2Value[2], e2Value[3], e2Value[4]) - elseif e2Value.r and e2Value.g and e2Value.b then - if e2Value.a then return Color(e2Value.r, e2Value.g, e2Value.b, e2Value.a) end - return Color(e2Value.r, e2Value.g, e2Value.b) - end - end - - return nil - end, -} - -- Separate from PropCore.CreateProp, to add some additional checks, and don't make PropCore.ValidAction check sent cases each time anything else is attempted to be spawned (microopt). function PropCore.CreateSent(self, class, pos, angles, freeze, data) if not wire_expression2_propcore_sents_enabled:GetBool() then return self:throw("Sent spawning is disabled by server! (wire_expression2_propcore_sents_enabled)", NULL) end @@ -462,7 +258,7 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) if value~=nil then -- Attempting to set provided value (need to cast from E2 to Lua type). local res = castE2ValueToLuaValue(org[1], value) - if res==nil then return self:throw("Incorrect parameter '".. param .. "' type during spawning '" .. class .. "'. Expected '" .. luaTypeIDToString[org[1]] .. "'. Received '" .. string.lower(type(value)) .. "'", NULL) end + if res==nil then return self:throw("Incorrect parameter '".. param .. "' type during spawning '" .. class .. "'. Expected '" .. typeIDToString[org[1]] .. "'. Received '" .. string.lower(type(value)) .. "'", NULL) end entityData[param] = res elseif org[2]~=nil then -- Attempting to set default value if none provided. @@ -708,7 +504,7 @@ e2function table sentGetData(string class) for key, tbl in pairs( sent ) do res.s[key] = E2Lib.newE2Table() res.s[key].size = 2 - res.s[key].n[1] = luaTypeIDToString[tbl[1]] + res.s[key].n[1] = typeIDToString[tbl[1]] res.s[key].n[2] = TypeID(tbl[2])==TYPE_BOOL and (tbl[2]==true and "1" or "0") or tostring(tbl[2]) res.s[key].ntypes[1] = "s" res.s[key].ntypes[2] = "s" @@ -735,7 +531,7 @@ e2function table sentGetDataTypes(string class) for key, tbl in pairs( sent ) do res.s[key] = E2Lib.newE2Table() res.s[key].size = 1 - res.s[key].n[1] = luaTypeIDToString[tbl[1]] + res.s[key].n[1] = typeIDToString[tbl[1]] res.s[key].ntypes[1] = "s" res.stypes[key] = "t" diff --git a/lua/entities/gmod_wire_expression2/core/e2lib.lua b/lua/entities/gmod_wire_expression2/core/e2lib.lua index 20760a544b..24cd3c8304 100644 --- a/lua/entities/gmod_wire_expression2/core/e2lib.lua +++ b/lua/entities/gmod_wire_expression2/core/e2lib.lua @@ -44,6 +44,162 @@ local function checkargtype(argn, value, argtype) end -- -------------------------- Helper functions ----------------------------- + +-- Only data types that can be directly casted, or already are in the same category. All other +-- E2 types are either need to be transformed, or can't be casted to anything except for table. +local e2TypeNameToLuaTypeIDTable = { + ["none"] = TYPE_NONE, + ["void"] = TYPE_NONE, + [""] = TYPE_NONE, + ["number"] = TYPE_NUMBER, + ["n"] = TYPE_NUMBER, + ["string"] = TYPE_STRING, + ["s"] = TYPE_STRING, + ["entity"] = TYPE_ENTITY, + ["e"] = TYPE_ENTITY, + ["vector"] = TYPE_VECTOR, + ["v"] = TYPE_VECTOR, + ["angle"] = TYPE_ANGLE, + ["a"] = TYPE_ANGLE, + ["effect"] = TYPE_EFFECTDATA, + ["xef"] = TYPE_EFFECTDATA, +} + +--- Helper function to get the Lua type ID from an E2 type name. (E2Lib.CastE2ValueToLuaValue is not limited to this!) +local function e2TypeNameToLuaTypeID(TypeName) + return e2TypeNameToLuaTypeIDTable[string.lower(TypeName)] or TYPE_TABLE +end + +-- Lua type -> E2 to lua casting function. No way to implement default behaviour, so use castE2ValueToLuaValue function instead of table. +-- (It's forward declaration(to make recursive table unpacking possible). Real table is beneath castE2ValueToLuaValue) +local castE2ValueToLuaValueTable = {} + +function E2Lib.castE2ValueToLuaValue(targetTypeID, e2Value) + if castE2ValueToLuaValueTable[targetTypeID] then + return castE2ValueToLuaValueTable[targetTypeID](e2Value) + end + + return nil +end + +-- Well, most of it is a nobrainer, but still helpful when you're just iterating and casting everything. +castE2ValueToLuaValueTable = { + [TYPE_BOOL] = function(e2Value) -- from 'number' + if TypeID(e2Value)==TYPE_NUMBER then + return e2Value > 0 + end + + return nil + end, + [TYPE_NUMBER] = function(e2Value) -- from 'number' or 'string' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_NUMBER then return e2Value end + if e2TypeID == TYPE_STRING then return tonumber(e2Value) end + + return nil + end, + [TYPE_STRING] = function(e2Value) -- from 'string' or 'number' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_STRING then return e2Value end + if e2TypeID == TYPE_NUMBER then return tostring(e2Value) end + + return nil + end, + [TYPE_TABLE] = function(e2Value) -- from 'table, array, ranger, quaternions, and a most other types that aren't present in other casts' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_TABLE then + if e2Value.ntypes or e2Value.stypes then -- Is it an E2 table? Unpack it correctly then. + local res = {} + + -- Handle 'n' field + for i, value in pairs(e2Value["n"]) do + res[i] = E2Lib.castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["ntypes"][i]), value) -- recursively unpacks any tables, or just returns the value. + end + + -- Handle 's' field + for key, value in pairs(e2Value["s"]) do + res[key] = E2Lib.castE2ValueToLuaValue(e2TypeNameToLuaTypeID(e2Value["stypes"][key]), value) -- recursively unpacks any tables, or just returns the value. + end + + return res + end + + return e2Value -- It's not? Just return it then. + end + + if e2TypeID == TYPE_ANGLE or e2TypeID == TYPE_COLOR or e2TypeID == TYPE_VECTOR or e2TypeID == TYPE_MATRIX then return e2Value:ToTable() end + + return nil + end, + [TYPE_ENTITY] = function(e2Value) -- from 'entity' + if TypeID(e2Value) == TYPE_ENTITY then return e2Value end + + return nil + end, + [TYPE_VECTOR] = function(e2Value) -- from 'vector' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_VECTOR then return e2Value end + if e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Vector(e2Value[1], e2Value[2], e2Value[3]) end + + return nil + end, + [TYPE_ANGLE] = function(e2Value) -- from 'angle' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_ANGLE then return e2Value + elseif e2TypeID == TYPE_TABLE and isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) then return Angle(e2Value[1], e2Value[2], e2Value[3]) end + + return nil + end, + [TYPE_DAMAGEINFO] = function(e2Value) -- from 'damageinfo' + if TypeID(e2Value) == TYPE_DAMAGEINFO then return e2Value end + end, + [TYPE_EFFECTDATA] = function(e2Value) -- from 'effectdata' + if TypeID(e2Value) == TYPE_EFFECTDATA then return e2Value end + end, + [TYPE_MATERIAL] = function(e2Value) -- from 'string' or 'itable' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_STRING then return Material(e2Value) end + if e2TypeID == TYPE_TABLE then -- Png parameters support + if #e2Value ~= 2 then return nil end + + if TypeID(e2Value[1]) ~= TYPE_STRING then return nil end + if TypeID(e2Value[2]) ~= TYPE_STRING then return nil end + + return Material(e2Value[1], e2Value[2]) + end + + return nil + end, + [TYPE_MATRIX] = function(e2Value) -- from 'matrix4' + if TypeID(e2Value) ~= TYPE_TABLE then return nil end + + if #e2Value == 16 then + for i = 1, 16 do + if not isnumber(e2Value[i]) then return nil end + end + + return Matrix({e2Value[1], e2Value[2], e2Value[3], e2Value[4]}, {e2Value[5], e2Value[6], e2Value[7], e2Value[8]}, {e2Value[9], e2Value[10], e2Value[11], e2Value[12]}, {e2Value[13], e2Value[14], e2Value[15], e2Value[16]}) + end + + return nil + end, + [TYPE_COLOR] = function(e2Value) -- +from 'vector' or 'vector4' or 'itable' or 'table' + local e2TypeID = TypeID(e2Value) + if e2TypeID == TYPE_VECTOR then + return Color(e2Value[1], e2Value[2], e2Value[3]) + elseif e2TypeID == TYPE_TABLE then -- vector4 support + direct table support + if isnumber(e2Value[1]) and isnumber(e2Value[2]) and isnumber(e2Value[3]) and isnumber(e2Value[4]) then + return Color(e2Value[1], e2Value[2], e2Value[3], e2Value[4]) + elseif e2Value.r and e2Value.g and e2Value.b then + if e2Value.a then return Color(e2Value.r, e2Value.g, e2Value.b, e2Value.a) end + return Color(e2Value.r, e2Value.g, e2Value.b) + end + end + + return nil + end, +} + local IsValid = IsValid -- Backwards compatibility diff --git a/lua/entities/sents_default_params.lua b/lua/entities/sents_default_params.lua index 438dc172c9..4600207090 100644 --- a/lua/entities/sents_default_params.lua +++ b/lua/entities/sents_default_params.lua @@ -1,5 +1,5 @@ -- Structure to register components for propcore's sentSpawn function. --- Most of the code is 'stolen' from Sevii77/starfall (https://github.com/Sevii77 / https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_sv/prop_sent.lua). +-- Most of the code is by Sevii77 from starfall (https://github.com/Sevii77 / https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_sv/prop_sent.lua). -- Thanks for not making my life easier :) -- To register - just use register(string classname, table data) in this file, or E2Lib.SentSpawn.WhitelistAdd(classname, data) global function. @@ -18,6 +18,8 @@ -- You can later on organize it however you want either in _preFactory or _postFactory. -- (Although it's possible to implement, I see no need in that, and that would introduce additional unneeded complexity and computation time) +-- WARNING: sentSpawn DO NOT MAKE ANY PROP PROTECTION CHECKS! Check if the entity is not a player/belongs to player yourself! + -- WARNING: You have to validate table structures by yourself! -- (Either you expect numerical table, ranger data, or any other kind of table.) @@ -27,17 +29,18 @@ -- TIP: You can use "_preFactory" and "_postFactory" keys to register a callbacks, that will be called before, and after the entity was spawned. -- Parameters are: _preFactory(playerThatTriesToSpawn, entityTable) and _postFactory(playerThatSpawned, spawnedEntity, DataTable) --- TIP: Most of basic casting is being handles by propcore's sentSpawn function, so you don't have to worry about that. --- (E2 Vectors/Vectors4 to Color, E2 Strings to Material, etc.) +-- TIP: Most of basic castings is being handled by propcore's sentSpawn function, so you don't have to worry about that. +-- (E.g. E2 Vectors/Vectors4 to Color, E2 Strings to Material, etc.) + +-- TIP: To return a strict-only error in _preFactory, or _postFactory, just return a string, which contains the error message. --- Supported types (in which can propcore's sentSpawn cast E2 types): +-- Supported types (to which can E2Lib.castE2ValueToLuaValue cast E2 values): -- TYPE_STRING, TYPE_NUMBER, TYPE_BOOL, TYPE_ENTITY, TYPE_VECTOR, -- TYPE_COLOR, TYPE_TABLE, TYPE_USERDATA, TYPE_ANGLE, TYPE_DAMAGEINFO, --- TYPE_MATERIAL, TYPE_EFFECTDATA, TYPE_MATRIX, TYPE_NIL( :) ) - --- Even tho you can manually append ents here, you may as well just delete them from below ;/ +-- TYPE_MATERIAL, TYPE_EFFECTDATA, TYPE_MATRIX -local SentSpawn = E2Lib.SENTSPAWN +local GetOwner = WireLib.GetOwner +local SentSpawn = E2Lib.SentSpawn if not SentSpawn then SentSpawn = {} E2Lib.SentSpawn = SentSpawn @@ -686,11 +689,22 @@ register("gmod_wire_output", { register("gmod_wire_motor", { _preFactory = function(ply, self) - if not IsValid(self.Ent1) then return "Ent1 is invalid entity!" end - if not IsValid(self.Ent2) then return "Ent2 is invalid entity!" end + if not IsValid(self.Ent1) then return "'Ent1' is invalid entity!" end + if not IsValid(self.Ent2) then return "'Ent2' is invalid entity!" end + + if self.Ent1 == self.Ent2 then return "'Ent1' and 'Ent2' must be different entities!" end + + if self.Ent1:IsPlayer() then return "'Ent1' cannot be a player!" end + if self.Ent2:IsPlayer() then return "'Ent2' cannot be a player!" end + + if self.Ent1:IsNPC() then return "'Ent1' cannot be an NPC!" end + if self.Ent2:IsNPC() then return "'Ent2' cannot be an NPC!" end + + if GetOwner(self.Ent1) ~= ply then return "You do not own 'Ent1'!" end + if GetOwner(self.Ent2) ~= ply then return "You do not own 'Ent2'!" end self.model = self.Model - self.MyId = "starfall_createsent" + self.MyId = "e2_spawned_sent" end, _postFactory = function(ply, self, enttbl) @@ -819,11 +833,22 @@ register("gmod_wire_igniter", { register("gmod_wire_hydraulic", { _preFactory = function(ply, self) - if not IsValid(self.Ent1) then return "Ent1 is invalid entity!" end - if not IsValid(self.Ent2) then return "Ent2 is invalid entity!" end + if not IsValid(self.Ent1) then return "'Ent1' is invalid entity!" end + if not IsValid(self.Ent2) then return "'Ent2' is invalid entity!" end + + if self.Ent1 == self.Ent2 then return "'Ent1' and 'Ent2' must be different entities!" end + + if self.Ent1:IsPlayer() then return "'Ent1' cannot be a player!" end + if self.Ent2:IsPlayer() then return "'Ent2' cannot be a player!" end + + if self.Ent1:IsNPC() then return "'Ent1' cannot be an NPC!" end + if self.Ent2:IsNPC() then return "'Ent2' cannot be an NPC!" end + + if GetOwner(self.Ent1) ~= ply then return "You do not own 'Ent1'!" end + if GetOwner(self.Ent2) ~= ply then return "You do not own 'Ent2'!" end self.model = self.Model - self.MyId = "starfall_createsent" + self.MyId = "e2_spawned_sent" end, _postFactory = function(ply, self, enttbl) @@ -1144,6 +1169,10 @@ register("gmod_wire_adv_emarker", { register("gmod_wire_wheel", { _preFactory = function(ply, self) if not IsValid(self.Base) then return "'Base' value is not valid entity!" end + + if self.Base:IsPlayer() then return "'Base' cannot be a player!" end + if self.Base:IsNPC() then return "'Base' cannot be an NPC!" end + if GetOwner(self.Base) ~= ply then return "You do not own 'Base' entity!" end end, _postFactory = function(ply, self, enttbl) diff --git a/lua/wire/wireshared.lua b/lua/wire/wireshared.lua index 464db949f9..2689c16a0e 100644 --- a/lua/wire/wireshared.lua +++ b/lua/wire/wireshared.lua @@ -1283,3 +1283,58 @@ function WireLib.NotifyBuilder(msg, severity, color) ret[n + 2] = msg return ret end + +local typeIDToStringTable = { + [TYPE_NONE] = "none", + [TYPE_NIL] = "nil", + [TYPE_BOOL] = "boolean", + [TYPE_LIGHTUSERDATA] = "lightuserdata", + [TYPE_NUMBER] = "number", + [TYPE_STRING] = "string", + [TYPE_TABLE] = "table", + [TYPE_FUNCTION] = "function", + [TYPE_USERDATA] = "userdata", + [TYPE_THREAD] = "thread", + [TYPE_ENTITY] = "entity", + [TYPE_VECTOR] = "vector", + [TYPE_ANGLE] = "angle", + [TYPE_PHYSOBJ] = "physobj", + [TYPE_SAVE] = "save", + [TYPE_RESTORE] = "restore", + [TYPE_DAMAGEINFO] = "damageinfo", + [TYPE_EFFECTDATA] = "effectdata", + [TYPE_MOVEDATA] = "movedata", + [TYPE_RECIPIENTFILTER] = "recipientfilter", + [TYPE_USERCMD] = "usercmd", + [TYPE_SCRIPTEDVEHICLE] = "scriptedvehicle", + [TYPE_MATERIAL] = "material", + [TYPE_PANEL] = "panel", + [TYPE_PARTICLE] = "particle", + [TYPE_PARTICLEEMITTER] = "particleemitter", + [TYPE_TEXTURE] = "texture", + [TYPE_USERMSG] = "usermsg", + [TYPE_CONVAR] = "convar", + [TYPE_IMESH] = "imesh", + [TYPE_MATERIAL] = "matrix", + [TYPE_SOUND] = "sound", + [TYPE_PIXELVISHANDLE] = "pixelvishandle", + [TYPE_DLIGHT] = "dlight", + [TYPE_VIDEO] = "video", + [TYPE_FILE] = "file", + [TYPE_LOCOMOTION] = "locomotion", + [TYPE_PATH] = "path", + [TYPE_NAVAREA] = "navarea", + [TYPE_SOUNDHANDLE] = "soundhandle", + [TYPE_NAVLADDER] = "navladder", + [TYPE_PARTICLESYSTEM] = "particlesystem", + [TYPE_PROJECTEDTEXTURE] = "projectedtexture", + [TYPE_PHYSCOLLIDE] = "physcollide", + [TYPE_SURFACEINFO] = "surfaceinfo", + [TYPE_COUNT] = "count", + [TYPE_COLOR] = "color", +} + +-- Silly function to make printouts more userfriendly. +function WireLib.typeIDToString(typeID) + return typeIDToStringTable[typeID] or "unregistered type" +end \ No newline at end of file From bf954ba1705395aef0535f039bf1cf2dd45c86e9 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sat, 15 Jun 2024 17:29:21 +0300 Subject: [PATCH 4/9] Update sents_default_params.lua --- lua/entities/sents_default_params.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lua/entities/sents_default_params.lua b/lua/entities/sents_default_params.lua index 4600207090..52edcdea6b 100644 --- a/lua/entities/sents_default_params.lua +++ b/lua/entities/sents_default_params.lua @@ -693,10 +693,10 @@ register("gmod_wire_motor", { if not IsValid(self.Ent2) then return "'Ent2' is invalid entity!" end if self.Ent1 == self.Ent2 then return "'Ent1' and 'Ent2' must be different entities!" end - + if self.Ent1:IsPlayer() then return "'Ent1' cannot be a player!" end if self.Ent2:IsPlayer() then return "'Ent2' cannot be a player!" end - + if self.Ent1:IsNPC() then return "'Ent1' cannot be an NPC!" end if self.Ent2:IsNPC() then return "'Ent2' cannot be an NPC!" end @@ -837,13 +837,13 @@ register("gmod_wire_hydraulic", { if not IsValid(self.Ent2) then return "'Ent2' is invalid entity!" end if self.Ent1 == self.Ent2 then return "'Ent1' and 'Ent2' must be different entities!" end - + if self.Ent1:IsPlayer() then return "'Ent1' cannot be a player!" end if self.Ent2:IsPlayer() then return "'Ent2' cannot be a player!" end - + if self.Ent1:IsNPC() then return "'Ent1' cannot be an NPC!" end if self.Ent2:IsNPC() then return "'Ent2' cannot be an NPC!" end - + if GetOwner(self.Ent1) ~= ply then return "You do not own 'Ent1'!" end if GetOwner(self.Ent2) ~= ply then return "You do not own 'Ent2'!" end From cf907dc3ff1d6ae3c2f93826ff637efc183a7db9 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sun, 16 Jun 2024 16:13:53 +0300 Subject: [PATCH 5/9] Update prop.lua --- lua/entities/gmod_wire_expression2/core/custom/prop.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 9283e52bf3..0aea0f6c08 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -9,8 +9,9 @@ local sbox_E2_maxProps = CreateConVar( "sbox_E2_maxProps", "-1", FCVAR_ARCHIVE ) local sbox_E2_maxPropsPerSecond = CreateConVar( "sbox_E2_maxPropsPerSecond", "4", FCVAR_ARCHIVE ) local sbox_E2_PropCore = CreateConVar( "sbox_E2_PropCore", "2", FCVAR_ARCHIVE ) -- 2: Players can affect their own props, 1: Only admins, 0: Disabled local sbox_E2_canMakeStatue = CreateConVar("sbox_E2_canMakeStatue", "1", FCVAR_ARCHIVE) -local wire_expression2_propcore_sents_whitelist = CreateConVar("wire_expression2_propcore_sents_whitelist", 1, FCVAR_ARCHIVE, "If 1 - players can spawn sents only that are added to the default sent list, if 0 - players can spawn sents both from registered list AND from entity tab.", 0, 1) -local wire_expression2_propcore_sents_enabled = CreateConVar("wire_expression2_propcore_sents_enabled", 1, FCVAR_ARCHIVE, "If 1 - allows to spawn sents. (Doesn't affect sentSpawn whitelist), if 0 - prevents sentSpawn to be uset at all.", 0, 1) +local wire_expression2_propcore_sents_whitelist = CreateConVar("wire_expression2_propcore_sents_whitelist", 1, FCVAR_ARCHIVE, "If 1 - players can spawn sents only from the default sent list. If 0 - players can spawn sents from both the registered list and the entity tab.", 0, 1) +local wire_expression2_propcore_sents_enabled = CreateConVar("wire_expression2_propcore_sents_enabled", 1, FCVAR_ARCHIVE, "If 1 - this allows sents to be spawned. (Doesn't affect the sentSpawn whitelist). If 0 - prevents sentSpawn from being used at all.", 0, 1) + local isOwner = E2Lib.isOwner local GetBones = E2Lib.GetBones local isValidBone = E2Lib.isValidBone From cae3836b871aa89d122a3c8f482d1f31e666795a Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sun, 23 Jun 2024 20:23:19 +0300 Subject: [PATCH 6/9] Moved sents_default_params.lua Moved sents_default_params.lua Renamed sents_default_params.lua Changed the way to block sents, and renamed whitelist to registry, to reinforce reusability for thirdparty addons --- lua/autorun/wire_load.lua | 1 + .../core/custom/prop.lua | 38 +++++++----- .../server/sents_registry.lua} | 60 ++++++++++++------- 3 files changed, 62 insertions(+), 37 deletions(-) rename lua/{entities/sents_default_params.lua => wire/server/sents_registry.lua} (93%) diff --git a/lua/autorun/wire_load.lua b/lua/autorun/wire_load.lua index 58a4917f10..aaf14834bc 100644 --- a/lua/autorun/wire_load.lua +++ b/lua/autorun/wire_load.lua @@ -88,6 +88,7 @@ if SERVER then include("wire/server/wirelib.lua") include("wire/server/modelplug.lua") include("wire/server/debuggerlib.lua") + include("wire/server/sents_registry.lua") if CreateConVar("wire_force_workshop", "1", FCVAR_ARCHIVE, "Should Wire force all clients to download the Workshop edition of Wire, for models? (requires restart to disable)"):GetBool() then if select(2, WireLib.GetVersion()):find("Workshop", 1, true) then diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 0aea0f6c08..78fafe76ef 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -197,9 +197,15 @@ local function boneVerify(self, bone) return ent, index end +-- A way to statically blacklist a registered sent +local blacklistedSents = { + --gmod_wire_foo = true, +} + -- Separate from PropCore.CreateProp, to add some additional checks, and don't make PropCore.ValidAction check sent cases each time anything else is attempted to be spawned (microopt). function PropCore.CreateSent(self, class, pos, angles, freeze, data) if not wire_expression2_propcore_sents_enabled:GetBool() then return self:throw("Sent spawning is disabled by server! (wire_expression2_propcore_sents_enabled)", NULL) end + if blacklistedSents[class] then return self:throw("Sent class '" .. class .. "' is blacklisted!", NULL) end if hook.Run( "Expression2_CanSpawnSent", class, self ) == false then return self:throw("A hook prevented this sent to be spawned!", nil) end if not WithinPropcoreLimits() then return self:throw("Prop limit reached! (cooldown or max)", NULL) end -- Same logic as in PropCore.ValidSpawn @@ -216,20 +222,20 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) local entity - local whitelist_sent, sent = list.Get("wire_spawnable_ents_whitelist")[class], list.Get("SpawnableEntities")[class] + local registered_sent, sent = list.Get("wire_spawnable_ents_registry")[class], list.Get("SpawnableEntities")[class] local isWhitelist = wire_expression2_propcore_sents_whitelist:GetBool() - if isWhitelist and not whitelist_sent and sent then + if isWhitelist and not registered_sent and sent then return self:throw("Spawning entity '" .. class .. "' is not allowed! wire_expression2_propcore_sents_whitelist is enabled", NULL) - elseif not whitelist_sent and not sent then + elseif not registered_sent and not sent then return self:throw("Sent class '" .. class .. "' is not registered nor in entity tab!", NULL) --elseif isWhitelist and sent then - --whitelist_sent = sent + --registered_sent = sent end data = castE2ValueToLuaValue(TYPE_TABLE, data) - if whitelist_sent then - local sentParams = whitelist_sent or {} + if registered_sent then + local sentParams = registered_sent or {} if data.Model and isstring(data.Model) then if #data.Model == 0 and sentParams.Model[2] and isstring(sentParams.Model[2]) then data.Model = sentParams.Model[2] end -- Let's try being forgiving (defaulting the model, if provided empty model path). @@ -280,8 +286,8 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) -- Better be safe, pcall this to ensure we continue running our code, in case these external functions cause an error... local factoryErrMessage local isOk, errMessage = pcall(function() - if whitelist_sent._preFactory then - factoryErrMessage = whitelist_sent._preFactory(self.player, enttbl) + if registered_sent._preFactory then + factoryErrMessage = registered_sent._preFactory(self.player, enttbl) if factoryErrMessage then error() end end @@ -292,8 +298,8 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) error("") end - if whitelist_sent._postFactory then - factoryErrMessage = whitelist_sent._postFactory(self.player, entity, enttbl) + if registered_sent._postFactory then + factoryErrMessage = registered_sent._postFactory(self.player, entity, enttbl) if factoryErrMessage then error() end end @@ -482,7 +488,7 @@ __e2setcost(25) e2function array sentGetWhitelisted() local res = {} - local sents = list.Get("wire_spawnable_ents_whitelist") + local sents = list.Get("wire_spawnable_ents_registry") for classname, tbl in pairs( sents ) do res[#res+1] = classname @@ -498,7 +504,7 @@ __e2setcost(30) e2function table sentGetData(string class) local res = E2Lib.newE2Table() - local sent = list.Get("wire_spawnable_ents_whitelist")[class] + local sent = list.Get("wire_spawnable_ents_registry")[class] if not sent then return res end local size = 0 @@ -525,7 +531,7 @@ __e2setcost(20) e2function table sentGetDataTypes(string class) local res = E2Lib.newE2Table() - local sent = list.Get("wire_spawnable_ents_whitelist")[class] + local sent = list.Get("wire_spawnable_ents_registry")[class] if not sent then return res end local size = 0 @@ -550,7 +556,7 @@ __e2setcost(20) e2function table sentGetDataDefaultValues(string class) local res = E2Lib.newE2Table() - local sent = list.Get("wire_spawnable_ents_whitelist")[class] + local sent = list.Get("wire_spawnable_ents_registry")[class] if not sent then return res end local size = 0 @@ -580,8 +586,8 @@ end e2function number sentCanCreate(string class) if not WithinPropcoreLimits() then return 0 end - local whitelist_sent, sent = list.GetForEdit("wire_spawnable_ents_whitelist")[class], list.Get("SpawnableEntities")[class] - if whitelist_sent then return 1 + local registered_sent, sent = list.GetForEdit("wire_spawnable_ents_registry")[class], list.Get("SpawnableEntities")[class] + if registered_sent then return 1 elseif sent and not wire_expression2_propcore_sents_whitelist:GetBool() then return 1 end return 0 diff --git a/lua/entities/sents_default_params.lua b/lua/wire/server/sents_registry.lua similarity index 93% rename from lua/entities/sents_default_params.lua rename to lua/wire/server/sents_registry.lua index 52edcdea6b..f21beb74ef 100644 --- a/lua/entities/sents_default_params.lua +++ b/lua/wire/server/sents_registry.lua @@ -1,10 +1,14 @@ --- Structure to register components for propcore's sentSpawn function. --- Most of the code is by Sevii77 from starfall (https://github.com/Sevii77 / https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_sv/prop_sent.lua). +-- Structure to register components for propcore's sentSpawn function and for thirdparty addons. +-- Most of entity code is by Sevii77 from starfall (https://github.com/Sevii77 / https://github.com/thegrb93/StarfallEx/blob/master/lua/starfall/libs_sv/prop_sent.lua). -- Thanks for not making my life easier :) --- To register - just use register(string classname, table data) in this file, or E2Lib.SentSpawn.WhitelistAdd(classname, data) global function. +-- To register - just use register(string classname, table data) in this file, or WireLib.SentSpawn.Register(classname, data) global function. -- Data is a table with keys as parameter names (case sensitive) and table, where [1] is lua type, and [2] is default value (can be nil, or not declared, if you don't want no default values). + + +-- ----- Regarding E2's propcore's sentSpawn ----- -- + -- WARNING: All your data that you want to get from user HAVE TO BE AT THE TOP SCOPE of the data table (second parameter to register function). -- E.g. You CAN NOT have something like that: -- @@ -18,13 +22,14 @@ -- You can later on organize it however you want either in _preFactory or _postFactory. -- (Although it's possible to implement, I see no need in that, and that would introduce additional unneeded complexity and computation time) --- WARNING: sentSpawn DO NOT MAKE ANY PROP PROTECTION CHECKS! Check if the entity is not a player/belongs to player yourself! +-- WARNING: e2's propcore's sentspawn DO NOT MAKE ANY PROP PROTECTION CHECKS! Check if the entity is not a player/belongs to player yourself! -- WARNING: You have to validate table structures by yourself! -- (Either you expect numerical table, ranger data, or any other kind of table.) --- TIP: If you want to blacklist an entity, you can either use E2Lib.SentSpawn.WhitelistRemove(classname), or just comment it out here, or --- return false on Expression2_CanSpawnSent hook. (First argument - classname. Second argument - E2's runtime context.) +-- TIP: If you want to stop an entity from being able to be spawned, either return false on Expression2_CanSpawnSent hook, or statically append a classname + true at +-- lua/entities/gmod_wire_expression2/core/custom/prop.lua:201 (local blacklistedSents table). +-- (Or comment out a register function call in this file, or use WireLib.Unregister, but that possibly can break thirdparty addon support. Use only if you know what you're doing.) -- TIP: You can use "_preFactory" and "_postFactory" keys to register a callbacks, that will be called before, and after the entity was spawned. -- Parameters are: _preFactory(playerThatTriesToSpawn, entityTable) and _postFactory(playerThatSpawned, spawnedEntity, DataTable) @@ -33,43 +38,56 @@ -- (E.g. E2 Vectors/Vectors4 to Color, E2 Strings to Material, etc.) -- TIP: To return a strict-only error in _preFactory, or _postFactory, just return a string, which contains the error message. +-- (If you return a string to non-strict E2, obv it will also stop spawning the entity) --- Supported types (to which can E2Lib.castE2ValueToLuaValue cast E2 values): +-- Supported types (to which can WireLib.castE2ValueToLuaValue cast E2 values): -- TYPE_STRING, TYPE_NUMBER, TYPE_BOOL, TYPE_ENTITY, TYPE_VECTOR, -- TYPE_COLOR, TYPE_TABLE, TYPE_USERDATA, TYPE_ANGLE, TYPE_DAMAGEINFO, -- TYPE_MATERIAL, TYPE_EFFECTDATA, TYPE_MATRIX + + +-- ----- Regarding thirdparty addon support ----- -- + +-- Tbh you can use this as you want, but make sure not to override existing entries, to not break propSpawn (or do it, if you know what you're doing). + +-- WARNING: Obviously, you shouldn't add any sents by WireLib.SentSpawn.Register, if you don't want them to be spawned by propcore's sentSpawn function. +-- (Or blacklist them beforehand) + local GetOwner = WireLib.GetOwner -local SentSpawn = E2Lib.SentSpawn +local SentSpawn = WireLib.SentSpawn if not SentSpawn then SentSpawn = {} - E2Lib.SentSpawn = SentSpawn + WireLib.SentSpawn = SentSpawn end --- Registers new class to be able to be spawned using sentSpawn, and appends it to 'wire_spawnable_ents_whitelist' list. --- Even tho it can be used outside of this file, I would recommend appending your entities here. (To keep everything tidy.) --- This is made mainly to support thirdparty addons. +-- Registers new class to be able to be spawned using sentSpawn, and appends it to 'wire_spawnable_ents_registry' list. +-- Even tho it's global function, I would recommend appending your entities here in this file. (To keep everything tidy and organized). +-- It've been made global to support thirdparty addons. ---@param class string ---@param data table -function SentSpawn.WhitelistAdd(class, data) +function SentSpawn.Register(class, data) if TypeID(class) ~= TYPE_STRING then ErrorNoHaltWithStack("Class type must be TYPE_STRING!") return end if TypeID(data) ~= TYPE_TABLE then ErrorNoHaltWithStack("Data type must be TYPE_TABLE") return end - list.Set("wire_spawnable_ents_whitelist", class, data) + list.Set("wire_spawnable_ents_registry", class, data) end --- Deletes registered sent class and it's data from 'wire_spawnable_ents_whitelist' list. (So it can no longer be spawned with sentSpawn.) --- Even tho it can be used outside of this file, I would recommend removing your entities here. (To keep everything tidy.) --- This is made mainly to support thirdparty addons. +-- Deletes registered sent class and it's data from 'wire_spawnable_ents_registry' list. +-- Even tho it can be used as a way to blacklist an entity from being spawned by propcore's sentSpawn function, better use use Expression2_CanSpawnSent hook, or statically +-- blacklist an entity in lua/entities/gmod_wire_expression2/core/custom/prop.lua:201 (local blacklistedSents table). +-- It've been made global to support thirdparty addons. ---@param class string -function SentSpawn.WhitelistRemove(class) - if not list.HasEntry("wire_spawnable_ents_whitelist", class) then ErrorNoHaltWithStack("Trying to remove entity that is not registered!") return end +function SentSpawn.Unregister(class) + if not list.HasEntry("wire_spawnable_ents_registry", class) then ErrorNoHaltWithStack("Trying to remove entity that is not registered!") return end - local whitelist = list.GetForEdit("wire_spawnable_ents_whitelist") + local whitelist = list.GetForEdit("wire_spawnable_ents_registry") table.remove(whitelist, class) end -local register = SentSpawn.WhitelistAdd + + +local register = SentSpawn.Register local SocketPlugPairs = {} for socket, tbl in pairs(list.Get("Wire_Socket_Models")) do From 88b796801778b1b1f4f60e3b085df3d7b9754321 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sun, 23 Jun 2024 20:42:39 +0300 Subject: [PATCH 7/9] gmod_wire_value bug fixes --- lua/wire/server/sents_registry.lua | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lua/wire/server/sents_registry.lua b/lua/wire/server/sents_registry.lua index f21beb74ef..fb772ede49 100644 --- a/lua/wire/server/sents_registry.lua +++ b/lua/wire/server/sents_registry.lua @@ -1063,8 +1063,7 @@ register("gmod_wire_value", { local castE2TypeToWireValueType = { NORMAL = function(val, e2TypeID) if e2TypeID == TYPE_NUMBER then return tostring(val) end - - if TypeIDe2TypeID == TYPE_STRING then return val end + if e2TypeID == TYPE_STRING then return val end return nil end, @@ -1073,9 +1072,7 @@ register("gmod_wire_value", { local x,y,z = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) if x and y and z then return x..", "..y..", "..z end end - if e2TypeID == TYPE_VECTOR then return val[1]..", "..val[2]..", "..val[3] end - if e2TypeID == TYPE_TABLE then if #val >= 3 and isnumber(val[1]) and isnumber(val[2]) and isnumber(val[3]) then return val[1]..", "..val[2]..", "..val[3] @@ -1086,7 +1083,6 @@ register("gmod_wire_value", { end, VECTOR2 = function(val, e2TypeID) if e2TypeID == TYPE_TABLE and #val >= 2 and isnumber(val[1]) and isnumber(val[2]) then return val[1]..", "..val[2] end - if e2TypeID == TYPE_STRING then local x,y,z = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *$" ) if x and y and z then return x..", "..y..", "..z end @@ -1098,7 +1094,6 @@ register("gmod_wire_value", { if e2TypeID == TYPE_TABLE and #val >= 4 and isnumber(val[1]) and isnumber(val[2]) and isnumber(val[3]) and isnumber(val[4]) then return val[1]..", "..val[2]..", "..val[3]..", "..val[4] end - if e2TypeID == TYPE_STRING then local x,y,z,a = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) if x and y and z and a then return x..", "..y..", "..z..", "..a end @@ -1111,11 +1106,12 @@ register("gmod_wire_value", { end, ANGLE = function(val, e2TypeID) if e2TypeID == TYPE_ANGLE then return val[1]..", "..val[2]..", "..val[3] end - if e2TypeID == TYPE_STRING then local p,y,r = string.match( val, "^ *([^%s,]+) *, *([^%s,]+) *, *([^%s,]+) *$" ) if p and y and r then return p..", "..y..", "..r end end + + return nil end } @@ -1141,7 +1137,7 @@ register("gmod_wire_value", { elseif e2TypeID == TYPE_STRING then val = {"STRING", castE2TypeToWireValueType["STRING"](val, e2TypeID)} else return "Incorrect 'value' parameter #"..i.." type! Expected table (Ex. table(\"normal\", 0)). Got: "..type( steamid ) end elseif not isnumber(val[1]) then -- Plain table - if TypeID(val[1])~=TYPE_STRING then return "Incorrect 'value' parameter #"..i.."[1] type! Expected string ('NORMAL/VECTOR/VECTOR2/VECTOR4/ANGLE/STRING'). Got: "..type( val ) end + if TypeID(val[1]) ~= TYPE_STRING then return "Incorrect 'value' parameter #"..i.."[1] type! Expected string ('NORMAL/VECTOR/VECTOR2/VECTOR4/ANGLE/STRING'). Got: "..type( val ) end local wireValueType = string.upper(tostring(val[1])) local CastFunc = castE2TypeToWireValueType[wireValueType] @@ -1153,16 +1149,17 @@ register("gmod_wire_value", { val = {wireValueType, CastFunc(val[2], TypeID(val[2]))} elseif #val == 2 then -- vector2 local tempVal = castE2TypeToWireValueType["VECTOR2"](val[2], typeID(val[2])) - if tempVal ~= nil then - val = {"VECTOR2", tempVal} - else + if not tempVal then return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) end + + val = {"VECTOR2", tempVal} elseif #val==4 then -- vector4 - local tempVal = castE2TypeToWireValueType["VECTOR2"](val[2], typeID(val[2])) - if tempVal == nil then return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) end + local tempVal = castE2TypeToWireValueType["VECTOR4"](val[2], typeID(val[2])) + if not tempVal then return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) end + val = {"VECTOR4", tempVal} - else -- table("normal", 0) support + else return "Corrupted 'value' parameter data." end From 5d99130b817beecac697a73d93041c81f51e279c Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Sun, 23 Jun 2024 20:44:13 +0300 Subject: [PATCH 8/9] remove whitespace -_- --- lua/wire/server/sents_registry.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/wire/server/sents_registry.lua b/lua/wire/server/sents_registry.lua index fb772ede49..47fe08c0c3 100644 --- a/lua/wire/server/sents_registry.lua +++ b/lua/wire/server/sents_registry.lua @@ -1157,7 +1157,7 @@ register("gmod_wire_value", { elseif #val==4 then -- vector4 local tempVal = castE2TypeToWireValueType["VECTOR4"](val[2], typeID(val[2])) if not tempVal then return "Incorrect 'value' parameter #"..i.." value! Expected 'VECTOR2'. Got: "..tostring(val[2]) end - + val = {"VECTOR4", tempVal} else return "Corrupted 'value' parameter data." From a8aa691d73210fa8e4130a54d250a78ef4c43e25 Mon Sep 17 00:00:00 2001 From: deltamolfar Date: Mon, 24 Jun 2024 21:41:03 +0300 Subject: [PATCH 9/9] WireLib.SentSpawn.Unregister table removal fix --- lua/entities/gmod_wire_expression2/core/custom/prop.lua | 2 -- lua/wire/server/sents_registry.lua | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lua/entities/gmod_wire_expression2/core/custom/prop.lua b/lua/entities/gmod_wire_expression2/core/custom/prop.lua index 78fafe76ef..e60a7b2061 100644 --- a/lua/entities/gmod_wire_expression2/core/custom/prop.lua +++ b/lua/entities/gmod_wire_expression2/core/custom/prop.lua @@ -229,8 +229,6 @@ function PropCore.CreateSent(self, class, pos, angles, freeze, data) return self:throw("Spawning entity '" .. class .. "' is not allowed! wire_expression2_propcore_sents_whitelist is enabled", NULL) elseif not registered_sent and not sent then return self:throw("Sent class '" .. class .. "' is not registered nor in entity tab!", NULL) - --elseif isWhitelist and sent then - --registered_sent = sent end data = castE2ValueToLuaValue(TYPE_TABLE, data) diff --git a/lua/wire/server/sents_registry.lua b/lua/wire/server/sents_registry.lua index 47fe08c0c3..30c177d378 100644 --- a/lua/wire/server/sents_registry.lua +++ b/lua/wire/server/sents_registry.lua @@ -51,7 +51,7 @@ -- Tbh you can use this as you want, but make sure not to override existing entries, to not break propSpawn (or do it, if you know what you're doing). --- WARNING: Obviously, you shouldn't add any sents by WireLib.SentSpawn.Register, if you don't want them to be spawned by propcore's sentSpawn function. +-- WARNING: Obviously, you shouldn't add any sents by WireLib.SentSpawn.Register, or directly to 'wire_spawnable_ents_registry' list, if you don't want them to be spawned by propcore's sentSpawn function. -- (Or blacklist them beforehand) local GetOwner = WireLib.GetOwner @@ -81,8 +81,7 @@ end function SentSpawn.Unregister(class) if not list.HasEntry("wire_spawnable_ents_registry", class) then ErrorNoHaltWithStack("Trying to remove entity that is not registered!") return end - local whitelist = list.GetForEdit("wire_spawnable_ents_registry") - table.remove(whitelist, class) + list.GetForEdit("wire_spawnable_ents_registry")[class] = nil end