1 (edited by necropotame 2016-07-09 14:08:05)

Topic: Tutorial: How to create a custom tune system per client

I post here a little tutorial about how to send custom parameters to each client/ This can be used for freezing effect, lock position of some tees, water physics, ...

In teeworlds, physics is done by the client and the server simultaneously. This allows the client to predict the position of each tee between two snapshots of the server. However, only the tee that you control is predicted. Other tees are simply interpolated between last position and next position. Constants used to compute the physics are called "Tune Parameters". If you want to change the physics in your mod, you usually change some of these tune parameters. When you do that, a message containing the new constants is sent to all clients.

If you want to change these constants to only one client, then things get more complicated. You must send the new tune parameter to only one client and in same time, change how the physics is computed in your server to allows one set of constants per tee. We will start with this point.

Change the core

The game core is the place where the physics is done. As we said, we want to use different tune parameters depending of the situation. The simplest way to do it is to change the file src/game/gamecore.h and gamecore.cpp

src/game/gamecore.h

void Tick(bool UseInput);
void Move();

We will add a pointer to a CTuningParams in order to let the core know which parameters must be used

void Tick(bool UseInput, const CTuningParams* pTuningParams);
void Move(const CTuningParams* pTuningParams);

src/game/gamecore.cpp
Then, the implementation of both functions must be changed . Just replace all occurrences of "m_pWorld->m_Tuning." by "pTuningParams->" in Move() and Tick(). For exemple,

void CCharacterCore::Move()
{
    float RampValue = VelocityRamp(length(m_Vel)*50, m_pWorld->m_Tuning.m_VelrampStart, m_pWorld->m_Tuning.m_VelrampRange, m_pWorld->m_Tuning.m_VelrampCurvature);
    ...

will become

void CCharacterCore::Move(const CTuningParams* pTuningParams)
{
    float RampValue = VelocityRamp(length(m_Vel)*50, pTuningParams->m_VelrampStart, pTuningParams->m_VelrampRange, pTuningParams->m_VelrampCurvature);
    ...

src/game/server/entities/character.cpp
Since the header of Tick() and Move() is different, we need to fix all calls of these function. Simply replace all occurrences of m_Core.Tick(xxx) by m_Core.Tick(xxx, GameServer()->Tuning()) and m_Core.Move() by m_Core.Move(GameServer()->Tuning()). The only exception is in the function TickDefered where you need to use &TempWorld.m_Tuning unstead of GameServer()->Tuning() to move the dummy (this is very important, otherwise, your mod will be unplayable).

void CCharacter::TickDefered()
{
    // advance the dummy
    {
        CWorldCore TempWorld;
        m_ReckoningCore.Init(&TempWorld, GameServer()->Collision());
        m_ReckoningCore.Tick(false);
        m_ReckoningCore.Move();
        m_ReckoningCore.Quantize();
    }

    //lastsentcore
    vec2 StartPos = m_Core.m_Pos;
    vec2 StartVel = m_Core.m_Vel;
    bool StuckBefore = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f));

    m_Core.Move();
    ...

will become

void CCharacter::TickDefered()
{
    // advance the dummy
    {
        CWorldCore TempWorld;
        m_ReckoningCore.Init(&TempWorld, GameServer()->Collision());
        m_ReckoningCore.Tick(false, &TempWorld.m_Tuning); // <--- Changes
        m_ReckoningCore.Move(&TempWorld.m_Tuning); // <--- Changes
        m_ReckoningCore.Quantize();
    }

    //lastsentcore
    vec2 StartPos = m_Core.m_Pos;
    vec2 StartVel = m_Core.m_Vel;
    bool StuckBefore = GameServer()->Collision()->TestBox(m_Core.m_Pos, vec2(28.0f, 28.0f));

    m_Core.Move(GameServer()->Tuning()); // <--- Changes
    ...
Fix the NetVersion

Since you edited a file used by both server and client, the NetVersion of your server will be different, meaning that no one will be able to join your server. To fix that, you need to fake your NetVersion.

src/engine/server/server.cpp
Search for "NetVersion" in server.cpp

if(str_comp(pVersion, GameServer()->NetVersion()) != 0))

and replace this line by this one

if((str_comp(pVersion, GameServer()->NetVersion()) != 0) && (str_comp(pVersion, "0.6 626fce9a778df4d4") != 0))

Here, "0.6 626fce9a778df4d4" is the default NetVersion of TeeWorlds 0.6.3.

Create a simple system to maintain custom tune parameters

The second part is to send the new tune parameters to the client. The problematic here is to never forget to send the new tune parameters that you use. It became tricky when you have tune zones, weapons with particular tunes and some other effects in same time. To avoid any mistakes, we will attach two CTuningParams to each players. One for the current tune parameters that we want to use in the core, and one to store the tune parameters used in the last Tick. The idea is to send automatically tune parameters to the client if we detect a difference between the current and the previous CTuningParams.

src/game/server/player.h
Add these lines at the end of the class CPlayer

private:
    CTuningParams m_PrevTuningParams;
    CTuningParams m_NextTuningParams;
    
    void HandleTuningParams(); //This function will send the new parameters if needed

public:
    CTuningParams* GetNextTuningParams() { return &m_NextTuningParams; };

src/game/server/player.cpp
In the constructor, add these lines

    m_PrevTuningParams = *pGameServer->Tuning();
    m_NextTuningParams = m_PrevTuningParams;

At the end of Tick(), call HandleTuningParams(); The implementation of HandleTuningParams is given by

    if(!(m_PrevTuningParams == m_NextTuningParams))
    {
        if(m_IsReady)
        {
            CMsgPacker Msg(NETMSGTYPE_SV_TUNEPARAMS);
            int *pParams = (int *)&m_NextTuningParams;
            for(unsigned i = 0; i < sizeof(m_NextTuningParams)/sizeof(int); i++)
                Msg.AddInt(pParams[i]);
            Server()->SendMsg(&Msg, MSGFLAG_VITAL, GetCID());
        }
        
        m_PrevTuningParams = m_NextTuningParams;
    }
    
    m_NextTuningParams = *GameServer()->Tuning();

src/game/gamecore.h
In the game core (header), add this function in the class CTuningParams to allow comparisons

    bool operator==(const CTuningParams& TuningParams)
    {
        #define MACRO_TUNING_PARAM(Name,ScriptName,Value) if(m_##Name != TuningParams.m_##Name) return false;
        #include "tuning.h"
        #undef MACRO_TUNING_PARAM
        return true;
    }

src/game/server/entities/character.cpp
Replace all occurrences of m_Core.Tick(xxx, GameServer()->Tuning()) by m_Core.Tick(xxx, m_pPlayer->GetNextTuningParams()) and m_Core.Move(GameServer()->Tuning()) by m_Core.Move(m_pPlayer->GetNextTuningParams()). Don't do it for the dummy! It still must use &TempWorld.m_Tuning.

Example of use

src/game/server/entities/character.cpp

In the function Tick, just before the call to CCharacterCore::Tick(), change the gravity of one player:

if(m_pPlayer->GetCID() == 0)
        m_pPlayer->GetNextTuningParams()->m_Gravity = 0.1f;

m_Core.Tick(true, m_pPlayer->GetNextTuningParams());

Try to connect 2 clients to your server: the first one should have lower gravity!

2

Re: Tutorial: How to create a custom tune system per client

Wow this is awesome. Thanks!

Having troubles finding servers in the serverlist? Go to Pastebin (its a referer cause there is daily a new pastebin) and add the lines to your settings.cfg (in %APPDATA%\teeworlds). Then open teeworlds and go to the favorites tab. (Note however, standard teeworlds client can only show 256 favorites, use ddnet instead)

3 (edited by SEG4 2016-07-08 15:09:14)

Re: Tutorial: How to create a custom tune system per client

Thank you very much, I was waiting for this lesson !!!

4

Re: Tutorial: How to create a custom tune system per client

You have error in

(str_comp(pVersion, GameServer()->NetVersion()) != 0)  && str_comp(pVersion, "0.6 626fce9a778df4d4" != 0))

Use it

if(str_comp(pVersion, GameServer()->NetVersion()) != 0 && str_comp(pVersion, "0.6 626fce9a778df4d4") != 0);

Checked big_smile

5

Re: Tutorial: How to create a custom tune system per client

Thanks, I've corrected it smile

6

Re: Tutorial: How to create a custom tune system per client

I have a problem big_smile
http://s018.radikal.ru/i513/1607/37/1a333b7fe37b.png

7

Re: Tutorial: How to create a custom tune system per client

Try with this

if((str_comp(pVersion, GameServer()->NetVersion()) != 0) && (str_comp(pVersion, "0.6 626fce9a778df4d4") != 0));

8 (edited by SEG4 2016-07-08 18:52:53)

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

Try with this

if((str_comp(pVersion, GameServer()->NetVersion()) != 0) && (str_comp(pVersion, "0.6 626fce9a778df4d4") != 0));

Nope, not work(((
I tested it in two clients!

Look this:
http://s017.radikal.ru/i401/1607/d1/0216fd477f01.png

9

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

Try with this

if((str_comp(pVersion, GameServer()->NetVersion()) != 0) && (str_comp(pVersion, "0.6 626fce9a778df4d4") != 0));

The semicolon at the end of the if-clause has to be removed, that the part below is not execute every time, no matter if the versions are equal or not.

if((str_comp(pVersion, GameServer()->NetVersion()) != 0) && (str_comp(pVersion, "0.6 626fce9a778df4d4") != 0))

10

Re: Tutorial: How to create a custom tune system per client

Ouuuups. Thx

11

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

Ouuuups. Thx

Ahahah, LOL lol

12

Re: Tutorial: How to create a custom tune system per client

SEG4 wrote:

You have error in

(str_comp(pVersion, GameServer()->NetVersion()) != 0)  && str_comp(pVersion, "0.6 626fce9a778df4d4" != 0))

Use it

if(str_comp(pVersion, GameServer()->NetVersion()) != 0 && str_comp(pVersion, "0.6 626fce9a778df4d4") != 0);

Checked big_smile

You introduce it tongue

13

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

You introduce it tongue

I'm sorry I did not specifically big_smile

14

Re: Tutorial: How to create a custom tune system per client

Server crashes when a player disconnected via "Timeout" or "Too weak conntection" with the reason "error sending data"! hmm
http://puu.sh/rYy2a/8a97350922.png

I will be banned if I troll again ...

15 (edited by necropotame 2016-10-28 16:50:18)

Re: Tutorial: How to create a custom tune system per client

I've got similar error with a different problem, by sending too much broadcast. Did you change tune parameters each tick ?

16

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

I've got similar error with a different problem, by sending too much broadcast. Did you change tune parameters each tick ?

Yeah, just with your example:

if(m_pPlayer->GetCID() == 0)
        m_pPlayer->GetNextTuningParams()->m_Gravity = 0.1f;

Oh and I also get this annoying "too much broadcast"-bug, I had too much /n/n/n/n/n/ns big_smile

I will be banned if I troll again ...

17

Re: Tutorial: How to create a custom tune system per client

In this example, the value stay the same all the time (0.1f). Try to print something each time you execute "Server()->SendMsg(&Msg, MSGFLAG_VITAL, GetCID());", to make sure that the client is not flooded.

18

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

In this example, the value stay the same all the time (0.1f).

Oh, yeah!

necropotame wrote:

Try to print something each time you execute "Server()->SendMsg(&Msg, MSGFLAG_VITAL, GetCID());", to make sure that the client is not flooded.

Yeah I did it and the message is flooding da hell! How can I fix it?

I will be banned if I troll again ...

19

Re: Tutorial: How to create a custom tune system per client

Try to see why "if(!(m_PrevTuningParams == m_NextTuningParams))" is always true in your case.

20

Re: Tutorial: How to create a custom tune system per client

necropotame wrote:

Try to see why "if(!(m_PrevTuningParams == m_NextTuningParams))" is always true in your case.

Because m_PrevTuningParams is 1000 and m_NextTuningParams 200, everytime! What da hell? How?! I have no idea!

I will be banned if I troll again ...

21

Re: Tutorial: How to create a custom tune system per client

m_PrevTuningParams is not an integer but a structure. How did you print it to get 1000 or 200 ? I can't really help you because I don't know what you did exactly. You can send some part of your code, and describe more what you are trying to do.