[Client Side] Sending data + DecUI

Started by umar4911, May 25, 2020, 07:57 AM

Previous topic - Next topic

umar4911

I wrote this guide for a friend who wanted to know how to send data from server to client and how to use DecUI. I thought why not post here too.

The function is taken from here https://forum.vc-mp.org/?topic=4484 Credits: Seby
DecUI is taken from here https://forum.vc-mp.org/?topic=6775 Credits: NewK


P.S: Sorry in advance if it's a little messed up.

Comments are added in each code for more explanation.





okay first example. We are sending kills from the server which should show to the player using a label


In order to record the identifier bytes, we can use enumerations (enum as short) like

[noae][noae][noae][noae]enum ByteInfo {
Kills = 0x02
}
[/noae][/noae][/noae][/noae]

Let's set that as soon as player spawns, it is shown.

[noae][noae][noae][noae][noae]

enum ByteInfo {
Kills = 0x02
};


function onPlayerSpawn(player)
{
// The ByteInfo.Kills will record it's identifier
SendDataToClient(player, ByteInfo.Kills, status[player.ID].kills, status[player.ID].Deaths)

// We send 3 values to the client, in the order, byte, int, int
}

// here is the special func
function SendDataToClient( player, ... )
{
if( vargv[0] )
{
local   byte = vargv[0], len = vargv.len();
Stream.StartWrite();
Stream.WriteByte( byte );
if( 1 < len )
{
for( local i = 1; i < len; i++ )
{
switch( typeof( vargv[i] ) )
{
case "integer": Stream.WriteInt( vargv[i] ); break;
case "string": Stream.WriteString( vargv[i] ); break;
case "float": Stream.WriteFloat( vargv[i] ); break;
}
}
}
if( player == null ) Stream.SendStream( null );
else if( typeof( player ) == "instance" ) Stream.SendStream( player );
else return;
}
}
[/noae][/noae][/noae][/noae][/noae]



Now we have sent data from the server. Lets get those values in client side
[noae][noae][noae][noae][noae]
enum ByteInfo {
Kills = 0x02
};


function Server::ServerData(Data)
{
// the first parameter we sent for all the data packets will be a byte so lets get it
local byte = Data.ReadByte();
// the local will store it's value

//since we used ByteInfo.Kills for the kills label
switch (byte)
{
case ByteInfo.Kills:
// the next values we sent where kills and deaths in the order and both were int vals
local kills = Data.ReadInt(),
deaths = Data.ReadInt();
// values stored

// I'll use a func to make the labels
CreateKillsGUI(kills, deaths);
break;
default: break;
}
}

function CreateKillsGUI(kills, deaths) {
// you know the drill how to create GUILabels

// In order to help you, I'll use DecUI to Create a Label
local LabelKills = UI.Label({
    id = "KillsLabel" 
    Text = "Kills: " + kills
FontSize = 16
TextColour = Colour(255, 255, 255)
// It helps you do some cool alignments
align = "hud_bottom"
FontFlags = (GUI_FFLAG_BOLD)

// I want the label to move a little more down
move = {down = "10%"}
});


local LabelDeaths = UI.Label({
    id = "LabelDeaths" 
    Text = "Deaths: " + deaths
FontSize = 16
TextColour = Colour(255, 255, 255)
align = "hud_bottom"
FontFlags = (GUI_FFLAG_BOLD)

// deaths should be a bit more down
move = {down = "13%"}
});
}
[/noae][/noae][/noae][/noae][/noae]

Since we created the labels in DecUI, we will be needing the update the labels too.

Let's make like, the player uses the same byte to send the data and the same function to update the labels. We will edit our function

[noae][noae][noae][noae][noae]function CreateKillsGUI(kills, deaths) {
    // We used ID `KillsLabel` to create Label for kills

    if(UI.Label("KillsLabel") != null)
    {
        // if the value is not null, means a label with that ID exists
        UI.Label("KillsLabel").Text = "Kills: " + kills
    }
    else
    {
        // if the condition is null, means the label doesn't exist. Lets create it
        local LabelKills = UI.Label({
            id = "KillsLabel" 
            Text = "Kills: " + kills
            FontSize = 16
            TextColour = Colour(255, 255, 255)
            // It helps you do some cool alignments
            align = "hud_bottom"
            FontFlags = (GUI_FFLAG_BOLD)

            // I want the label to move a little more down
            move = {down = "10%"}
        });
    }
    // similar for the deaths

    if(UI.Label("LabelDeaths") != null)
    {
        UI.Label("LabelDeaths").Text = "Deaths: " + deaths
    }
    else
    {
        local LabelDeaths = UI.Label({
            id = "LabelDeaths" 
            Text = "Deaths: " + deaths
            FontSize = 16
            TextColour = Colour(255, 255, 255)
            align = "hud_bottom"
            FontFlags = (GUI_FFLAG_BOLD)

            // deaths should be a bit more down
            move = {down = "13%"}
        });
    }
}
[/noae][/noae][/noae][/noae][/noae]
Okay let's do some more cool things with DecUI. Let's create a progress bar for health instead of the text


We will be using event onPlayerHeathChange. Also we need to add another identifier in the enum.
Server Side
[noae][noae][noae][noae][noae]enum ByteInfo {
Kills = 0x02
HpBar = 0x03
};

function onPlayerHeathChange(player, oldHp, newHp)
{
//since we need to send the new health only and Health is a float value, we'll convert to integer
SendDataToClient(player, ByteInfo.HpBar, newHp);
}
[/noae][/noae][/noae][/noae][/noae]


Client-Side has a function `Script::ScriptLoad()` which runs when all files are downloaded and loaded in the server. Acts like onPlayerJoin

Client Side
[noae][noae][noae][noae][noae]// lets get the func scriptload
enum ByteInfo {
Kills = 0x02
HpBar = 0x03
};

function Script::ScriptLoad() {

    // first of all, we need to remove the player's health from hud
    Hud.RemoveFlags( HUD_FLAG_HEALTH );

    SetHealthBar(); // the function used to create the health bar
}

// to change the values of the progressbar when player's health change
function Server::ServerData(Data)
{
// the first parameter we sent for all the data packets will be a byte so lets get it
local byte = Data.ReadByte();
// the local will store it's value

switch (byte)
{
case ByteInfo.Kills: // kills label
// the next values we sent where kills and deaths in the order and both were int vals
local kills = Data.ReadInt(),
deaths = Data.ReadInt();
// values stored

// I'll use a func to make the labels
CreateKillsGUI(kills, deaths);
break;

        case ByteInfo.HpBar: //GUI Health bar
            // we sent player's health which is in an int value. Lets store that in a variable
            local health = Data.ReadInt();

            // lets create using a func again
            SetHealthBar(health);
        break;
default: break;
}
}

//now the function itself
function SetHealthBar(health = 100) {

    // we will use canvas and add progress bar as its child so we can easily position the label

    // condition to check if the health was already present
    if(UI.Canvas("HealthCanvas") == null)
    {
        local hpcan = UI.Canvas({
            id = "HealthCanvas" 
            // another cool func in DecUI
            RelativeSize = ["7%", "3.5%"] // parameters are width and height, using percent means the part of the screen size of the player
           
            // we want it at the position of old health bar and hud_left doesn't exist :c
            align = "hud_right"
            move = {left = "19.5%", down = "8.5%"}               
           
            // lets add a progressbar but as a child of the canvas
            children = [
                // progressbar
                UI.ProgressBar({
                    id = "HealthBar"
                    RelativeSize = ["100%", "100%"] // it will inherit the size of the canvas since canvas is it's parent


                    // some basic things
                    MaxValue = 100
                    Thickness = 2
                    StartColour = Colour(255, 77, 77)
                    EndColour = Colour(150, 40, 40)
                    selectedColour = Colour(255, 100, 100)
                    Value = health
                })
                // now lets add a label
                UI.Label({
                    id = "HealthLabel" 
                    Text = health + "%"
                    FontSize = 12
                    TextColour = Colour(255, 255, 255)
                    align = "center" // it will align itself according to the canvas since canvas is the parent. center means to the center of the canvas
                })
            ]
        });
    }
    else
    {
        // when we receive the data from the server when player health is changed


        //if the canvas is already present, then obv children will also be. lets change their values
        UI.ProgressBar("HealthBar").Value = health;
        UI.Label("HealthLabel").Text = health + "%";   
    }
}
[/noae][/noae][/noae][/noae][/noae]
RT Member | Asian City Scripter | CTB Contributor | VKs Contributor | European City Contributor | EAD Scripter

Xmair

It's a lot better to check for the health on the client side each frame than it is to send streams each time health changes from the server. For the streams, you should consider storing the stream identifiers in enumerations. Other than that, good one.

Credits to Boystang!

VU Full Member | VCDC 6 Coordinator & Scripter | EG A/D Contributor | Developer of VCCNR | Developer of KTB | Ex-Scripter of EAD

umar4911

Quote from: Xmair on May 25, 2020, 11:30 AMIt's a lot better to check for the health on the client side each frame than it is to send streams each time health changes from the server. For the streams, you should consider storing the stream identifiers in enumerations. Other than that, good one.
since one of the main objective of the tutorial was to send data from server to client. That's the reason, I'm sending data each time health changes. I wasn't getting any example in my mind so what I got, I made. I was even thinking to send data onPlayerJoin :P (but didn't due to script load delay)

For streams identification, I myself prefer enum. It was just a basic tutorial that's the reason I'm didn't make it. Thanks for your opinion btw.
RT Member | Asian City Scripter | CTB Contributor | VKs Contributor | European City Contributor | EAD Scripter

Sebastian

Like Xmair,  I suggest using an "enum" for a better approach.

enum StreamType
{
Kills = 0x02
Deaths = 0x03
Etc
}


Place it in both: serverside and clientside

This way,  you can use StreamType. Kills and StreamType. Deaths for e.g. instead of 0x02 and 0x03

umar4911

RT Member | Asian City Scripter | CTB Contributor | VKs Contributor | European City Contributor | EAD Scripter

Sebastian

Quote from: umar4911 on May 25, 2020, 02:53 PMUpdate: Added enumerations

You should also replace every 0x02 with StreamType. Kills

umar4911

Update: Added enumerations as the start and added in the whole script
RT Member | Asian City Scripter | CTB Contributor | VKs Contributor | European City Contributor | EAD Scripter