[Tutorial] Efficient Multiple Spawning System
#1

Efficient Multiple Spawning System

What is this??
If you have more than one spawn point in your spawning system for each city, this is an optimized system that will find the correct spawn point from an array of spawn points to avoid players from flooding at the same spawn.This system follows an algorithm to compute the right spawn point.

The Purpose of the Tutorial:
I've seen many gamemodes that have a lengthy code and inefficient method for spawning players.Even the sample gamemode in the SAMP Server, "Grand Larency" does it in an unoptimized way.Here is my method that will find the spawn point in just 3-4 lines.Instead of using long, lengthy,CPU Consuming switch case or if...else.

What will this system do:
You will've an array of spawn points of different regions.You can have unlimited regions and unlimited spawn points.Region here, means a large area of the map,for example LV,SF,LS.You will have a constant number of spawn points for each region in the array(Will be discussed later).What my system does is, it returns the array index of a spawn point(the index is not random, it gives an appropriate index for avoiding flooding of players) from the spawn point array which belongs to the region that was asked.

Pros and Cons(Advantages & Disadvantages):
Positives:
*Does not repeat the spawn point for the next spawning player until the count is reset
*Easier to read the code, reduces the code drastically.
*Easy to understand, simple knowledge is required to use.
*Can be implemented before you start your game mode and later add contents to it.
*Unlimited Regions and Spawn Points
*Avoids players bumping at one point

Negatives:
*All the spawn points must be in one single array.Might reduce the readability of the code.
*There has to be constant number of spawn points in each region(If you have variable spawn points for different regions, then add the spawn points of the same region again at the end such tat the number of spawn points in every region becomes constant)

How to develop it??
For this tutorial I will use 3 regions(namely LS,SF,LV) and 10 different spawn points for each region(my own spawn points :P ).It's your wish to have as many number of regions and spawn points per region.

Step 1:Setting up the regions
First let's define an ID for each of the region.You have to keep the flow of the define continuous.It has to be in a arithmetic progression with common difference 1(It means the ID should be without gaps, like 0,1,2,3,4 not 1,3,4,6).Also the first region ID should be 0 and +1 for each extra region.

You can define the ID in two ways: using defines,using enums
1.Using #define
Code:
#define SPAWN_LS 0
#define SPAWN_SF 1
#define SPAWN_LV 2
Here, where ever we use SPAWN_LS or SPAWN_SF or SPAWN_LV in the code,the value defined in the define directive will be substituted by the compiler.This mainly increases readability of the code and helps avoid silly mistakes.

If you are new to #defines, I suggest you to go through this tutorial by Y_Less(a.k.a ColeMiner).

Ex:a = SPAWN_SF + 1; during compile time it the compiler will substitute the value of SPAWN_SF so it will be a = 1 + 1 since the value of SPAWN_SF is 1.

Using Enumeration
Code:
enum Spawns_t 
{
     SPAWN_LS = 0,
     SPAWN_SF,  //This will be 1 (assigned by the compiler.It is the rule of enumeration)
     SPAWN_LV //This will be 2 
}
This too does the same.In an enumeration the first variable must be given a value and the remaining variables will have an incremented value of the variable behind it.So here,SPAWN_SF will be 1 (SPAWN_LS + 1) and SPAWN_LV will be 2(SPAWN_SF + 1).

If you are new to enumerations, I suggest you to go through this tutorial by iPLEOMAX.

Use either #define or enumeration, not both.Using both will create errors.

Step 2:Creating spawn points for the regions
The next step is to create different spawn points for each region.
I have collected 10 spawn points for each region.You can make your own spawn points(unlimited spawn points).

You have to write all the spawns in a particular format for my algorithm to work.I will explain it in the next part what it is and how it works.The format is given below.

NOTE:The number of spawn points for each region should be same.If you have unequal number of spawn points for each region then add an existing spawn point of that region twice to get the number of spawns per region equal.

You have to arrange them according to the number you defined each region in this format:
Code:
const Float:Spawns[][4]
{
    REGION_0_SPAWN_1
    REGION_0_SPAWN_2
    REGION_0_SPAWN_3
    .....

    REGION_1_SPAWN_1
    REGION_1_SPAWN_2
    REGION_1_SPAWN_3
    .....

    REGION_2_SPAWN_1
    REGION_2_SPAWN_2
    REGION_2_SPAWN_3
    ......
}
Why '[][4]'??That 4...
Its because every location in San Andreas is written X,Y,Z and the 4th is the Rotation or Facing Angle.It determines the angle at which the player is facing or simply you can say it tells where your tiny player marker in your radar will be pointing to.Therefore, its very important.You'd not like you player spawn facing to a big wall which would be annoying!

Why 'const'?
const is a keyword in PAWN that makes the variable(not literally, it cannot be varied, so its not a variable) unchangeable.Also its faster than variables declared using new.

If you are new to this keyword then refer this page.

Here are my collection of Spawn Points.
10 for each region
Code:
const Float:Spawns[][4]
{
        {395.3744,-1799.8812,7.8281,2.9473}, //LS Spawns
	{595.5536,-1243.5162,18.1057,22.3843},
	{656.2115,-1225.1598,16.7225,70.0481}, 
	{1004.4772,-1361.2067,13.3209,357.1465},
	{1169.7753,-1489.6567,22.7650,89.2481},
	{2807.9282,-1176.8883,25.3805,173.6018},
	{2621.5681,-2227.2996,13.3710,86.5449},
	{1938.9319,-2147.9319,13.5547,179.5551},
	{1446.3792,-2286.4343,13.5469,92.1186},
	{1235.4366,-2037.1150,61.0313,272.8595}
	
	{-1935.0742,678.1586,46.5625,356.4240}, //SF Spawns
	{-1922.5177,680.0504,46.5625,2.6907},
	{-1934.6843,264.8631,41.0469,276.9846},
	{-2022.3842,155.8002,28.8359,266.6432},
	{-2314.1555,-169.0953,35.3203,178.2457},
	{-2126.3633,-383.9755,35.3359,2.5950},
	{-2720.4807,-317.9581,7.8438,41.5845},
	{-2521.2214,-621.2564,132.7300,1.2376},
	{-1928.7382,-790.2328,32.1506,273.2949},
	{-1953.3707,1339.3734,7.1875,174.4267}
	
	{1098.1039,1609.9438,12.5469,180.6180}, //LV Spawns
	{2007.6625,1544.3978,12.9850,272.5070},
	{2024.0055,1343.0667,10.8203,268.3386},
	{2476.1292,1340.7408,10.8289,87.9012},
	{2496.2332,1434.5453,10.8203,189.2649},
	{2483.7869,1527.8451,11.0802,319.4963},
	{2092.6619,2075.7512,10.8203,270.5955},
	{2095.9263,1285.3276,10.8203,1.1614},
	{1995.7908,1073.3232,10.8203,8.5701},
	{1705.8315,1255.5388,10.6813,353.0535} 
}
Step 3: Declaring Variables to count the spawns
Now you'll need to create one more array of variables that will hold the index of the last used Spawn Point so that the next player who wants to spawn doesn't get the same spawn point(this is to avoid flooding players at the same spawn point and also to avoid a bug where 2 or more players spawn inside each other and get stuck).This variable will be incremented every time a spawn point is used and will be reset when it reaches the last spawn point.

Code:
new SpawnCount[3] = 0; //3 because I am using 3 different REGIONS
Step 4:Computing the index
This is the part where all the computation will be carried out for determining the correct spawn point.
This function will find the next spawn point of that region and return it.

To keep things neat and readable I will define a few more things.
Code:
#define MAX_SPAWNS_PER_REGION 10
Code:
stock GetSpawnIndex(spawn_region)
{
     new i = (MAX_SPAWNS_PER_REGION * spawn_region) + SpawnCount[spawn_region]; //This will get the index of the next spawn of the spawn points array
pawnCount[spawn_region]++; //Increment the count
     if(SpawnCount[spawn_region] >= MAX_SPAWNS_PER_REGION)  //If it goes more than the limit then we might get an error "Array index out of bounds" which in turn would crash the server.
     {
         SpawnCount[spawn_region] = 0; //Reset the count
     }
     return i;     //Return the index of the spawn point from the array
}
Parameters:
spawn_region = The REGION where the next spawn point must be.If we give the spawn_region as SPAWN_LV then we'll get a next spawn point that is in LV.

Returns:
The index of the spawn point form the spawns array.The index returned will be a spawn point that belongs to the given region.

The formula for finding the index is = (MAX_SPAWNS_PER_REGION * SPAWN_REGION) + SPAWN_COUNT;
As there are 10 spawn points for each region defined (here in the example script) ,according to the format and the code, every 10 entries in the array belongs to each region.So multiplying 10 with the region ID will give the first entry of the requested region in the array.Later adding the SpawnCount will give an index of the spawn point which is different from a spawn point that was used while spawning the previous player.What if the SpawnCount goes more than the MAX LIMIT??To overcome this problem,the spawn count must not go more than the limit.Hence we reset the counter to 0 every time it reaches the max limit.

The formula is applied and the result is stored in variable i.
The respective SpawnCount variable is incremented so that next player doesn't get the same spawn point once again.
Then the SpawnPoint is checked if it has reached the last spawn point for that region and if so it is reset to 0.
And the index is returned.

This will give us the correct index that we need to use from the spawn points array.

Step 5:Processing the returned index to spawn
We now need to spawn the player using the index that was returned by the function.
Code:
new index = GetSpawnIndex(SPAWN_SF); //You can provide any region to GetSpawnIndex depending on your needs.
SetSpawnInfo(playerid,teamid,skinid,Spawns[index][0],Spawns[index][1],Spawns[index][2],Spawns[index][3],-1,-1,-1,-1,-1,-1);
SetSpawnInfo is a SA:MP native function that sets the player spawn position.We need to provide it with the X,Y,Z co-ordinates of the spawn point.We will use the index returned by GetSpawnIndex and give the correct set of spawn points.

Final Code
The final code should look somewhat like this
Code:
#define SPAWN_LS 0
#define SPAWN_SF 1
#define SPAWN_LV 2

#define MAX_SPAWNS_PER_REGION 10
/////////////////////////////////////////////////////////////////////////////////////
const Float:Spawns[][4]
{
        {395.3744,-1799.8812,7.8281,2.9473}, //LS Spawns
	{595.5536,-1243.5162,18.1057,22.3843},
	{656.2115,-1225.1598,16.7225,70.0481}, 
	{1004.4772,-1361.2067,13.3209,357.1465},
	{1169.7753,-1489.6567,22.7650,89.2481},
	{2807.9282,-1176.8883,25.3805,173.6018},
	{2621.5681,-2227.2996,13.3710,86.5449},
	{1938.9319,-2147.9319,13.5547,179.5551},
	{1446.3792,-2286.4343,13.5469,92.1186},
	{1235.4366,-2037.1150,61.0313,272.8595}
	
	{-1935.0742,678.1586,46.5625,356.4240}, //SF Spawns
	{-1922.5177,680.0504,46.5625,2.6907},
	{-1934.6843,264.8631,41.0469,276.9846},
	{-2022.3842,155.8002,28.8359,266.6432},
	{-2314.1555,-169.0953,35.3203,178.2457},
	{-2126.3633,-383.9755,35.3359,2.5950},
	{-2720.4807,-317.9581,7.8438,41.5845},
	{-2521.2214,-621.2564,132.7300,1.2376},
	{-1928.7382,-790.2328,32.1506,273.2949},
	{-1953.3707,1339.3734,7.1875,174.4267}
	
	{1098.1039,1609.9438,12.5469,180.6180}, //LV Spawns
	{2007.6625,1544.3978,12.9850,272.5070},
	{2024.0055,1343.0667,10.8203,268.3386},
	{2476.1292,1340.7408,10.8289,87.9012},
	{2496.2332,1434.5453,10.8203,189.2649},
	{2483.7869,1527.8451,11.0802,319.4963},
	{2092.6619,2075.7512,10.8203,270.5955},
	{2095.9263,1285.3276,10.8203,1.1614},
	{1995.7908,1073.3232,10.8203,8.5701},
	{1705.8315,1255.5388,10.6813,353.0535} 
}
////////////////////////////////////////////////////////////////////////////
new SpawnCount[3] = 0;
////////////////////////////////////////////////////////////////////////////
stock GetSpawnIndex(spawn_region)
{
     new i; 
     i = (MAX_SPAWNS_PER_REGION * spawn_region) + SpawnCount[spawn_region]; //This will pick the next spawn
     SpawnCount[spawn_region]++; //Increment the count
     if(SpawnCount[spawn_region] >= MAX_SPAWNS_PER_REGION)  //Impossible to go above 10
     {
         SpawnCount[spawn_region] = 0; //Reset the count
     }
     return i;     //Return the index of the spawn point from the array
}
/////////////////////////////////////////////////////////////////////////////
public OnPlayerConnect (playerid)
{
    new skinid = random(299);
   
    new index = GetSpawnIndex(SPAWN_SF); //The second parameter(i.e:SPAWN_SF here) can be any region
SetSpawnInfo(playerid,1,skinid,Spawns[index][0],Spawns[index][1],Spawns[index][2],Spawns[index][3],-1,-1,-1,-1,-1,-1);

return 1;
}
Notes:
  • Using #defines make the code neater.It helps in making a change and also increases the readability of the code.
  • Don't use negative values while defining Regions.
  • Begin the first region with 0 and the following regions must be in a +1 progression(i.e:0,1,2,3,4,5,etc)
  • To reduce complications I had avoided few optimizations and some good programming habits.But you can do it if you know.
  • Use static variable inside GetSpawnIndex instead of a global variable for SpawnCount.
  • You can optimize the code by using this code:
    Code:
     if(++SpawnCount[spawn_region] >= MAX_SPAWNS_PER_REGION)
    It removes a line that increments the SpawnCount and does it inside the if itself.Will increase the speed(because we just avoided an extra memory fetch).
Updates:
  • [8th March 2014]Revised the whole tutorial.Fixed many spelling mistakes,grammatical mistakes and improved the language.

Reply
#2

Quote:
Originally Posted by Y_Less
View Post
OK, well I hope when it does arrive you're going to justify the use of the word "efficient" - I'm intrigued now and expect good evidence!
It has arrived!!

I use my formula for calculating the right spawn point instead of long if...else if... or switch...case.
This thingy(tiz tut) is no match for the CPU Time and the size of the code compared to if..else and switch..case.

Hence, justified.
Reply
#3

Why not:

Code:
stock const offsets[][2] = {{LS_START, LS_END}, {SF_START, SF_END}, {LV_START, LV_END}};
stock GetSpawnLocation(location, &Float:x, &Float:y, &Float:z, &Float:a) {
    new idx = random(offsets[location][1] - offsets[location][0]) + offsets[location][0];
    x = spawns[idx][0];
    y = spawns[idx][1];
    z = spawns[idx][2];
    a = spawns[idx][3];
}
Reply
#4

Quote:
Originally Posted by Dan..
View Post
Why not:

Code:
stock const offsets[][2] = {{LS_START, LS_END}, {SF_START, SF_END}, {LV_START, LV_END}};
stock GetSpawnLocation(location, &Float:x, &Float:y, &Float:z, &Float:a) {
    new idx = random(offsets[location][1] - offsets[location][0]) + offsets[location][0];
    x = spawns[idx][0];
    y = spawns[idx][1];
    z = spawns[idx][2];
    a = spawns[idx][3];
}
Awesome!

Random might give same numbers or some one number may come rare and another gets to be very common.
But my system gives in an order and all get equal number of times whereas in your case few come many times some may even repeat.Yours might be faster than mine but mine does it without any chance of repetitions nor flooding of players in one place.

Anyway thanks I will use your snippet somewhere
Reply
#5

Sorry!I did not know it had already existed.I just saw many gamemodes and scripts that used to use if...else to pick the region then random to get the location thats why I wrote.
Tutorial - Epic Fail xD
Reply
#6

When I visited this, I expected some self generating spawns ! But anyways, it can really be done in some easier methods, which I'll share tomorrow. Cheers!
Reply
#7

Quote:
Originally Posted by Rajat_Pawar
View Post
When I visited this, I expected some self generating spawns ! But anyways, it can really be done in some easier methods, which I'll share tomorrow. Cheers!
How "Self-Generating Spawns" ?? How??
Reply
#8

Quote:
Originally Posted by Y_Less
View Post
I can't actually find the tutorial I was talking about - so the fact that there's a new one is a good thing.
-_- -_- -_-
Reply
#9

http://www.comicsanscriminal.com/
Thanks. The font is giving me a headache.
Reply
#10

Nice Link Learnt a lesson Now I am irritated by my font Changing it
Reply
#11

Quote:
Originally Posted by Yashas
View Post
Awesome!

Random might give same numbers or some one number may come rare and another gets to be very common.
But my system gives in an order and all get equal number of times whereas in your case few come many times some may even repeat.Yours might be faster than mine but mine does it without any chance of repetitions nor flooding of players in one place.

Anyway thanks I will use your snippet somewhere
If players spawned at location X are AFK, that spot is flooded anyway.

pawn Code:
main() {
    new r[10];
    for (new i = 0; i != 200000; ++i) {
        ++r[random(10)];
    }
    for (new i = 0; i != 10; ++i) {
        printf("%d %d", i, r[i]);
    }
}
Code:
0 20087
1 19945
2 19838
3 20122
4 19882
5 20016
6 20165
7 20117
8 20022
9 19806
So yeah, these values are pretty random...
Reply
#12

Quote:
Originally Posted by Dan..
View Post
If players spawned at location X are AFK, that spot is flooded anyway.

pawn Code:
main() {
    new r[10];
    for (new i = 0; i != 200000; ++i) {
        ++r[random(10)];
    }
    for (new i = 0; i != 10; ++i) {
        printf("%d %d", i, r[i]);
    }
}
Code:
0 20087
1 19945
2 19838
3 20122
4 19882
5 20016
6 20165
7 20117
8 20022
9 19806
So yeah, these values are pretty random...
ye but hes not gonna have 200000 players lol.. try with 15 or 20

i know its not big deal .. u make a point
Reply
#13

Quote:
Originally Posted by Yashas
View Post
How "Self-Generating Spawns" ?? How??
https://sampforum.blast.hk/showthread.php?tid=427067
An idea
Reply
#14

Quote:
Originally Posted by mastermax7777
View Post
ye but hes not gonna have 200000 players lol.. try with 15 or 20

i know its not big deal .. u make a point

200k Players - Computer Fried and the expeditive increase in the internet bills will get you roasted.
Serious LAG and why did I get cooking here
Reply
#15

I got some errors :/
pawn Code:
ThisOne.pwn(101) : error 001: expected token: ";", but found "{"
hisOne.pwn(108) : error 055: start of function body without function header
pawn Code:
new Float:Spawns[][4]
{//101
    {-238.4964,2608.2761,62.7135,2.8976},
    {-222.8692,2608.7708,62.7162,360.0000},
    {-208.2713,2608.9612,62.7135,0.4203},
    {-309.0230,2681.0723,62.6375,90.4226},
    {-309.0230,2681.0723,62.6375,90.4226}//Base 1
   
    {-1402.5712,2641.9802,55.6993,85.5085},//108
    {-1453.7939,2654.7534,55.8459,2.3052},
    {-1562.8668,2693.7920,55.7868,176.0593},
    {-1514.3767,2535.9150,55.7019,357.5095},
    {-1529.4465,2639.0063,55.8478,92.3112}//BASE 2
}
new SpawnCount[2] = 0;

SetSpawnInformation(spawn_region)
{
    new i;
    i = (MAX_SPAWNS_PER_REGION * spawn_region) + SpawnCount[spawn_region];
    SpawnCount[spawn_region]++;
    if(SpawnCount[spawn_region] >= MAX_SPAWNS_PER_REGION)
    {
        SpawnCount[spawn_region] = 0;
    }
    return i;
}
Reply
#16

You forgot a semi at the end of the curly brace and a = at the beginning of your Spawns Array.

The correct code should be
pawn Code:
new Float:Spawns[][4] =
{//101
    {-238.4964,2608.2761,62.7135,2.8976},
    {-222.8692,2608.7708,62.7162,360.0000},
    {-208.2713,2608.9612,62.7135,0.4203},
    {-309.0230,2681.0723,62.6375,90.4226},
    {-309.0230,2681.0723,62.6375,90.4226}//Base 1
   
    {-1402.5712,2641.9802,55.6993,85.5085},//108
    {-1453.7939,2654.7534,55.8459,2.3052},
    {-1562.8668,2693.7920,55.7868,176.0593},
    {-1514.3767,2535.9150,55.7019,357.5095},
    {-1529.4465,2639.0063,55.8478,92.3112}//BASE 2
};
new SpawnCount[2] = 0;

SetSpawnInformation(spawn_region)
{
    new i;
    i = (MAX_SPAWNS_PER_REGION * spawn_region) + SpawnCount[spawn_region];
    SpawnCount[spawn_region]++;
    if(SpawnCount[spawn_region] >= MAX_SPAWNS_PER_REGION)
    {
        SpawnCount[spawn_region] = 0;
    }
    return i;
}
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)