Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EFL_KEEP_ON_RECREATE_ENTITIES flag causes entity duplication during game.CleanUpMap() #6105

Open
Riddle1001 opened this issue Dec 8, 2024 · 7 comments

Comments

@Riddle1001
Copy link

Details

When using game.CleanUpMap(), entities marked with EFL_KEEP_ON_RECREATE_ENTITIES via lua are being both preserved and recreated, leading to unintended entity duplication.

Steps to reproduce

  1. Create an entity and set EFL_KEEP_ON_RECREATE_ENTITIES flag on it
  2. Call game.CleanUpMap()
  3. Observe that the flagged entity has been duplicated - both the original entity is preserved and a new copy is created
@robotboy655
Copy link
Contributor

This is kind of intentional, since there's just no way to know which entities should not be re-created.

@Riddle1001
Copy link
Author

This is kind of intentional, since there's just no way to know which entities should not be re-created.

I see, this much be a very very niche use case then because I can't really think of a scenario where I'd want to duplicate (or recreate without removing the original) entities rather than just exclude certain entities. I was hoping to reduce some of the serverside lag that comes with game.CleanUpMap

@robotboy655
Copy link
Contributor

Use classname based filters, not EFL_KEEP_ON_RECREATE_ENTITIES. The flag is really meant for non map spawned entities, usually singletons.

@Riddle1001
Copy link
Author

Use classname based filters, not EFL_KEEP_ON_RECREATE_ENTITIES. The flag is really meant for non map spawned entities, usually singletons.

Well, I wanted to only remove and recreate breakable prop_phyics, and then for prop_phyics that have 0 health I wanted to just reset their position via some other code, which would (I hoped) significantly reduce the lag. Using the classname for this wouldn't work since breakable props and non breakable props (almost always) have the same classnames

I was hoping this function

function BetterCleanUpMap(filter)
    local original_flags = {}

    local all_entities = ents.GetAll()

    for _, ent in ipairs(all_entities) do
        if IsValid(ent) and (not filter or filter(ent)) then
            original_flags[ent] = ent:IsEFlagSet(EFL_KEEP_ON_RECREATE_ENTITIES)
            ent:AddEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
        end
    end

    game.CleanUpMap(false, {"env_fire", "entityflame", "_firesmoke", "info_player_start"})

    for ent, had_flag in pairs(original_flags) do
        if IsValid(ent) then
            if not had_flag then
                ent:RemoveEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
            end
        end
    end
end

Would've worked the way I expected it to BetterCleanUpMap(function(ent) ent:Health() == 0 end) -- Keep entities with 0 HP

@Zaurzo
Copy link

Zaurzo commented Dec 10, 2024

I think you can use the hammerid internal variable for those entities. Check if it's map created, and if it's filtered, store its hammer ID (you can get it via GetInternalVariable), and then make a PostCleanupMap that loops across all map created ents and checks if any of them have a filtered hammer ID and then remove the dupes

-- A better version of game.CleanUpMap that allows for entities to be put into the filter

local _filterLookup = {}

function game.BetterCleanUpMap(dontSendToClients, filter, ...)
    local entsMarked

    if istable(filter) then
        for k, ent in ipairs(filter) do
            if isentity(ent) then
                if ent:CreatedByMap() then
                    local id = ent:GetInternalVariable('hammerid')

                    if id ~= 0 then
                        _filterLookup[ent] = true
                        _filterLookup[id] = true
                    end
                end

                -- This check is in case the entity already has the flag
                -- We don't want to remove it if it's pre-set
                if not ent:IsEFlagSet(EFL_KEEP_ON_RECREATE_ENTITIES) then
                    entsMarked = entsMarked or {}

                    table.insert(entsMarked, ent)

                    ent:AddEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
                end
            end
        end
    end

    game.CleanUpMap(dontSendToClients, filter, ...)

    if not entsMarked then return end

    for k, ent in ipairs(entsMarked) do
        ent:RemoveEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
    end
end

-- Remove the map-created entity duplicates
hook.Add('PostCleanupMap', 'game.BetterCleanUpMap', function()
    for k, ent in ipairs(ents.GetAll()) do
        if not _filterLookup[ent] then
            local id = ent:GetInternalVariable('hammerid')

            if _filterLookup[id] then
                ent:Remove()
            end
        end
    end

    _filterLookup = {}
end)

@Riddle1001
Copy link
Author

I think you can use the hammerid internal variable for those entities. Check if it's map created, and if it's filtered, store its hammer ID (you can get it via GetInternalVariable), and then make a PostCleanupMap that loops across all map created ents and checks if any of them have a filtered hammer ID and then remove the dupes

-- A better version of game.CleanUpMap that allows for entities to be put into the filter

local _filterLookup = {}

function game.BetterCleanUpMap(dontSendToClients, filter, ...)
local entsMarked

if istable(filter) then
    for k, ent in ipairs(filter) do
        if isentity(ent) then
            if ent:CreatedByMap() then
                local id = ent:GetInternalVariable('hammerid')

                if id ~= 0 then
                    _filterLookup[ent] = true
                    _filterLookup[id] = true
                end
            end

            -- This check is in case the entity already has the flag
            -- We don't want to remove it if it's pre-set
            if not ent:IsEFlagSet(EFL_KEEP_ON_RECREATE_ENTITIES) then
                entsMarked = entsMarked or {}

                table.insert(entsMarked, ent)

                ent:AddEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
            end
        end
    end
end

game.CleanUpMap(dontSendToClients, filter, ...)

if not entsMarked then return end

for k, ent in ipairs(entsMarked) do
    ent:RemoveEFlags(EFL_KEEP_ON_RECREATE_ENTITIES)
end

end

-- Remove the map-created entity duplicates
hook.Add('PostCleanupMap', 'game.BetterCleanUpMap', function()
for k, ent in ipairs(ents.GetAll()) do
if not _filterLookup[ent] then
local id = ent:GetInternalVariable('hammerid')

        if _filterLookup[id] then
            ent:Remove()
        end
    end
end

_filterLookup = {}

end)

I'll try that here in a little bit, my initial worry is the collisions that come from unfrozen entities will induce more lag than the game.CleanUpMap, or, the unfrozen props may be launched into directions due to the collisions. Will update you.

@Riddle1001
Copy link
Author

I think you can use the hammerid internal variable for those entities. Check if it's map created, and if it's filtered, store its hammer ID (you can get it via GetInternalVariable), and then make a PostCleanupMap that loops across all map created ents and checks if any of them have a filtered hammer ID and then remove the dupes

Alright, so I read the code now, this still removes and recreates the entities passed into the filter. The whole point of not wanting to remove and recreate entities (specifically props in my case) is to reduce the very measurable and gameplay experience effecting serverside lag the comes with game.CleanUpMap. What I want is to do is only remove & recreate breakable props (since they have some sort of state e.g health, or could just be completely gone if they're broken), and the props that have no state/interaction, and are just props, to have their position/angle reset since there would be no need to completely remove & recreate them, and this would hopefully reduce the lag. I'm going to try my own implementation of what i'm describing, and how I hoped ``game.CleanUpMap` worked, with https://github.com/Nak2/NikNaks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants