samp-incognito / samp-streamer-plugin

Streamer Plugin for SA-MP (San Andreas Multiplayer)
Apache License 2.0
232 stars 92 forks source link

Actor animations preloading #244

Open IstuntmanI opened 6 years ago

IstuntmanI commented 6 years ago

As you may know, applying an animation for the first time isn't working, it has to be preloaded. It works simply by applying it twice for the player, even if another player gets streamed in afterwards (I just tested this, my logic tells me that this is weird, looks like it isn't actually a client-sided problem. If it was it wouldn't show up for the player which didn't have you streamed in when you preloaded it, but it would show up only for you).

My suggestion is to fix this inside the streamer plugin, just like it does with few other SA-MP bugs (I can only remember about the checkpoints size bug right now, which was easy to fix). Not sure if it regularly works simply by applying it twice to the actor, even if no player has the actor streamed-in, but Streamer Plugin is constantly creating and deleting the same actor when it gets streamed in to a player or streamed out for every player, so it would need constant reapplying twice.

Snippets, for C++, for regular actors:

int FIXES_gsActorAnimTimer[ MAX_ACTORS ] = { 0 };

std::vector< std::string > FIXES_gscAnimLib =
{
    "AIRPORT",      "ATTRACTORS",   "BAR",          "BASEBALL",     "BD_FIRE",
    "BEACH",        "BENCHPRESS",   "BF_INJECTION", "BIKE_DBZ",     "BIKED",
    "BIKEH",        "BIKELEAP",     "BIKES",        "BIKEV",        "BLOWJOBZ",
    "BMX",          "BOMBER",       "BOX",          "BSKTBALL",     "BUDDY",
    "BUS",          "CAMERA",       "CAR",          "CAR_CHAT",     "CARRY",
    "CASINO",       "CHAINSAW",     "CHOPPA",       "CLOTHES",      "COACH",
    "COLT45",       "COP_AMBIENT",  "COP_DVBYZ",    "CRACK",        "CRIB",
    "DAM_JUMP",     "DANCING",      "DEALER",       "DILDO",        "DODGE",
    "DOZER",        "DRIVEBYS",     "FAT",          "FIGHT_B",      "FIGHT_C",
    "FIGHT_D",      "FIGHT_E",      "FINALE",       "FINALE2",      "FLAME",
    "FLOWERS",      "FOOD",         "FREEWEIGHTS",  "GANGS",        "GFUNK",
    "GHANDS",       "GHETTO_DB",    "GOGGLES",      "GRAFFITI",     "GRAVEYARD",
    "GRENADE",      "GYMNASIUM",    "HAIRCUTS",     "HEIST9",       "INT_HOUSE",
    "INT_OFFICE",   "INT_SHOP",     "JST_BUISNESS", "KART",         "KISSING",
    "KNIFE",        "LAPDAN1",      "LAPDAN2",      "LAPDAN3",      "LOWRIDER",
    "MD_CHASE",     "MD_END",       "MEDIC",        "MISC",         "MTB",
    "MUSCULAR",     "NEVADA",       "ON_LOOKERS",   "OTB",          "PARACHUTE",
    "PARK",         "PAULNMAC",     "PED",          "PLAYER_DVBYS", "PLAYIDLES",
    "POLICE",       "POOL",         "POOR",         "PYTHON",       "QUAD",
    "QUAD_DBZ",     "RAPPING",      "RIFLE",        "RIOT",         "ROB_BANK",
    "ROCKET",       "RUNNINGMAN",   "RUSTLER",      "RYDER",        "SAMP",
    "SCRATCHING",   "SEX",          "SHAMAL",       "SHOP",         "SHOTGUN",
    "SILENCED",     "SKATE",        "SMOKING",      "SNIPER",       "SNM",
    "SPRAYCAN",     "STRIP",        "SUNBATHE",     "SWAT",         "SWEET",
    "SWIM",         "SWORD",        "TANK",         "TATTOOS",      "TEC",
    "TRAIN",        "TRUCK",        "UZI",          "VAN",          "VENDING",
    "VORTEX",       "WAYFARER",     "WEAPONS",      "WOP",          "WUZI"
};

std::bitset< 135 > FIXES_gsPlayerAnimLibs[ MAX_PLAYERS ]; // 135 is the size of the previous std::vector

int GetAnimLibIndex( const std::string animlib ) // could be made faster I guess ?
{
    for( unsigned int i = 0, j = FIXES_gscAnimLib.size( ); i < j; i ++ )
    {
        if( boost::iequals( FIXES_gscAnimLib[ i ], animlib ) )
            return i;
    }

    return -1;
}

void Timer_ApplyActorAnimation( int timerid, std::tuple< int, int, std::string, float, bool, bool, bool, bool, int > params )
{
    int actorid = std::get< 0 >( params );

    sampgdk::ApplyActorAnimation( actorid, FIXES_gscAnimLib[ std::get< 1 >( params ) ].c_str( ), std::get< 2 >( params ).c_str( ), std::get< 3 >( params ), std::get< 4 >( params ), std::get< 5 >( params ), std::get< 6 >( params ), std::get< 7 >( params ), std::get< 8 >( params ) );
    FIXES_gsActorAnimTimer[ actorid ] = 0;
}

void ApplyActorAnimationDelay( const char animname[ ], int actorid, int idx, float fDelta, bool loop, bool lockx, bool locky, bool freeze, int time )
{
    if( FIXES_gsActorAnimTimer[ actorid ] )
    {
        Timers::KillTimer( FIXES_gsActorAnimTimer[ actorid ] );
        FIXES_gsActorAnimTimer[ actorid ] = 0;
    }

    if( FIXES_gsActorAnimLibs[ actorid ][ idx ] == false )
    {
        FIXES_gsActorAnimLibs[ actorid ].set( idx );
        FIXES_gsActorAnimTimer[ actorid ] = Timers::SetTimerEx( Timer_ApplyActorAnimation, 350, false, { actorid, idx, animname, fDelta, loop, lockx, locky, freeze, time } );
    }
}

bool ApplyActorAnimation( int actorid, const char animlib[ ], const char animname[ ], float fDelta, bool loop, bool lockx, bool locky, bool freeze, int time ) // First Hook
{
    int idx = GetAnimLibIndex( animlib );

    if( idx != -1 )
    {
        ApplyActorAnimationDelay( animname, actorid, idx, fDelta, loop, lockx, locky, freeze, time );
        return sampgdk::ApplyActorAnimation( actorid, animlib, animname, fDelta, loop, lockx, locky, freeze, time );
    }

    return 0;
}

Instead of the regular sampgdk::ApplyActorAnimation after the actor is created, it should call the function from above I guess. Also, when the actor gets destroyed the preloaded animations bitset should be .reset( ). (or, to save memory, maybe it should reapply an animation twice everytime it gets changed ?)

Not sure if it actually needs a delay (timer) between preloading and actually applying it, but this is how fixes.inc does it (it probably won't be preloaded if there's no delay, due to how data is sent, but this may be different for actors). Maybe @ziggi or @Y-Less know more about animations on actors, especially in our case ?


A little discussion about this: image

Southclaws commented 6 years ago

I don't see how this makes sense in the Streamer plugin, it would fit better in a new plugin.

Y-Less commented 6 years ago

It is already fixed in at least fixes.inc, maybe YSF as well.

IstuntmanI commented 6 years ago

Yeah, it is fixed in fixes.inc for regular scripts, but Streamer Plugin 's Dynamic Actors still have the preloading bug which should be fixed in that way too.

karimcambridge commented 6 years ago

The server scripter should decide to fix this.

You putting this in the plugin would have to tell us so we can remove the duplicate preloading in our scripts. It's already in the widely used fixes.inc.

IstuntmanI commented 6 years ago

I'm not sure if I'm missing something or you are missing something. Actors are created everytime when they get streamed for a player, which means that the animations aren't preloaded for them at that moment, so we would need some kind of OnActorCreated function to apply the animation to them correctly, as the first internal ApplyActorAnimation function call would just preload the animation. Maybe I'm overthinking this and doing something wrong. It's weird that actors need animations preloading. When I tested it with a friend the whole preloading animation thing gave me logic issues, haha (as I described in the first post).

IstuntmanI commented 6 years ago

http://wiki.sa-mp.com/wiki/ApplyActorAnimation: image

Oh, weird, afaik fixes.inc isn't doing this.

Y-Less commented 6 years ago

Oh. Well it's pretty easy to fix, since fixes already does this for normal animations.

IstuntmanI commented 6 years ago

If the fix is indeed the one listed on wiki, then it will be way easier to fix it inside this plugin (well, mostly because it will use less memory when we have lots of actors doing animations).

karimcambridge commented 6 years ago

https://github.com/Open-GTO/sa-mp-fixes/blob/master/fixes.inc#L1011

AFAIK it does ^^^

The animation has to be loaded for the SA-MP player, not the SA-MP actor.

billylvn commented 3 years ago

anjay stuntman