3
\$\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\$
  • 1
    \$\begingroup\$ Are the cubes modifiable? If not, can you just stream down the seed and let the client recreate the terrain? \$\endgroup\$ – Sean Middleditch Sep 9 '15 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\$ – Corey Iles Sep 10 '15 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\$ – Sean Middleditch Sep 10 '15 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\$ – Corey Iles Sep 11 '15 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\$ – Sean Middleditch Sep 12 '15 at 17:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.