From 80bd29368b00c84649eba03857c8038f7d956e7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Fri, 3 May 2024 00:20:08 +0300 Subject: [PATCH 1/3] Fix #243: Players outside changelevel trigger being incorrectly transitioned --- gamemode/sv_transition.lua | 366 ++++++++----------------------------- 1 file changed, 75 insertions(+), 291 deletions(-) diff --git a/gamemode/sv_transition.lua b/gamemode/sv_transition.lua index 9840d94d..a734eedf 100644 --- a/gamemode/sv_transition.lua +++ b/gamemode/sv_transition.lua @@ -10,7 +10,6 @@ local ENT_TYPE_DOOR = 2 local ENT_TYPE_GENERIC = 3 local SERIALIZE_VECTOR = function(ent, val) return ent:WorldToLocal(val) end local SERIALIZE_ANGLES = function(ent, val) return ent:WorldToLocalAngles(val) end - local FIELD_SERIALIZE = { ["Pos"] = SERIALIZE_VECTOR, ["Pos1"] = SERIALIZE_VECTOR, @@ -22,7 +21,6 @@ local FIELD_SERIALIZE = { local DESERIALIZE_VECTOR = function(ent, val) return ent:LocalToWorld(val) end local DESERIALIZE_ANGLES = function(ent, val) return ent:LocalToWorldAngles(val) end - local FIELD_DESERIALIZE = { ["Pos"] = DESERIALIZE_VECTOR, ["Pos1"] = DESERIALIZE_VECTOR, @@ -40,18 +38,11 @@ local DEFAULT_TRANSITION_DATA = { function GM:InitializeTransitionData() local transitionData = DEFAULT_TRANSITION_DATA - if self.IsChangeLevel == true then DbgPrint("Loading TransitionData ...") local encodedTransitionData = util.GetPData("Lambda" .. lambda_instance_id:GetString(), "TransitionData", nil) - - if encodedTransitionData ~= nil then - transitionData = util.JSONToTable(encodedTransitionData) - end - - if transitionData.Map ~= self:GetCurrentMap() then - transitionData = DEFAULT_TRANSITION_DATA - end + if encodedTransitionData ~= nil then transitionData = util.JSONToTable(encodedTransitionData) end + if transitionData.Map ~= self:GetCurrentMap() then transitionData = DEFAULT_TRANSITION_DATA end else DbgPrint("No changelevel, using default TransitionData") end @@ -69,14 +60,10 @@ function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restar -- 1. Lets collect all entities with the landmark name we have to seperate them by landmark and trigger local transitionTriggers = {} local landmarkEnt = nil - if landmark ~= nil and landmark ~= "" then for _, v in pairs(ents.FindByName(landmark)) do if v:GetClass() == "info_landmark" then - if landmarkEnt ~= nil then - DbgPrint("Something is wrong, we already have found the landmark") - end - + if landmarkEnt ~= nil then DbgPrint("Something is wrong, we already have found the landmark") end landmarkEnt = v DbgPrint("Found landmark entity: " .. tostring(landmarkEnt)) elseif v:GetClass() == "trigger_transition" then @@ -89,20 +76,14 @@ function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restar -- 2. We now create a list of objects to transfer. local objectTable = {} local playerTable = {} - if not IsValid(landmarkEnt) then DbgPrint("Unable to find landmark! - " .. tostring(landmark)) else self:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) - -- In case players didnt make it, we erase their position from the data. for k, v in pairs(playerTable) do local ply = Entity(v.RefId) - - if not IsValid(ply) then - DbgError("Invalid player detected, this should not happen") - end - + if not IsValid(ply) then DbgError("Invalid player detected, this should not happen") end if restart == true or table.HasValue(playersInTrigger, ply) == false then DbgPrint("Removing player: " .. tostring(ply) .. " from transitioning, not in changelevel trigger") --playerTable[k] = nil @@ -111,14 +92,10 @@ function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restar end end - if restart == true then - objectTable = {} - end - + if restart == true then objectTable = {} end DbgPrint("Transitioning #" .. tostring(table.Count(objectTable)) .. " objects") --PrintTable(objectTable) DbgPrint("Transitioning #" .. tostring(table.Count(playerTable)) .. " players") - --PrintTable(playerTable) local transitionData = { Objects = objectTable, @@ -129,14 +106,12 @@ function GM:TransitionToLevel(activator, map, landmark, playersInTrigger, restar hook.Run("SaveTransitionData", transitionData) -- We have to mimic the input on transition. local transitionMap = {} - for _, v in pairs(objectTable) do transitionMap[Entity(v.RefId)] = true end for _, v in pairs(ents.GetAll()) do local caps = v:ObjectCaps() - if bit.band(caps, FCAP_NOTIFY_ON_TRANSITION) ~= 0 then if transitionMap[v] == true then v:Fire("OutsideTransition") @@ -182,28 +157,31 @@ local TRANSITION_ENFORCED_NPC = { function GM:ShouldTransitionObject(obj, playersInTrigger) if obj:IsWorld() then return false end + if obj:IsPlayer() then + if obj:IsBot() then + -- Ignore bots. + return false + end + -- Players always transition. + return true + end + local transition = false local caps = obj:ObjectCaps() local class = obj:GetClass() if TRANSITION_BLACKLIST[class] == true then return false end - if bit.band(caps, FCAP_DONT_SAVE) ~= 0 and obj:IsVehicle() == false then DbgPrint("Ignoring object for transition (FCAP_DONT_SAVE): " .. tostring(obj)) - return false end - if bit.band(caps, FCAP_ACROSS_TRANSITION) ~= 0 then - transition = true - end - + if bit.band(caps, FCAP_ACROSS_TRANSITION) ~= 0 then transition = true end if bit.band(caps, FCAP_FORCE_TRANSITION) ~= 0 then transition = true obj.ForceTransition = true end local globalName = obj:GetNWString("GlobalName", obj:GetInternalVariable("globalname") or "") - if globalName ~= "" and obj:IsDormant() == false then transition = true obj.ForceTransition = true @@ -216,7 +194,6 @@ function GM:ShouldTransitionObject(obj, playersInTrigger) if obj:IsVehicle() and transition == false then local driver = obj:GetDriver() - if IsValid(driver) and driver:IsPlayer() then if table.HasValue(playersInTrigger, driver) == true then DbgPrint("Enforcing vehicle to transition because player is driving: " .. tostring(obj)) @@ -229,22 +206,11 @@ function GM:ShouldTransitionObject(obj, playersInTrigger) end end - if transition == false then - DbgPrint("Object " .. tostring(obj) .. " failed transition pass, caps: " .. tostring(caps)) - end - + if transition == false then DbgPrint("Object " .. tostring(obj) .. " failed transition pass, caps: " .. tostring(caps)) end local parent = obj:GetParent() - - if IsValid(parent) and (parent:IsPlayer() or parent:IsNPC() or parent:IsWeapon()) then - transition = false - end - + if IsValid(parent) and (parent:IsPlayer() or parent:IsNPC() or parent:IsWeapon()) then transition = false end local owner = obj:GetOwner() - - if IsValid(owner) and (owner:IsPlayer() or owner:IsNPC()) then - transition = false - end - + if IsValid(owner) and (owner:IsPlayer() or owner:IsNPC()) then transition = false end if obj:IsWeapon() and IsValid(owner) and owner:IsNPC() then -- We deal with that specifically. transition = false @@ -255,7 +221,6 @@ function GM:ShouldTransitionObject(obj, playersInTrigger) transition = true obj.ForceTransition = true end - return transition end @@ -264,15 +229,10 @@ function GM:SerializePlayerData(landmarkEnt, ply, playersInTrigger) local weapons = {} local playerSaveTable = ply:GetSaveTable() local prevWeapon = nil - - if playerSaveTable ~= nil and IsValid(playerSaveTable.m_hLastWeapon) then - prevWeapon = playerSaveTable.m_hLastWeapon - end - + if playerSaveTable ~= nil and IsValid(playerSaveTable.m_hLastWeapon) then prevWeapon = playerSaveTable.m_hLastWeapon end for _, weapon in pairs(ply:GetWeapons()) do local isActive = ply:GetActiveWeapon() == weapon local isPrevious = prevWeapon == weapon - local weaponData = { Class = weapon:GetClass(), Ammo1 = { @@ -295,7 +255,6 @@ function GM:SerializePlayerData(landmarkEnt, ply, playersInTrigger) local groundEnt = ply:GetGroundEntity() local groundId = nil local groundPos = nil - if IsValid(groundEnt) and groundEnt:GetClass() == "func_tracktrain" then groundId = groundEnt:EntIndex() groundPos = groundEnt:WorldToLocal(ply:GetPos()) @@ -323,10 +282,7 @@ function GM:SerializePlayerData(landmarkEnt, ply, playersInTrigger) } if table.HasValue(playersInTrigger, ply) == true and ply:Alive() == true then - if ply:InVehicle() then - data.Vehicle = ply:GetVehicle():EntIndex() - end - + if ply:InVehicle() then data.Vehicle = ply:GetVehicle():EntIndex() end data.Include = true else DbgPrint("Player not in transition volume: " .. tostring(ply)) @@ -337,12 +293,8 @@ function GM:SerializePlayerData(landmarkEnt, ply, playersInTrigger) -- Serialize vectors, angles to local ones by landmark. for k, v in pairs(data) do local serializeFn = FIELD_SERIALIZE[k] - - if serializeFn then - data[k] = serializeFn(landmarkEnt, v) - end + if serializeFn then data[k] = serializeFn(landmarkEnt, v) end end - return data end @@ -360,7 +312,6 @@ local SAVETABLE_BLACKLIST = { function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) DbgPrint("GM:SerializeEntityData(" .. tostring(landmarkEnt) .. ", " .. tostring(ent) .. ")") local currentMap = self:GetCurrentMap() - local data = { Class = ent:GetClass(), RefId = ent:EntIndex(), @@ -392,22 +343,10 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) GlobalName = ent:GetNWString("GlobalName", ent:GetInternalVariable("globalname")) } - if ent.LambdaKeyValues ~= nil then - table.Merge(data.KeyValues, ent.LambdaKeyValues) - end - - if ent.GetNWVars ~= nil then - data.NWVars = ent:GetNWVars() - end - - if ent.GetOutputsTable ~= nil then - data.Outputs = ent:GetOutputsTable() - end - - if ent.EntityOutputs ~= nil then - data.EntityOutputs = table.Copy(ent.EntityOutputs) - end - + if ent.LambdaKeyValues ~= nil then table.Merge(data.KeyValues, ent.LambdaKeyValues) end + if ent.GetNWVars ~= nil then data.NWVars = ent:GetNWVars() end + if ent.GetOutputsTable ~= nil then data.Outputs = ent:GetOutputsTable() end + if ent.EntityOutputs ~= nil then data.EntityOutputs = table.Copy(ent.EntityOutputs) end if ent:IsNPC() then data.Type = ENT_TYPE_NPC data.MovementActivity = ent:GetMovementActivity() @@ -416,13 +355,9 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) data.Activity = ent:GetActivity() data.NPCState = ent:GetNPCState() local activeWeapon = ent:GetActiveWeapon() - - if IsValid(activeWeapon) then - data.ActiveWeapon = activeWeapon:GetClass() - end + if IsValid(activeWeapon) then data.ActiveWeapon = activeWeapon:GetClass() end elseif ent:IsVehicle() then data.Type = ENT_TYPE_VEHICLE - if ent:IsGunEnabled() then data.EnableGun = true else @@ -430,13 +365,9 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) end data.VehicleScript = ent:GetInternalVariable("VehicleScript") - - if ent:GetNWBool("IsPassengerSeat", false) == true then - data.IsPassengerSeat = true - end + if ent:GetNWBool("IsPassengerSeat", false) == true then data.IsPassengerSeat = true end elseif ent:IsDoor() then data.Type = ENT_TYPE_DOOR - if ent:IsDoorOpen() or ent:IsDoorOpening() then data.SpawnFlags = bit.bor(data.SpawnFlags, 1) -- Starts Open end @@ -453,7 +384,6 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) for i = 0, ent:GetPhysicsObjectCount() - 1 do local physObj = ent:GetPhysicsObjectNum(i) - if IsValid(physObj) then local physPos = physObj:GetPos() local physAng = physObj:GetAngles() @@ -477,26 +407,14 @@ function GM:SerializeEntityData(landmarkEnt, ent, playersInTrigger) end local parent = ent:GetParent() - - if IsValid(parent) and not parent:IsPlayer() then - data.Parent = parent:EntIndex() - end - + if IsValid(parent) and not parent:IsPlayer() then data.Parent = parent:EntIndex() end local owner = ent:GetOwner() - - if IsValid(owner) and not owner:IsPlayer() then - data.Owner = parent:EntIndex() - end - + if IsValid(owner) and not owner:IsPlayer() then data.Owner = parent:EntIndex() end -- Serialize vectors, angles to local ones by landmark. for k, v in pairs(data) do local serializeFn = FIELD_SERIALIZE[k] - - if serializeFn then - data[k] = serializeFn(landmarkEnt, v) - end + if serializeFn then data[k] = serializeFn(landmarkEnt, v) end end - return data end @@ -506,27 +424,21 @@ function GM:TransitionObjects(landmarkEnt, objects, objectTable, playerTable, pl local transitionTable = {} local debugTransition = false -- FIXME: Make this part of the debug logging. local processedPlayers = {} - for _, touchingEnt in pairs(objects) do if not IsValid(touchingEnt) then continue end -- Prevent duplicates if processed[touchingEnt] == true then continue end processed[touchingEnt] = true - - if touchingEnt:IsPlayer() and not touchingEnt:IsBot() then + if touchingEnt:IsPlayer() then local ply = touchingEnt local data = self:SerializePlayerData(landmarkEnt, ply, playersInTrigger) table.insert(playerTable, data) processedPlayers[ply] = true - - if debugTransition == true then - table.insert(transitionTable, ply) - end + if debugTransition == true then table.insert(transitionTable, ply) end else local ent = touchingEnt local data = self:SerializeEntityData(landmarkEnt, ent, playersInTrigger) table.insert(objectTable, data) - if debugTransition == true then table.insert(transitionTable, ent) ent:AddDebugOverlays(bit.bor(OVERLAY_PIVOT_BIT, OVERLAY_BBOX_BIT, OVERLAY_NAME_BIT)) @@ -543,54 +455,42 @@ function GM:TransitionObjects(landmarkEnt, objects, objectTable, playerTable, pl end end - if debugTransition == true then - PrintTable(transitionTable) - end + if debugTransition == true then PrintTable(transitionTable) end end function GM:InTransitionVolume(volumes, obj) local caps = obj:ObjectCaps() if bit.band(caps, FCAP_FORCE_TRANSITION) ~= 0 or obj.ForceTransition == true then return true end - for _, volume in pairs(volumes) do local pos = obj:GetPos() local volPos = volume:GetPos() local volMins = volPos + volume:OBBMins() local volMaxs = volPos + volume:OBBMaxs() - -- FIXME: This is currently inaccurate, but theres no way to do it properly. - if pos:WithinAABox(volMins, volMaxs) == false then - return false - end + if pos:WithinAABox(volMins, volMaxs) == false then return false end end - return true end function GM:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) local objects = {} - DbgPrint("Collecting player references...") - - for _, v in pairs(player.GetAll()) do - if v:Alive() == false then continue end - table.insert(objects, v) - - if v:InVehicle() and table.HasValue(playersInTrigger, v) == true then - table.insert(objects, v:GetVehicle()) - end - end DbgPrint("Collecting objects...") local checkVolumes = table.Count(transitionTriggers) > 0 --local inPVS = ents.FindInPVS(landmarkEnt) -- Currently crashing, we use landmark:TestPVS instead. local allEnts = ents.GetAll() + DbgPrint("Transition Volumes:") + for _, v in pairs(transitionTriggers) do + DbgPrint(" " .. tostring(v)) + end for _, v in pairs(allEnts) do if self:ShouldTransitionObject(v, playersInTrigger) == false then continue end - if v.ForceTransition ~= true then - DbgPrint("Testing PVS on: " .. tostring(v)) - if landmarkEnt:TestPVS(v) == false then continue end + if landmarkEnt:TestPVS(v) == false then + DbgPrint("PVS test failed for: " .. tostring(v)) + continue + end else DbgPrint("Enforcing transition: " .. tostring(v)) end @@ -602,9 +502,25 @@ function GM:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, play continue end + if v:IsPlayer() then + if v:IsBot() then + -- Ignore bots. + continue + end + if v:Alive() == false then + -- Ignore dead players. + continue + end + + table.insert(objects, v) + if v:InVehicle() and table.HasValue(playersInTrigger, v) == true then + -- Include vehicle. + table.insert(objects, v:GetVehicle()) + end + end + table.insert(objects, v) end - return objects end @@ -612,12 +528,10 @@ function GM:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, DbgPrint("GM:TransitionNearbyObjects") -- Create initial list. local initialObjectList = self:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) - -- Notify entities about their current state. for _, v in pairs(ents.GetAll()) do local caps = v:ObjectCaps() if bit.band(caps, FCAP_NOTIFY_ON_TRANSITION) == 0 then continue end - if table.HasValue(initialObjectList, v) == false then v:Input("OutsideTransition", activator, activator, nil) DbgPrint("Notifying " .. tostring(v) .. " outside of transitioning") @@ -634,54 +548,8 @@ function GM:TransitionNearbyObjects(activator, landmarkEnt, transitionTriggers, self:TransitionObjects(landmarkEnt, newObjectList, objectTable, playerTable, playersInTrigger) end --- UNUSED -function GM:TransitionObjectsByVolumes(landmarkEnt, transitionTriggers, objectTable, playerTable) - for _, trigger in pairs(transitionTriggers) do - if trigger.GetTouching == nil then - DbgPrint("Flawed trigger, does not have a member called GetTouchingObjects") - continue - end - - local objects = trigger:GetTouching() - - -- Ensure we have always all players in the table. - for _, v in pairs(player.GetAll()) do - table.insert(objects, v) - - if v:InVehicle() then - table.insert(objects, v:GetVehicle()) - end - end - - self:TransitionObjects(landmarkEnt, objects, objectTable, playerTable) - end -end - --- UNUSED -function GM:TransitionObjectsByLandmark(landmarkEnt, objectTable, playerTable) - DbgPrint("GM:TransitionObjectsByLandmark") - local objects = {} - - for _, v in pairs(player.GetAll()) do - table.insert(objects, v) - - if v:InVehicle() then - table.insert(objects, v:GetVehicle()) - end - end - - local inPVS = ents.FindInPVS(landmarkEnt) -- Thanks again Willox :3 - - for _, v in pairs(inPVS) do - table.insert(objects, v) - end - - self:TransitionObjects(landmarkEnt, objects, objectTable, playerTable) -end - function GM:FindEntityByTransitionReference(id) self.CreatedTransitionObjects = self.CreatedTransitionObjects or {} - return self.CreatedTransitionObjects[id] end @@ -693,13 +561,9 @@ function GM:PostLoadTransitionData() local entryLandmark = self:GetEntryLandmark() if entryLandmark == nil then return end local landmarkEnt = nil - for _, v in pairs(ents.FindByName(entryLandmark)) do if v:GetClass() == "info_landmark" then - if landmarkEnt ~= nil then - DbgPrint("Something is wrong, we already have found the landmark") - end - + if landmarkEnt ~= nil then DbgPrint("Something is wrong, we already have found the landmark") end landmarkEnt = v DbgPrint("Found entry landmark entity: " .. tostring(landmarkEnt) .. "( " .. entryLandmark .. ")") break @@ -707,35 +571,24 @@ function GM:PostLoadTransitionData() end if IsValid(landmarkEnt) == false then - if table.Count(self.TransitionData.Players) > 0 or table.Count(self.TransitionData.Objects) > 0 then - DbgError("No landmark found to resolve transition data") - end - + if table.Count(self.TransitionData.Players) > 0 or table.Count(self.TransitionData.Objects) > 0 then DbgError("No landmark found to resolve transition data") end return end DbgPrint("Resolving absolute position on transition players.") - for objId, obj in pairs(self.TransitionData.Players) do for k, v in pairs(obj) do local deserializeFn = FIELD_DESERIALIZE[k] - - if deserializeFn then - self.TransitionData.Players[objId][k] = deserializeFn(landmarkEnt, v) - end + if deserializeFn then self.TransitionData.Players[objId][k] = deserializeFn(landmarkEnt, v) end end end DbgPrint("Resolving absolute position on transition Objects.") - for objId, data in pairs(self.TransitionData.Objects) do for k, v in pairs(data) do if objId == nil then continue end local deserializeFn = FIELD_DESERIALIZE[k] - - if deserializeFn then - self.TransitionData.Objects[objId][k] = deserializeFn(landmarkEnt, v) - end + if deserializeFn then self.TransitionData.Objects[objId][k] = deserializeFn(landmarkEnt, v) end end end end @@ -745,16 +598,11 @@ function GM:LoadTransitionData(data) end local sv_lan = GetConVar("sv_lan") - function GM:GetPlayerTransitionData(ply) - if self.TransitionData == nil then - Error("No transition data table, something is flawed") - end - + if self.TransitionData == nil then Error("No transition data table, something is flawed") end -- Lan support, because duplicates of STEAM_0:0:0 local key = "SteamID64" local val = ply:SteamID64() - if sv_lan:GetBool() == true then key = "UserID" val = ply:UserID() @@ -763,11 +611,9 @@ function GM:GetPlayerTransitionData(ply) for _, v in pairs(self.TransitionData.Players) do if v[key] == val then DbgPrint("Found transition data!") - return v end end - return nil end @@ -776,7 +622,6 @@ GM.CreatedTransitionObjects = GM.CreatedTransitionObjects or {} -- therefor we go by a whitelist. local DOOR_KEYVALUES = {"opendir", "ajarangles", "forceclosed", "spawnpos", "dmg", "hardware", "speed", "health", "returndelay", "movedir"} local VEHICLE_KEYVALUES = {} - local KEYVALUE_BLACKLIST = { ["hammerid"] = true, ["globalname"] = true, @@ -794,17 +639,14 @@ function GM:CreateTransitionObjects() local objects = self.TransitionData.Objects or {} local objCount = table.Count(objects) local curMap = self:GetCurrentMap() - for _, data in pairs(objects) do if data.GlobalName ~= nil and isstring(data.GlobalName) then local e = ents.FindByGlobalName(data.GlobalName) - if IsValid(e) then local oldMdl = data.Mdl data.Mdl = e:GetModel() or oldMdl else local mapData = game.FindEntityByGlobalNameInMapData(data.GlobalName) - if mapData ~= nil and mapData["model"] ~= nil then local oldMdl = data.Mdl data.Mdl = mapData["model"] @@ -817,18 +659,14 @@ function GM:CreateTransitionObjects() -- R local entryLandmark = self:GetEntryLandmark() local landmarkEntities = {} - if entryLandmark ~= nil then DbgPrint("Entry Landmark: " .. entryLandmark) - for _, v in pairs(ents.FindByName(entryLandmark)) do if v:GetClass() ~= "trigger_transition" then continue end DbgPrint("Landmark Entity:", v) local touchingObjects = v:GetTouching() - for _, obj in pairs(touchingObjects) do if v:IsWorld() then continue end - if self:ShouldTransitionObject(obj) or obj.ForceTransition == true then landmarkEntities[obj:EntIndex()] = obj DbgPrint("Entry landmark entity", obj, obj:GetName()) @@ -840,14 +678,11 @@ function GM:CreateTransitionObjects() local function findByGlobalName(name) for k, v in pairs(ents.GetAll()) do local globalName = v:GetNWString("GlobalName", v:GetInternalVariable("globalname")) - if globalName == name then DbgPrint("Found global!") - return k, v end end - return nil, nil end @@ -855,7 +690,6 @@ function GM:CreateTransitionObjects() for k, v in pairs(landmarkEntities) do if v:GetName() == name then return k, v end end - return nil, nil end @@ -863,7 +697,6 @@ function GM:CreateTransitionObjects() for _, data in pairs(objects) do if data.GlobalName ~= nil and isstring(data.GlobalName) and data.GlobalName ~= "" then local k, obj = findByGlobalName(data.GlobalName) - if k ~= nil then DbgPrint("Removing duplicate global entity: " .. tostring(obj)) obj:Remove() @@ -877,22 +710,14 @@ function GM:CreateTransitionObjects() local k local obj local isGlobal = false - if data.GlobalName ~= nil and isstring(data.GlobalName) and data.GlobalName ~= "" then k, obj = findByGlobalName(data.GlobalName) - - if k ~= nil then - isGlobal = true - end - end - - if k == nil and data.Name ~= nil and data.Name ~= "" then - k = findByName(data.Name) + if k ~= nil then isGlobal = true end end + if k == nil and data.Name ~= nil and data.Name ~= "" then k = findByName(data.Name) end if k ~= nil then obj = landmarkEntities[k] - if isGlobal ~= true then DbgPrint("Removing duplicate entity", obj, data.Name or data.GlobalName or "") obj:Remove() @@ -911,10 +736,8 @@ function GM:CreateTransitionObjects() local entityTransitionData = {} ignoreKeyIndex = -1 -- ignoreKeyIndex + 1 DbgPrint("IgnoreKeyIndex: " .. ignoreKeyIndex) - for _, data in pairs(objects) do if data.Ignored == true then continue end - -- NOTE/FIXME: Observed different results on linux if util.IsInWorld(data.Pos) == false then DbgPrint("Ignoring creation of " .. data.Class .. ", position out of world: " .. tostring(data.Pos)) @@ -924,7 +747,6 @@ function GM:CreateTransitionObjects() DbgPrint("Creating: " .. data.Class, data.Name, data.GlobalName) local ent local dispatchSpawn = true - if IsValid(data.GlobalEnt) then ent = data.GlobalEnt dispatchSpawn = false @@ -942,12 +764,10 @@ function GM:CreateTransitionObjects() ent.ShouldDispatchSpawn = dispatchSpawn -- Do key values first because we might override a few things with setters. local keyIndex = 0 - for k, v in pairs(data.KeyValues) do if KEYVALUE_BLACKLIST[k] == true then continue end v = tostring(v) DbgPrint(ent, "KeyValue: ", k, v) - -- Deal with specifics. if data.Type == ENT_TYPE_DOOR and table.HasValue(DOOR_KEYVALUES, k) then ent:SetKeyValue(k, v) @@ -982,7 +802,6 @@ function GM:CreateTransitionObjects() ent:SetPos(data.Pos) ent:SetAngles(data.Ang) ent:SetVelocity(data.Vel) - if data.Mdl ~= nil then ent:SetModel(data.Mdl) DbgPrint(data.Mdl) @@ -1000,22 +819,16 @@ function GM:CreateTransitionObjects() ent:SetMoveCollide(data.MoveCollide) ent:SetMoveType(data.MoveType) ent:SetSequence(data.Sequence) - - if data.IsPassengerSeat == true then - ent:SetNWBool("IsPassengerSeat", true) - end - + if data.IsPassengerSeat == true then ent:SetNWBool("IsPassengerSeat", true) end if data.Type == ENT_TYPE_NPC then ent:SetMovementActivity(data.MovementActivity) --ent:SetMovementSequence(data.MovementSequence) ent:SetExpression(data.Expression) ent:SetNPCState(data.NPCState) - if data.ActiveWeapon ~= nil then ent:SetKeyValue("additionalequipment", data.ActiveWeapon) GAMEMODE:EntityKeyValue(ent, "additionalequipment", data.ActiveWeapon) end - elseif data.Type == ENT_TYPE_VEHICLE then if data.EnableGun == true then ent:SetKeyValue("EnableGun", "1") @@ -1023,19 +836,14 @@ function GM:CreateTransitionObjects() ent:SetKeyValue("EnableGun", "0") end - if data.VehicleScript ~= nil then - ent:SetKeyValue("VehicleScript", data.VehicleScript) - end + if data.VehicleScript ~= nil then ent:SetKeyValue("VehicleScript", data.VehicleScript) end end if data.Outputs ~= nil then ent:SetOutputsTable(table.Copy(data.Outputs)) -- Dont mess with the references on cleanups. end - if ent.SetNWVars ~= nil and data.NWVars ~= nil then - ent:SetNWVars(data.NWVars) - end - + if ent.SetNWVars ~= nil and data.NWVars ~= nil then ent:SetNWVars(data.NWVars) end if data.Type == ENT_TYPE_DOOR then ent:SetSaveValue("m_vecPosition1", data.Pos1) ent:SetSaveValue("m_vecPosition2", data.Pos2) @@ -1049,45 +857,33 @@ function GM:CreateTransitionObjects() -- Second iteration: We resolve dependencies. DbgPrint("Fixing object referencs...") - for _, ent in pairs(self.CreatedTransitionObjects) do local data = entityTransitionData[ent] if data == nil then continue end - if data.Parent then local parent = self.CreatedTransitionObjects[data.Parent] - if IsValid(parent) then ent:SetParent(parent) - -- FIX: Make sure we assign the seat to the vehicle. - if ent:GetNWBool("IsPassengerSeat", false) == true then - parent:SetNWEntity("PassengerSeat", ent) - end + if ent:GetNWBool("IsPassengerSeat", false) == true then parent:SetNWEntity("PassengerSeat", ent) end end end if data.Owner then local owner = self.CreatedTransitionObjects[data.Owner] - - if IsValid(owner) then - ent:SetOwner(owner) - end + if IsValid(owner) then ent:SetOwner(owner) end end for k, v in pairs(data.SaveTable) do if isstring(v) and v:sub(1, 8) == "CoopRef_" then local refId = tonumber(v:sub(9)) local refEnt = self.CreatedTransitionObjects[refId] - if IsValid(refEnt) and refEnt ~= ent and ent:IsNPC() == false then DbgPrint("Resolved reference for " .. tostring(ent) .. ": " .. k .. " -> " .. tostring(refEnt)) ent:SetSaveValue(k, refEnt) end else - if SAVETABLE_WHITELIST[k] == true then - ent:SetSaveValue(k, v) - end + if SAVETABLE_WHITELIST[k] == true then ent:SetSaveValue(k, v) end end end @@ -1096,27 +892,17 @@ function GM:CreateTransitionObjects() -- Third iteration: We spawn and activate. DbgPrint("Spawning objects...") - for _, ent in pairs(self.CreatedTransitionObjects) do DbgPrint("Spawning: " .. tostring(ent)) if ent.ShouldDispatchSpawn ~= true then continue end ent:Spawn() - - if ent:IsNPC() or ent:IsVehicle() then - ent:Activate() - end - + if ent:IsNPC() or ent:IsVehicle() then ent:Activate() end local data = entityTransitionData[ent] if data == nil then continue end - - if data.GlobalName ~= nil and data.GlobalName ~= "" then - ent:SetNWString("GlobalName", data.GlobalName) - end - + if data.GlobalName ~= nil and data.GlobalName ~= "" then ent:SetNWString("GlobalName", data.GlobalName) end -- Correct phys positions. for k, v in pairs(data.Phys) do local physObj = ent:GetPhysicsObjectNum(k) - if IsValid(physObj) then local physPos = ent:LocalToWorld(v[1]) local physAng = ent:LocalToWorldAngles(v[2]) @@ -1126,7 +912,5 @@ function GM:CreateTransitionObjects() end end - if ignoreKeyIndex ~= -1 then - ignoreKeyIndex = ignoreKeyIndex + 1 - end + if ignoreKeyIndex ~= -1 then ignoreKeyIndex = ignoreKeyIndex + 1 end end \ No newline at end of file From 6b13b5b5f46d3d7ec03c93176e56c7a42b332780 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Fri, 3 May 2024 00:21:04 +0300 Subject: [PATCH 2/3] Update changelog.md --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index 4796b82c..22901e15 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,7 @@ - Fixed: d2_coast_03: Blocked a exploit where player can use it to skip the final scene. - Fixed: Disallow commanding citizen NPCs that have an active schedule. - Fixed: Enemy scaling was improperly calculcated, now the math is right. +- Fixed: Players can sometimes spawn in the void if they didn't make it to the end of the map. - Changed: Added a new setting to block respawns until a checkpoint is activated, previously it relied on lambda_max_respawn_timeout being -1. 0.9.19 From 4df8e77dab2b6e8e146a7466cab3437a7d1366e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Fri, 3 May 2024 00:23:26 +0300 Subject: [PATCH 3/3] Fix formatting --- gamemode/sv_transition.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gamemode/sv_transition.lua b/gamemode/sv_transition.lua index a734eedf..40592e34 100644 --- a/gamemode/sv_transition.lua +++ b/gamemode/sv_transition.lua @@ -158,9 +158,9 @@ local TRANSITION_ENFORCED_NPC = { function GM:ShouldTransitionObject(obj, playersInTrigger) if obj:IsWorld() then return false end if obj:IsPlayer() then - if obj:IsBot() then + if obj:IsBot() then -- Ignore bots. - return false + return false end -- Players always transition. return true @@ -474,7 +474,6 @@ end function GM:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, playerTable, playersInTrigger) local objects = {} - DbgPrint("Collecting objects...") local checkVolumes = table.Count(transitionTriggers) > 0 --local inPVS = ents.FindInPVS(landmarkEnt) -- Currently crashing, we use landmark:TestPVS instead. @@ -507,6 +506,7 @@ function GM:GetTransitionList(landmarkEnt, transitionTriggers, objectTable, play -- Ignore bots. continue end + if v:Alive() == false then -- Ignore dead players. continue