little infinite scrolling system, g_hudelem parent/child bleed fix *UPDATED*

Xeirh

Veteran
Messages
8
Reaction score
4
Points
793
C:
/*
*
* (unsigned)(hud - g_hudelems) < (unsigned)((18 * (31 + 31) + 192))
*
* this is the vm limit, let us break this down some. hopefully someone
* will find some of this useful, most of this will apply to other titles.
*
* 18: players
* 31: archive pool
* 31: non-archive pool
* 192: additional asset - only god knows
*
* by default ... if undefined, hudelems will default and use the archive pool.
* the archive pool is the most consistent in titles, nothing hardly messes with
* this pool. by default one hudelem from both pools is always reserved so you can
* hang up having the full pool. further, another one is gone as the cross hair is also
* one less hudelem in that pool. so the free archive pool is twenty-nine and always will
* be unless you mess with the pool or playing on zm_prison zclassic as another archived
* hudelem will be introduced know as self.client_hint so twenty-eight, so lets talk auto
* archive systems to switch between the pools.
*
*/

hudelem_count()
{
    if( !isdefined( self.hudelem_count ) || self.hudelem_count < 0 )
        self.hudelem_count = 0;

    // easy right? but I want all the hudelems
    return self.hudelem_count < 29;
}

hudelem_count()
{
    if( !isdefined( self.hudelem_count ) || self.hudelem_count < 0 )
        self.hudelem_count = 0;

    pool = 30;

    // checking the start location: if prison, it's classic, cellblock is grief
    if( getdvar( "ui_zm_mapstartlocation" ) == "prison" )
        pool = 29;

    return self.hudelem_count < pool;
}

/*
*
* you can also get more complex by reassigning archive states based on self.client_hint
* being defined but I personally don't think this is worth it.
*
* zm_transit:
* - archived 29, non-archived 23
* zm_nuked:
* - archived 29, non-archived 23
* zm_prison:
* - archived 28, non-archived 23, archived 29 if not classic
* zm_buried:
* - archived 29, non-archived 23
* zm_highrise: 
* - archived 29, non-archived 23
* zm_tomb:
* - archived 29, non-archived 23
*
*/

setpoint( point, x, y )
{
    switch( point )
    {
        case "top_right":
            self.alignx = "right";
            self.aligny = "top";
            break;

        default:
            self.alignx = "left";
            self.aligny = "top";
            break;
    }

    self.horzalign = "user_center";
    self.vertalign = "user_top";
    self.x = x;
    self.y = y;
}

setclient_text( text, font, fontscale, point, x, y, color, sort, alpha )
{
    element = newclienthudelem( self );
    element.archived = self hudelem_count();
    element.font = font;
    element.fontscale = fontscale;
    element.width = 0;
    element.height = int( level.fontheight * fontscale );
    element.color = color;
    element.sort = sort;
    element.alpha = alpha;
    element.hidewheninmenu = true;
    element.hidewheninscope = true;

    element settext( text );
    element setpoint( point, x, y );

    self.hudelem_count++;

    return element;
}

setclient_icon( shader, width, height, point, x, y, color, sort, alpha )
{
    element = newclienthudelem( self );
    element.archived = self hudelem_count();
    element.width = width;
    element.height = height;
    element.color = color;
    element.sort = sort;
    element.alpha = alpha;
    element.hidewheninmenu = true;
    element.hidewheninscope = true;

    element setshader( shader, width, height );
    element setpoint( point, x, y );

    self.hudelem_count++;

    return element;
}
destroyelement( element )
{
    element destroy();
    self.hudelem_count--;
}
/*
*
* keep in mind: you do not need a fancy archive system, 28 elements is more
* than enough for anyone on earth. staying inside the archive pool also means
* full stealth.
*
*/
C:
index = self.cursor;

if( self attackbuttonpressed() != && self adsbuttonpressed() )
{
    delta = 1;

    if( self.structure.size > 1 )
    {
        if( self adsbuttonpressed() )
            delta = -1;

        self.cursor = ( index + self.structure.size + delta ) % self.structure.size;
    }

    wait 0.08;
}

/*
*
* wrapping with modulo:
*
* 0 % 5 = 0
* 1 % 5 = 1
* 2 % 5 = 2
* 3 % 5 = 3
* 4 % 5 = 4
* 5 % 5 = 0
* 6 % 5 = 1
* 7 % 5 = 2
* 8 % 5 = 3
* 9 % 5 = 4
*
* Keep in mind that this is slower than using statements, but the performance
* impact is negligible and nothing to worry about. modulo can also be somewhat
* fragile, but this usage should be perfectly safe.
*
*/
C:
/*
*
* was first fixed in vanguard, so any game below this title. when creating
* a element using parent and children via setparent will leak variables. this
* happens because the children array doesn't have a destructor, so the array
* will infinitely grow until you hit to 32 bit memory limit.
*
* another eazy fix is to just not contribute to parent and children, it's
* not like anyone used them for the correct reason anyways, this will save
* a lot of variables and you won't contribute to the leak.
*
* keep in mind that most of the _hud_utils depend on getparent(), using
* any of the functions will crash the game. check out hudelem allspark for
* my hud utils.
*
* self.parent = removechild( self ); should also work.
*
*/
addchild( element )
{
    element.index = self.children.size;
    self.children[ self.children.size ] = element;

    self remove_destroyed_children();
}

removedestroyedchildren()
{
    if( isdefined( self.childchecktime ) && self.childchecktime == gettime() )
        return;

    self.childchecktime = gettime();
    tempchildren = [];

    foreach( i, element in self.children )
    {
        if( !isdefined( element ) )
            continue;

        element.index = tempchildren.size;
        tempchildren[ tempchildren.size ] = element;
    }

    self.children = tempchildren;
}

// here is the utils I personally use, these have reduced variable usage.
setpoint( point, x, y )
{
    switch( point )
    {
        case "top_right":
            self.alignx = "right";
            self.aligny = "top";
            break;

        default:
            self.alignx = "left";
            self.aligny = "top";
            break;
    }

    self.horzalign = "user_center";
    self.vertalign = "user_top";
    self.x = x;
    self.y = y;
}

setclient_text( text, font, fontscale, point, x, y, color, sort, alpha )
{
    element = newclienthudelem( self );
    element.font = font;
    element.fontscale = fontscale;
    element.width = 0;
    element.height = int( level.fontheight * fontscale );
    element.color = color;
    element.sort = sort;
    element.alpha = alpha;
    element.hidewheninmenu = true;
    element.hidewheninscope = true;

    element settext( text );
    element setpoint( point, x, y );

    return element;
}

setclient_icon( shader, width, height, point, x, y, color, sort, alpha )
{
    element = newclienthudelem( self );
    element.width = width;
    element.height = height;
    element.color = color;
    element.sort = sort;
    element.alpha = alpha;
    element.hidewheninmenu = true;
    element.hidewheninscope = true;

    element setshader( shader, width, height );
    element setpoint( point, x, y );

    return element;
}
 

Attachments

  • Polly-prissy-pants.png
    Polly-prissy-pants.png
    228.9 KB · Views: 24
Last edited:

Xeirh

Veteran
Messages
8
Reaction score
4
Points
793
this is something that humanity probably doesn't deserve, but this would work on all titles, if conditional operators not supported just use statements with conditions and call it a day. also keep in mind that the archive system is tested for black ops II
 

JayCoder

Veteran
Messages
385
Reaction score
151
Points
903
C++:
should_archive()
{
    if( !isdefined( self.archive_result ) || self.archive_result < 0 )
        self.archive_result = 0;

    return self.archive_result < 31;
}

destroy_element( element )
{
    element destroy();
    self.archive_result--;
}

/*
*
* example archive: element.archived = self should_archive();
*
* both pools have one reserved so you can never get the full sixty-two, we
* eat at the archive pool first as it's never messed with so it's very consistent
*
* zombies archive limit: 30 -> after black screen passed
* multiplayer archive limit: 24
*
* ( unsigned )( hud < g_hudelems ) < ( unsigned )( ( 18 * ( 31 + 31 ) + 192 ) )
*
* 18 players
* 31 archived
* 31 non archived
* 192 additional assets -> 1308 being the engine limit
*
* example destroy: self destroy_element( self.description );
*
* you would use this custom destroy as a replacement for destroy for your elements, as
* it is needed for the archive system to work correctly
*
*/

C++:
cursor = self get_cursor();

if( self attackbuttonpressed() && !self adsbuttonpressed() || !self attackbuttonpressed() && self adsbuttonpressed() )
{
    if( self.structure.size > 1 )
    {
        course = self attackbuttonpressed() ? 1 : -1;
        factor = ( ( cursor + course ) + self.structure.size ) % self.structure.size;

        self set_cursor( factor );
        // you would call an update here and yes this really is the infinite scrolling system
    }

    wait( 0.08 );
}

/*
*
* wrapping with modulo:
*
* 0 % 5 = 0
* 1 % 5 = 1
* 2 % 5 = 2
* 3 % 5 = 3
* 4 % 5 = 4
* 5 % 5 = 0
* 6 % 5 = 1
* 7 % 5 = 2
* 8 % 5 = 3
* 9 % 5 = 4
*
* modulo scary -> keep in mind this is slower than statements, but
* it's nothing to be scared of and the performance isn't even bad
* also, modulo can be pretty fragile, but you shouldn't break this
*
*/

// hidden lego ninjago technique
center = int( self.option_limit / 2 );
offset = self.structure.size > self.option_limit ? clamp( cursor - center, 0, self.structure.size - self.option_limit ) : 0;

for( x = 0; x < <size>; x++ )
{
    index = x + offset;

    self.xeirh[ "string_" + index ] = self create_text( <params> );
}

C++:
/*
*
* was first fixed in vanguard, meaning every title below has this
* leak. Pretty much everytime an element is created by the game or
* client, anything using the setparent system would leak variables.
* this means anything and everything hud element would leak that used
* setparent. infinity loader has a hook ability to fix it out right but
* if you eat spam and roman noodles every night, you don't have infinity loader,
* your best bet would just be cutting out the use of parents and children or using
* a custom setparent, the game would still leak but you won't contribute. this alone
* will greatly improve game longevity. keep in mind that most of the _hud_utils
* depend on getparent();, meaning using _hud_utils without parent will crash
*
*/

add_child( element )
{
    element.index = self.children.size;
    self.children[ self.children.size ] = element;

    self remove_undefined_children();
}

remove_undefined_children()
{
    if( isdefined( self.child_check_time ) && self.child_check_time == gettime() )
        return;

    self.child_check_time = gettime();
    temporary = [];

    foreach( x, element in self.children )
    {
        if( !isdefined( element ) )
            continue;

        element.index = temporary.size;
        temporary[ temporary.size ] = element;

    }


    self.children = temporary;
}
why can't you just do an array like normal people, make a function called destroyall(element.array); the **** is this? it seems more complicated
 

Xeirh

Veteran
Messages
8
Reaction score
4
Points
793
why can't you just do an array like normal people, make a function called destroyall(element.array); the **** is this? it seems more complicated
nothing complicated about that, arrays use twice as much memory, you have to undefine parent also to restore the memory. destroy automatically undefines the element, array = 1 parent, contents = 1 child, destroy = +1 child, but now you have to manually undefine the parent to restore the memory. not using arrays is a lot cleaner and faster and they have to get indexed every time.

C++:
destroy_all( array )
{
    // no reason for the array to be undefined so skip the statement

    foreach( x, key in getarraykeys( array ) )
    {
        if( !isdefined( array[ key ] ) )
            continue;

        self destroy_element( array[ key ] );
    }

    array = undefined; // memory restored, this might runtime array ref depending on the compiler
}

and if you're worried about a handful of destroy_elements, just use a non-localized array and combine or add to and for loop destroy.
 
Last edited:

JayCoder

Veteran
Messages
385
Reaction score
151
Points
903
nothing complicated about that, arrays use twice as much memory, you have to undefine parent also to restore the memory. destroy automatically undefines the element, array = 1 parent, contents = 1 child, destroy = +1 child, but now you have to manually undefine the parent to restore the memory. not using arrays is a lot cleaner and faster and they have to get indexed every time.

C++:
destroy_all( array )
{
    // no reason for the array to be undefined so skip the statement

    foreach( x, key in getarraykeys( array ) )
    {
        if( !isdefined( array[ key ] ) )
            continue;

        self destroy_element( array[ key ] );
    }

    array = undefined; // memory restored, this might runtime array ref depending on the compiler
}

and if you're worried about a handful of destroy_elements, just use a non-localized array and combine or add to and for loop destroy.
I do the same thing pretty much a crossed all cods. cod4,cod5,mw2,bo2,bo3
 

Xeirh

Veteran
Messages
8
Reaction score
4
Points
793
I do the same thing pretty much a crossed all cods. cod4,cod5,mw2,bo2,bo3
yeah this is currently what I'm using, came to the conclusion that passing arrays via params created a temporary copy that causes a leak so a direct ref or an additional helper function will fix this, this is leak free, but I use same frame reconstruction, so I destroy and create within frames
C-like:
destroy_all()
{
    if( !isdefined( self.xeirh ) )
        return;

    foreach( x, parent in self.xeirh )
    {
        if( !isdefined( parent ) )
            continue;

        if( isarray( parent ) )
        {
            foreach( y, child in parent )
            {
                if( !isdefined( child ) )
                    continue;

                self destroy_element( child );
            }

            parent = undefined; // restore child array memory
        }
        else
            self destroy_element( parent );
    }

    self.xeirh = undefined; // restore parent array memory
    self.structure = undefined;
}
 

JayCoder

Veteran
Messages
385
Reaction score
151
Points
903
yeah this is currently what I'm using, came to the conclusion that passing arrays via params created a temporary copy that causes a leak so a direct ref or an additional helper function will fix this, this is leak free, but I use same frame reconstruction, so I destroy and create within frames
C-like:
destroy_all()
{
    if( !isdefined( self.xeirh ) )
        return;

    foreach( x, parent in self.xeirh )
    {
        if( !isdefined( parent ) )
            continue;

        if( isarray( parent ) )
        {
            foreach( y, child in parent )
            {
                if( !isdefined( child ) )
                    continue;

                self destroy_element( child );
            }

            parent = undefined; // restore child array memory
        }
        else
            self destroy_element( parent );
    }

    self.xeirh = undefined; // restore parent array memory
    self.structure = undefined;
}
you know what would be ****ed if we all pretended to be triple A, and Copyright a concepted idea, like bro I know you improved it but your going to need to pay me to use that.
 

cooljay

Veteran
Messages
7
Reaction score
9
Points
778
why can't you just do an array like normal people, make a function called destroyall(element.array); the **** is this? it seems more complicated
fun fact, that alone will not decrement the passed array's .size field nor undefine it as arrays are "pass by value" aka the function only receives a copy, not a pointer to the original
 

JayCoder

Veteran
Messages
385
Reaction score
151
Points
903
fun fact, that alone will not decrement the passed array's .size field nor undefine it as arrays are "pass by value" aka the function only receives a copy, not a pointer to the original
a live reference would of been better maybe... , also to be fair that is more complicated and I just don't like it, I like to be able to just use 1 function to destroy an array of elements..
 
Top