Jump to content

Hair and undershirt color bug (fix in comments)


DevJeff

Recommended Posts

Hi,

 

I am experiencing a hair and undershirt coloring bug on my server. It seems to only affect the player whose server ID is 0 (zero). That is, from player zero's perspective, they look fine. From all other players' perspectives, player zero's undershirt and hair will flicker between a variety of colors.

Recording: https://streamable.com/sdk7mf

The steps we've taken to try and resolve the issue:

  • All component palette values to 0.
  • All component palette values to 2.
  • Components that are torso-related to palette value to 2, all else palette value 0.
  • setHairColor separately.
  • Single server-side JavaScript script with setClothes.
  • Single client-side JavaScript script with setComponentVariation.
  • Single server-side C# script with NAPI.Player.SetPlayerClothes.
  • Server-side script to set the player's clothing, client-side script to fetch the configuration and apply it in "entityStreamIn" event.
  • Set undershirt to 15. (Only the hair switches color when this is done.)
  • Entity data. (+ addDataHandler)
  • Host server on different Windows 10 machine.
  • Host server on debian12 VPS.

Clean GTA5 installations, no mods, clean server-files. The server is hosted locally. Second game instance is running on a different computer connected through LAN.

 

Single server-side JavaScript file:

let playerClothes = new Map();

mp.events.add('playerJoin', (player) => {
    const randomHair = Math.floor(Math.random() * 20) + 5;
    const clothing = [
        { component: 0, drawable: 0, texture: 0, palette: 0 },
        { component: 1, drawable: 0, texture: 0, palette: 0 },
        { component: 2, drawable: randomHair, texture: 0, palette: 0 },
        { component: 3, drawable: 0, texture: 0, palette: 2 },
        { component: 4, drawable: 0, texture: 0, palette: 0 },
        { component: 5, drawable: 0, texture: 0, palette: 0 },
        { component: 6, drawable: 0, texture: 0, palette: 0 },
        { component: 7, drawable: 0, texture: 0, palette: 0 },
        { component: 8, drawable: 0, texture: 0, palette: 2 },
        { component: 9, drawable: 0, texture: 0, palette: 2 },
        { component: 10, drawable: 0, texture: 0, palette: 2 },
        { component: 11, drawable: 16, texture: 0, palette: 2 }
    ];

    playerClothes.set(player.id, clothing);
});

mp.events.add('playerReady', (player) => {
    const clothing = playerClothes.get(player.id);

    clothing.forEach((slot) => {
        player.setClothes(slot.component, slot.drawable, slot.texture, slot.palette);
    });
});

 

Single client-side JavaScript script:

mp.events.add('playerReady', () => {
    const player = mp.players.local;

    const clothing = [
        { component: 0,  drawable: 0, texture: 0, palette: 2 },
        { component: 1,  drawable: 0, texture: 0, palette: 2 },
        { component: 2,  drawable: 5, texture: 0, palette: 2 },
        { component: 3,  drawable: 0, texture: 0, palette: 2 },
        { component: 4,  drawable: 0, texture: 0, palette: 2 },
        { component: 5,  drawable: 0, texture: 0, palette: 2 },
        { component: 6,  drawable: 0, texture: 0, palette: 2 },
        { component: 7,  drawable: 0, texture: 0, palette: 2 },
        { component: 8,  drawable: 0, texture: 0, palette: 2 },
        { component: 9,  drawable: 0, texture: 0, palette: 2 },
        { component: 10, drawable: 0, texture: 0, palette: 2 },
        { component: 11, drawable: 10, texture: 0, palette: 2 }
    ];

    for (let i = 0; i < clothing.length; i++) {
        const slot = clothing[i];

        player.setComponentVariation(slot.component, slot.drawable, slot.texture, slot.palette);
    }
});

mp.events.add('entityStreamIn', (entity) => {
    if (entity.type !== 'player') {
        return;
    }

    const clothing = [
        { component: 0,  drawable: 0, texture: 0, palette: 2 },
        { component: 1,  drawable: 0, texture: 0, palette: 2 },
        { component: 2,  drawable: 17, texture: 0, palette: 2 },
        { component: 3,  drawable: 0, texture: 0, palette: 2 },
        { component: 4,  drawable: 0, texture: 0, palette: 2 },
        { component: 5,  drawable: 0, texture: 0, palette: 2 },
        { component: 6,  drawable: 0, texture: 0, palette: 2 },
        { component: 7,  drawable: 0, texture: 0, palette: 2 },
        { component: 8,  drawable: 0, texture: 0, palette: 2 },
        { component: 9,  drawable: 0, texture: 0, palette: 2 },
        { component: 10, drawable: 0, texture: 0, palette: 2 },
        { component: 11, drawable: 33, texture: 0, palette: 2 }
    ];

    for (let i = 0; i < clothing.length; i++) {
        const slot = clothing[i];

        entity.setComponentVariation(slot.component, slot.drawable, slot.texture, slot.palette);
    }
});


Server-side script to store clothing data, client-side script to fetch in "entityStreamIn" event:

/*
 * SERVER
 *
 */
let playerClothes = new Map();

mp.events.add('clothes::requestForPlayer', (player, id) => {
    const clothing = playerClothes.get(id);
    player.call('clothes::forPlayer', [id, JSON.stringify(clothing)]);
});

mp.events.add('playerJoin', (player) => {
    const randomHair = Math.floor(Math.random() * 20) + 5;
    const clothing = [
        { component: 0, drawable: 0, texture: 0, palette: 0 },
        { component: 1, drawable: 0, texture: 0, palette: 0 },
        { component: 2, drawable: randomHair, texture: 0, palette: 0 },
        { component: 3, drawable: 0, texture: 0, palette: 2 },
        { component: 4, drawable: 0, texture: 0, palette: 0 },
        { component: 5, drawable: 0, texture: 0, palette: 0 },
        { component: 6, drawable: 0, texture: 0, palette: 0 },
        { component: 7, drawable: 0, texture: 0, palette: 0 },
        { component: 8, drawable: 0, texture: 0, palette: 2 },
        { component: 9, drawable: 0, texture: 0, palette: 2 },
        { component: 10, drawable: 0, texture: 0, palette: 2 },
        { component: 11, drawable: 16, texture: 0, palette: 2 }
    ];

    playerClothes.set(player.id, clothing);
});

/*
 * CLIENT
 *
 */
mp.events.add('playerReady', () => {
    mp.events.callRemote("clothes::requestForPlayer", mp.players.local.remoteId);
});

mp.events.add('entityStreamIn', (entity) => {
    if (entity.type !== 'player') {
        return;
    }

    mp.events.callRemote("clothes::requestForPlayer", entity.remoteId);
});

mp.events.add('clothes::forPlayer', (id, clothesJSON) => {
    const forPlayer = mp.players.atRemoteId(id);
    const clothing = JSON.parse(clothesJSON);
    for (let i = 0; i < clothing.length; i++) {
        const slot = clothing[i];

        forPlayer.setComponentVariation(slot.component, slot.drawable, slot.texture, slot.palette);
    }
});


Single server-side C# script:


using System;
using System.Collections.Generic;
using System.Linq;
using GTANetworkAPI;

namespace ClothingTest
{
    public class Class1 : Script
    {
        public Class1()
        {
        }

        [ServerEvent(Event.PlayerConnected)]
        public void OnPlayerConnect(Player player)
        {
            Dictionary<int, int[]> clothing = new Dictionary<int, int[]>();
            clothing.Add(0, new int[2] { 0, 0 });
            clothing.Add(1, new int[2] { 0, 0 });
            clothing.Add(2, new int[2] { 17, 0 });
            clothing.Add(3, new int[2] { 0, 0 });
            clothing.Add(4, new int[2] { 0, 0 });
            clothing.Add(5, new int[2] { 0, 0 });
            clothing.Add(6, new int[2] { 0, 0 });
            clothing.Add(7, new int[2] { 0, 0 });
            clothing.Add(8, new int[2] { 0, 0 });
            clothing.Add(9, new int[2] { 0, 0 });
            clothing.Add(10, new int[2] { 0, 0 });
            clothing.Add(11, new int[2] { 33, 0 });

            for (int i = 0; i < 11; i++)
            {
                var value = clothing.ElementAt(i).Value;
                NAPI.Player.SetPlayerClothes(player, i, value[0], value[1]);
            }
        }
    }
}


I am out of ideas. Thank you for your time.

Edited by DevJeff
  • Like 1
Link to comment
Share on other sites

Considered Fixed

Thanks to the folks on Discord - louzzy, vienna, DaNeo, and Kopra - we found a solution to the problem.

The fix: https://wiki.rage.mp/index.php?title=Player::setCustomization

We used the code snippet provided on the wiki for testing and the issue hasn't occurred in the dozen of attempts.

 

Edit: When we took the setCustomization code out, the issue came back near immediately, so we can confidently say it is fixed.

Edited by DevJeff
  • Like 1
Link to comment
Share on other sites

  • DevJeff changed the title to Hair and undershirt color bug (fix in comments)

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...