4
\$\begingroup\$

So I've ported a terrain generator to SA:MP (a GTA:SA mod that connects you to players around the world). The terrain is generated perfectly, after only a few hours of scripting. I was surprised how simple it was.

The problem is that in SA:MP only 1000 objects max can be streamed in for a player at a time, and I've got it set to a 500 limit to ensure play quality. Obviously, streaming all cubes that are in the players' range (which is currently what I'm doing and it's horrible) would be very inefficient! I need an algorithm that would stream only the cubes that are on an outside layer (any objects that wouldn't be visible to the players view shouldn't be streamed).

The current streamer is located here.

The terrain generator is exactly as follows:

#include a_samp
#include streamer

#define START_X 5000.0
#define START_Y 5000.0
#define START_Z -5.5

#define WIDTH   128
#define HEIGHT  128
#define DEPTH   32

#define WATER_LEVEL 6
#define SEED 2512389

Float:findnoise(Float:x, Float:y)
{
    new n = (floatround(x) + floatround(y) * 57);
    n = ((n + SEED) << 13) ^ (n + SEED);
    new nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
    return 1.0 - (float(nn) / 1073741824.0);
}

Float:interpolate(Float:a, Float:b, Float:x)
{
    new Float:ft = x * 3.1415927;
    new Float:f = (1.0 - floatcos(ft)) * 0.5;
    return a * (1.0 - f) + b * f;
}

Float:noise(Float:x, Float:y)
{
    new Float:floorx = floatround(x, floatround_floor);
    new Float:floory = floatround(y, floatround_floor);

    new Float:s = findnoise(floorx, floory), 
        Float:t = findnoise(floorx + 1, floory), 
        Float:u = findnoise(floorx, floory + 1), 
        Float:v = findnoise(floorx + 1, floory + 1);

    return interpolate(interpolate(s, t, x - floorx), interpolate(u, v, x - floorx), y - floory);
}

render(Float:zoom = 25.0, Float:p = 0.25)
{
    new octaves = 2;
    for(new y; y < HEIGHT; y++)
    {
        for(new x; x < WIDTH; x++)
        {
            new Float:getnoise;
            for(new a; a < octaves - 1; a++)
            {
                new Float:frequency = floatpower(2.0, a);
                new Float:amplitude = floatpower(p, a);
                getnoise += noise(float(x) * frequency / zoom, float(y) / zoom * frequency) * amplitude;
            }

            new height = floatround((getnoise * 128.0) + 128.0);
            clamp(height, 0, 255);
            new ez = floatround(float(height * DEPTH) / 255.0);
                                //Sand      //Grass     //Dirt          //Water     //Stone
            static colors[5] = {0xFFFFEB35, 0xFF573B0C, 0xFF5E9D34, 0xFF009999, 0xFF808080};
            for(new z; z < ez + 1; z++)
            {
                new type = GetBlock(z, ez);
                if(type)
                {
                    new o = CreateDynamicObject(19789, x + START_X, y + START_Y, z + START_Z, 0.0, 0.0, 0.0);
                    SetDynamicObjectMaterial(o, 0, -1, "none", "none", colors[type - 1]);
                }
            }
        }
    }
}

GetBlock(z, height)
{
    if (height > WATER_LEVEL)
    {
        if(z > height)
            return 0;//Air
        if(z == height)
            return 3;//Grass
        if(z > height - 7) //-7
            return 2;//Dirt
    }      
    else
    {
        if(z > height) //5
            return 0;//Water - 4
        if(z > height - 2) //-5
            return 1;//Sand
        if(z > height - 7)//-10
            return 2;//Dirt
    }
    return 5;//Stone
}

main(){}
public OnGameModeInit()
{   
    render();
    AddPlayerClass(0, START_X, START_Y, START_Z + 10.0, 0.0, 0, 0, 0, 0, 0, 0);
    return 1;
}

The render function is what begins the generation and creates the objects (cubes). OnGameModeInit is a callback that is ran as soon as the server is initialized. Currently there isn't any textures applied, just some color based on the block type, but that's off topic.

Is it reasonable to edit the source of the streamer to only stream the visible layer? If so, is anyone willing to help me with it? If nobody is willing to help personally, can someone at least tell me how I should go about doing this?

Note: This was ruled off-topic on the Arqade SE.

\$\endgroup\$
10
  • 1
    \$\begingroup\$ Are the cubes modifiable? If not, can you just stream down the seed and let the client recreate the terrain? \$\endgroup\$ Commented Sep 9, 2015 at 1:44
  • \$\begingroup\$ Well I can't let the client stream them, everything needs to be server-sided. The clients lag is on their own hands (they won't lag unless they have a VERY bad set of specs). \$\endgroup\$ Commented Sep 10, 2015 at 11:39
  • \$\begingroup\$ Lag is irrelevant, as is the server being authoritative. If the server is generating a terrain from integer seed S and chunk location X,Y then the client can generate the exact same terrain from the exact same S, X, and Y. Thus all you need to stream are those three numbers. Obviously that only works if the terrain is not (highly) mutable after generation; you should clarify in your question what your actual constraints and needs are for this terrain. \$\endgroup\$ Commented Sep 10, 2015 at 20:26
  • \$\begingroup\$ No, this isn't a minecraft-like game where the clients stream their own objects client-sidedly. All objects are handled by the server and each object's position is sent in packets to the clients. \$\endgroup\$ Commented Sep 11, 2015 at 21:47
  • \$\begingroup\$ I understand this is an online game and I think you're entirely missing my point, but comments aren't a good fit for extended conversation. Good luck with your game. :) \$\endgroup\$ Commented Sep 12, 2015 at 17:35

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.