[Tutorial] Smart use of the Streamer [Incognito]
#1

Introduction

Hey there!

This is my first tutorial ever! I have been scripting for this game for a while and I feel it's time to share some of the things I learnt in my journey.

The first thing I'd like to talk about the (hidden) advantages of Incognito's streamer plugin.

If you haven't heard about the streamer before: Incognito's streamer is a plugin that streams all sorts of properties. It's faster than SA:MP's native streamer. You can read the details in their thread!

Let's talk areas first.

1. Areas

An area is a 2D or 3D property a player can interact with. Its most simple form is defined as follows:

Code:
new iAreaID = CreateDynamicRectangle(0.0, 0.0, 10.0, 10.0);
We now stored the ID of the area that was created with CreateDynamicRectangle in the iAreaID variable. Whenever a player enters or exit this area, one of the following is triggered:

Code:
OnPlayerEnterDynamicArea(playerid, areaid); // When a player enters an area.
OnPlayerLeaveDynamicArea(playerid, areaid); // When a player exits an area.
So this means we can do something with the player when they enter an area!
Let's say you want to make a house system:
- House 1's exterior coordinate teleport you to House 1's interior coordinate.
- House 2's exterior coordinate teleport you to House 2's interior coordinate.
- etc.

Let's sketch a situation in which using the streamer can be very beneficial.
A house system contains 2000 different house properties. Each has a house exterior and an interior you can travel from and to with the /enterhouse and /exithouse command. What I usually see in gamemodes is the following (in ZCMD):

Code:
#define MAX_HOUSES 2000

enum HInfo {
	
	Float:HouseExteriorX,
	Float:HouseExteriorY,
	Float:HouseExteriorZ,
	Float:HouseInteriorX,
	Float:HouseInteriorY,
	Float:HouseInteriorZ
}
new House[MAX_HOUSES][HInfo];

CMD:enterhouse(playerid, params[]) {
	
	for(new h; h < MAX_HOUSES; h++) {

		if(IsPlayerInRangeOfPoint(playerid, 2.0, House[h][HouseExteriorX], House[h][HouseExteriorY], House[h][HouseExteriorZ])) { // Virtual World checks are left out.

			SetPlayerPos(playerid, House[h][HouseInteriorX], House[h][HouseInteriorY], House[h][HouseInteriorZ]);
			// etc.
		}
	}
	return 1;
}

CMD:exithouse(playerid, params[]) {
	
	for(new h; h < MAX_HOUSES; h++) {

		if(IsPlayerInRangeOfPoint(playerid, 2.0, House[h][HouseInteriorX], House[h][HouseInteriorY], House[h][HouseInteriorZ])) { // Virtual World checks are left out.

			SetPlayerPos(playerid, House[h][HouseExteriorX], House[h][HouseExteriorY], House[h][HouseExteriorZ]);
			// etc.
		}
	}
	return 1;
}
So everytime someone enters (or exits) a house, they run this code to check to what linked coordinate they want to send the player to.
Above code loops through 2000 coordinates, comparing each set with the current player's position. There's nothing wrong with using this kind of vicinity check, but this block of code runs about 1/ms. Thus not very fast, especially when it's called very often!

Now let's give the streamer a go!
Code:
#define MAX_HOUSES 2000

enum HInfo {
	
	Float:HouseExteriorX, // I prefer Float:HousePos[3], but to be clear I use the linguistically more understandable version.
	Float:HouseExteriorY,
	Float:HouseExteriorZ,
	Float:HouseInteriorX,
	Float:HouseInteriorY,
	Float:HouseInteriorZ,
	bool:bExists, // I'd use a bitvar for this, but I won't make it too complicated just yet.
	iAreaID[2] // We create 2, one for the enter location and one for the exit location.
}
new House[MAX_HOUSES][HInfo];

House[h][iAreaID][0] = CreateDynamicSphere(House[h][HouseExteriorX], House[h][HouseExteriorY], House[h][HouseExteriorZ], 3.0, House[h][HouseVW]); // The house exterior.
House[h][iAreaID][1] = CreateDynamicSphere(House[h][HouseInteriorX], House[h][HouseInteriorY], House[h][HouseInteriorZ], 3.0, House[h][HouseVW]); // The house interior.
Now we created the areas and we have the area ID's stored. Let's see what happens when a player enters the house entrance point:

Code:
OnPlayerEnterDynamicArea(playerid, areaid) // -> The areaid that we receive is the ID of the house exterior area.
But we need the corresponding ID of the house to fetch that house ID's interior coordinates.
So let's assign the ID of the house to the area. We do that with:

Code:
Streamer_SetIntData(type, STREAMER_ALL_TAGS id, data, value);
For our areas (remember, both exterior and interior!), this is:

Code:
CreateHouse(h) { // Let's say this is where the house is created.

	Streamer_SetIntData(STREAMER_TYPE_AREA, House[h][iAreaID][0], E_STREAMER_EXTRA_ID, h);
	Streamer_SetIntData(STREAMER_TYPE_AREA, House[h][iAreaID][1], E_STREAMER_EXTRA_ID, h);
}
And now the ID of the house is properly assigned to the area. So when a player enters the entrance point, we can fetch the House ID:

Code:
OnPlayerEnterDynamicArea(playerid, areaid) {
	// If you plan on using this in more systems, make sure the areaid equals that of the respective propery too!

	new h = Streamer_GetIntData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID);
	if(0 <= h < MAX_HOUSES) { // A bounds check
		if(House[h][bExists]) SetPVarInt(playerid, "AtHouse", h); // Just make sure that the house actually exists. For example with a variable.
	}
}

OnPlayerExitDynamicArea(playerid, areaid) {
	
	new h = Streamer_GetIntData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID);
	if(0 <= h < MAX_HOUSES) { // A bounds check
		if(House[h][bExists]) DeletePVar(playerid); // Make sure to delete the PVar when they go out of the enter/exit point.
	}
}
So when they want to enter the house with /enterhouse, you can use:

Code:
CMD:enterhouse(playerid, params[]) {
	
	if(GetPVarType(playerid, "AtHouse")) {

		new h = GetPVarInt(playerid, "AtHouse");
		SetPlayerPos(playerid, House[h][HouseInteriorX], House[h][HouseInteriorY], House[h][HouseInteriorZ], 3.0, House[h][HouseVW]);
		// etc.
	}
	else SendClientMessage(playerid, 0xFFFFFFFF, "You are not at a house.");
	return 1;
}
I benchmarked above code at 250 times/ms. Quite a bit faster than the 1 time/ms version!
Conclusion: we save our server a lot of stress when using the streamer to let the player interact with server properties!

Part II will soon follow!
I hope you enjoyed this first sneak peek into how you can use the streamer for more than just streaming.
Reply
#2

Reserved.
Reply
#3

I'm glad you brought up extra IDs, it's actually an awesome feature not a lot of scripts utilize. The speed of it's usage is expedited way more than it would be had it been done in PAWN rather than through a plugin already, combine it with the speed you gain from not having to loop through all houses and you have an extremely fast function that can be used for nearly all types of get nearest item looping.
Reply
#4

I never thought of this. Looks a lot cleaner.
Reply
#5

I was planning on doing an advanced steamer tutorial since most people just use it for simple object management. Streamer is capable of so much more than that!

Thank you for this thread, and I hope P2 will be even greater, but I still plan on making thread for all of the more advanced stuff.
Reply
#6

This is one of the most basic uses of it, and yet most of the people don't even see beyond the streaming stuff of that amazing plugin, I mean you could make so many beautiful things with the streamer.
GJ Jingles.
Reply
#7

Really good tutorial, as Cryder said, Incognito's streamer is an amazing tool.

I wonder what method would you use in order to identify different properties, for example, differentiate between houses and businesses.
Quote:
Originally Posted by Jingles
// If you plan on using this in more systems, make sure the areaid equals that of the respective propery too!
I used arrays in order to save two values, the first one indicates the property type and the second one the id of the property.
PHP Code:
CreateHouse(index)
{
    new array[
2];
    array[
0] = 1/* House */
    
array[1] = index;
    
h_Info[index][Area] = CreateDynamicSphere(...);
    
Streamer_SetArrayData(STREAMER_TYPE_AREAh_Info[index][Area], E_STREAMER_EXTRA_ID, array, 2);
    return 
1;
}
CreateBusiness(index)
{
    new array[
2];
    array[
0] = 2/* Business */
    
array[1] = index;
    
b_Info[index][Area] = CreateDynamicSphere(...);
    
Streamer_SetArrayData(STREAMER_TYPE_AREAb_Info[index][Area], E_STREAMER_EXTRA_ID, array, 2);
    return 
1;

Then:
PHP Code:
public OnPlayerEnterDynamicArea(playeridareaid)
{
    new array[
2];
    
Streamer_GetArrayData(STREAMER_TYPE_AREAareaidE_STREAMER_EXTRA_ID, array, 2);
    switch(array[
0])
    {
        case 
1/* House */
        
{
            
SetPlayerPos(playeridh_Info[array[1]][IntPos][0], ...);
        }
        case 
2/* Business */
        
{
            
SetPlayerPos(playeridb_Info[array[1]][IntPos][0], ...);
        } 
    }
    return 
1;

What method would you use guys?
Reply
#8

Quote:
Originally Posted by RIDE2DAY
View Post
Really good tutorial, as Cryder said, Incognito's streamer is an amazing tool.

I wonder what method would you use in order to identify different properties, for example, differentiate between houses and businesses.


I used arrays in order to save two values, the first one indicates the property type and the second one the id of the property.
PHP Code:
CreateHouse(index)
{
    new array[
2];
    array[
0] = 1/* House */
    
array[1] = index;
    
h_Info[index][Area] = CreateDynamicSphere(...);
    
Streamer_SetArrayData(STREAMER_TYPE_AREAh_Info[index][Area], E_STREAMER_EXTRA_ID, array, 2);
    return 
1;
}
CreateBusiness(index)
{
    new array[
2];
    array[
0] = 2/* Business */
    
array[1] = index;
    
b_Info[index][Area] = CreateDynamicSphere(...);
    
Streamer_SetArrayData(STREAMER_TYPE_AREAb_Info[index][Area], E_STREAMER_EXTRA_ID, array, 2);
    return 
1;

Then:
PHP Code:
public OnPlayerEnterDynamicArea(playeridareaid)
{
    new array[
2];
    
Streamer_GetArrayData(STREAMER_TYPE_AREAareaidE_STREAMER_EXTRA_ID, array, 2);
    switch(array[
0])
    {
        case 
1/* House */
        
{
            
SetPlayerPos(playeridh_Info[array[1]][IntPos][0], ...);
        }
        case 
2/* Business */
        
{
            
SetPlayerPos(playeridb_Info[array[1]][IntPos][0], ...);
        } 
    }
    return 
1;

What method would you use guys?
That's exactly how you do it Well done!
Reply
#9

Can you give us example when player can enter and EXIT house pls ?
Reply
#10

Quote:
Originally Posted by Sioux
View Post
Can you give us example when player can enter and EXIT house pls ?
I use pickups for houses (blue and green house icon) and have set extra ID as "MAX_HOUSES + index" for each house pickup on loading. Retrieving the house and entering/exiting without loops is very simple afterwards.
PHP Code:
// global:
new gPlayer_InHouse[MAX_PLAYERS];
// OnPlayerConnect:
gPlayer_InHouse[playerid] = -1;
// OnPlayerPickUpDynamicPickup:
switch (Streamer_GetIntData(STREAMER_TYPE_PICKUPpickupidE_STREAMER_MODEL_ID))
{
    case 
12721273:
    {
        
// getting the "index" of the house to access the array
        
new houseid Streamer_GetIntData(STREAMER_TYPE_PICKUPpickupidE_STREAMER_EXTRA_ID) - MAX_HOUSES;
        if (
gPlayer_InHouse[playerid] == -1// player is not in any house, so this is "entering"
        
{
            
gPlayer_InHouse[playerid] = houseid;
            
// set position for interior of house.
            
SetPlayerPos(...);
        }
        else 
// player is in a house, so this is "exiting"
        
{
            
// set position for exterior of house and set some offset (for x, y) so the player won't be teleported on top of the pickup
            
SetPlayerPos(...);
            
gPlayer_InHouse[playerid] = -1;
        }
    }

PS: There are two pickups, one for interior and the other for exterior. You may keep the one that enters the building and on pressing ENTER or any other key making the player to exit (you have the houseid already anyway).
Reply
#11

If I delete the area, should I remove from the array? And if, how can i do it?
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)