Jump to content

  • Log In with Google      Sign In   
  • Create Account


#Actualpapalazaru

Posted 03 September 2012 - 08:47 AM

A very rough example of what client-side prediction would look like (code not tested, just showing the process of how client prediction / server correction would work).

// player actions.
struct PlayerInputs
{ 
    float dt; // frame timestep
    Vector mouselook; 
    Vector keymove; 
    u_int actions;
};
struct PlayerState
{   
    // current update sequence number.
    int sqn;
    Vector position;   
    Vector velocity;   
    Quaternion orientation;   
    Vector angularVelocity;
    // update player state with a set of inputs.
    void update(PlayerInputs inputs)
    {
	    sqn++;   
	    // do stuff with inputs. Move player position, jump, ect...
	    //.....
    }
};
// a bundle of player inputs, and the subsequent player state calculated from those inputs.
struct PredictionPacket
{
    PlayerInputs inputs;
    PlayerState state;
};
struct ClientPrediction
{
    // list of local predictions.
    std::list<PredictionPacket> packets;
    // add a new update at the end of the queue.
    void recordUpdate(PlayerInputs inputs, PlayerState state);
    {
	    packets.push_front(PredictionPacket(Inputs, state))
    }
    // server acknowledged our prediction.
    void applyAcknowledgement(int ack)
    {
	    // discard older packets that have been acknowledged.
	    while(!packets.empty() && (packets.back().state.sqn <= ack))
		    packets.pop_back();		   
    }
	   
    // server sent us a correction.
    PlayerState applyCorrection(PlayerState correction)
    {
	    // discard older packets.
	    while(!packets.empty() && (packets.back().state.sqn < correction.sqn))
		    packets.pop_back();
	    // apply the correction for that sequence number.
	    if(!packets.empty() && (packets.back().state.sqn == correction.sqn))
	    {
		    // that's the state we should be in at that sequence number.
		    packets.back().state = correction;
		    // correct states up to the latest, current state.
		    for(std::list<PredictionPacket>::reverse_iterator it = packets.rbegin(); it != packets.rend(); ++it)
		    {
			    correction.update((*it).inputs);
			    (*it).state = correction;
		    }
	    }
	    // that's the latest state we should be in.
	    return correction;
    }
};
void UpdateClient(PlayerInputs inputs)
{
    // player update
    m_currentState.update(inputs);
    m_prediction.recordUpdate(inputs, m_currentState);
    // send prediction packets not yet acknowledge by server.
    sendPrediction();
    // process server acknwledgements, if we received any.
    int ack;
    if(receivedAcknowledgement(ack))
	    m_prediction.applyAcknowledgement(ack);
    // process server corrections, if we received any.
    PlayerState server_correction;
    if(receiveCorrectionPacket(server_correction))
	    m_currentState = m_prediction.applyCorrection(server_correction);
}
struct ServerCorrection
{
    // list of updates received from client.
    std::list<PredictionPacket> packets;
    // what we need to do after receiving a new update.
    bool has_ack;
    bool has_correction;
    PlayerState server_correction;
    // apply new client packet.
    void applyPredicition(PredictionPacket client_prediction)
    {
	    // discard older packets received from client.
	    while(!packets.empty() && (packets.back().state.sqn < ack))
		    packets.pop_back();   
	    // empty queue.
	    if(packets.empty())
	    {
		    // not the first packet.
		    if(client_prediction.state.sqn != 0)
			    return;
	   
		    // first packet in the list. always accept.
		    packets.push_front(client_prediction);
		   
		    // reset flags.
		    has_ack = true;
		    has_correction = false;
	    }
	    // part of an unbroken queue.
	    else
	    {
		    // not the packet we expected. Block until the client sends us that packet.
		    if(client_prediction.state.sqn != (packets.front().state.sqn + 1))
			    return;
		    // calculate the state on server.
		    PlayerState server_prediction = packets.front().state;
		    server_prediction.update(client_prediction.inputs);
		   
		    // no corrections currently pending.
		    // see if we can ack or re-correct.
		    if(!has_correction)
		    {
			    // new correction needed to be sent.
			    if(server_prediction != client_prediction.state)
			    {
				    has_correction = true;
				    server_correction = server_prediction;
			    }
			    // we can acknowledge the client prediction.
			    else
			    {
				    has_ack = true;
			    }
		    }
		    // add state at the end of the queue.
		    packets.push_front(PredictionPacket(client_prediction.inputs, server_prediction));
	    }
    }
    // get correction, if any are pending.
    bool getCorrection(PlayerState& correction)
    {
	    // no corrections pending.
	    if(!has_correction)
		    return false;
	    // send latest correction.
	    correction = server_correction;
	    has_correction = false;
	    return true;
    }
    // get ack, if any are pending.
    bool getAcknowledgement(int& ack)
    {
	    // no ack pending.
	    if(!has_ack)
		    return false;
	    // send latest ack.
	    ack = packets.front().state.sqn;
	    has_ack = false;
	    return true;
    }
};
void UpdateServer()
{
    // process client packets.
    PredictionPacket client_prediction;
    while(receivePredictionPacket(client_prediction))
    {
	    // validate client side predcition.
	    m_server_correction.applyPredicition(client_prediction);
    }
    // we have corrected the client prediction.
    PlayerState server_correction;
    if(m_server_correction.getCorrection(server_correction))
	    sendCorrection(server_correction);
    // client prediction is good.
    int ack;
    if(m_server_correction.getAcknowledgement(ack))
	    sendAcknowledgement(ack);
}

#3papalazaru

Posted 03 September 2012 - 08:47 AM

A very rough example of what client-side prediction would look like (code not tested, just showing the process of how client prediction / server correction would work).

<code>
// player actions.
struct PlayerInputs
{
float dt; // frame timestep
Vector mouselook;
Vector keymove;
u_int actions;
};
struct PlayerState
{
// current update sequence number.
int sqn;
Vector position;
Vector velocity;
Quaternion orientation;
Vector angularVelocity;
// update player state with a set of inputs.
void update(PlayerInputs inputs)
{
sqn++;
// do stuff with inputs. Move player position, jump, ect...
//.....
}
};
// a bundle of player inputs, and the subsequent player state calculated from those inputs.
struct PredictionPacket
{
PlayerInputs inputs;
PlayerState state;
};
struct ClientPrediction
{
// list of local predictions.
std::list<PredictionPacket> packets;
// add a new update at the end of the queue.
void recordUpdate(PlayerInputs inputs, PlayerState state);
{
packets.push_front(PredictionPacket(Inputs, state))
}
// server acknowledged our prediction.
void applyAcknowledgement(int ack)
{
// discard older packets that have been acknowledged.
while(!packets.empty() && (packets.back().state.sqn <= ack))
packets.pop_back();
}

// server sent us a correction.
PlayerState applyCorrection(PlayerState correction)
{
// discard older packets.
while(!packets.empty() && (packets.back().state.sqn < correction.sqn))
packets.pop_back();
// apply the correction for that sequence number.
if(!packets.empty() && (packets.back().state.sqn == correction.sqn))
{
// that's the state we should be in at that sequence number.
packets.back().state = correction;
// correct states up to the latest, current state.
for(std::list<PredictionPacket>::reverse_iterator it = packets.rbegin(); it != packets.rend(); ++it)
{
correction.update((*it).inputs);
(*it).state = correction;
}
}
// that's the latest state we should be in.
return correction;
}
};
void UpdateClient(PlayerInputs inputs)
{
// player update
m_currentState.update(inputs);
m_prediction.recordUpdate(inputs, m_currentState);
// send prediction packets not yet acknowledge by server.
sendPrediction();
// process server acknwledgements, if we received any.
int ack;
if(receivedAcknowledgement(ack))
m_prediction.applyAcknowledgement(ack);
// process server corrections, if we received any.
PlayerState server_correction;
if(receiveCorrectionPacket(server_correction))
m_currentState = m_prediction.applyCorrection(server_correction);
}
struct ServerCorrection
{
// list of updates received from client.
std::list<PredictionPacket> packets;
// what we need to do after receiving a new update.
bool has_ack;
bool has_correction;
PlayerState server_correction;
// apply new client packet.
void applyPredicition(PredictionPacket client_prediction)
{
// discard older packets received from client.
while(!packets.empty() && (packets.back().state.sqn < ack))
packets.pop_back();
// empty queue.
if(packets.empty())
{
// not the first packet.
if(client_prediction.state.sqn != 0)
return;

// first packet in the list. always accept.
packets.push_front(client_prediction);

// reset flags.
has_ack = true;
has_correction = false;
}
// part of an unbroken queue.
else
{
// not the packet we expected. Block until the client sends us that packet.
if(client_prediction.state.sqn != (packets.front().state.sqn + 1))
return;
// calculate the state on server.
PlayerState server_prediction = packets.front().state;
server_prediction.update(client_prediction.inputs);

// no corrections currently pending.
// see if we can ack or re-correct.
if(!has_correction)
{
// new correction needed to be sent.
if(server_prediction != client_prediction.state)
{
has_correction = true;
server_correction = server_prediction;
}
// we can acknowledge the client prediction.
else
{
has_ack = true;
}
}
// add state at the end of the queue.
packets.push_front(PredictionPacket(client_prediction.inputs, server_prediction));
}
}
// get correction, if any are pending.
bool getCorrection(PlayerState& correction)
{
// no corrections pending.
if(!has_correction)
return false;
// send latest correction.
correction = server_correction;
has_correction = false;
return true;
}
// get ack, if any are pending.
bool getAcknowledgement(int& ack)
{
// no ack pending.
if(!has_ack)
return false;
// send latest ack.
ack = packets.front().state.sqn;
has_ack = false;
return true;
}
};
void UpdateServer()
{
// process client packets.
PredictionPacket client_prediction;
while(receivePredictionPacket(client_prediction))
{
// validate client side predcition.
m_server_correction.applyPredicition(client_prediction);
}
// we have corrected the client prediction.
PlayerState server_correction;
if(m_server_correction.getCorrection(server_correction))
sendCorrection(server_correction);
// client prediction is good.
int ack;
if(m_server_correction.getAcknowledgement(ack))
sendAcknowledgement(ack);
}
</code>

#2papalazaru

Posted 03 September 2012 - 08:46 AM

A very rough example of what client-side prediction would look like (code not tested, just showing the process of how client prediction / server correction would work).

// player actions.
struct PlayerInputs
{
float dt; // frame timestep
Vector mouselook;
Vector keymove;
u_int actions;
};
struct PlayerState
{
// current update sequence number.
int sqn;
Vector position;
Vector velocity;
Quaternion orientation;
Vector angularVelocity;
// update player state with a set of inputs.
void update(PlayerInputs inputs)
{
sqn++;
// do stuff with inputs. Move player position, jump, ect...
//.....
}
};
// a bundle of player inputs, and the subsequent player state calculated from those inputs.
struct PredictionPacket
{
PlayerInputs inputs;
PlayerState state;
};
struct ClientPrediction
{
// list of local predictions.
std::list<PredictionPacket> packets;
// add a new update at the end of the queue.
void recordUpdate(PlayerInputs inputs, PlayerState state);
{
packets.push_front(PredictionPacket(Inputs, state))
}
// server acknowledged our prediction.
void applyAcknowledgement(int ack)
{
// discard older packets that have been acknowledged.
while(!packets.empty() && (packets.back().state.sqn <= ack))
packets.pop_back();
}

// server sent us a correction.
PlayerState applyCorrection(PlayerState correction)
{
// discard older packets.
while(!packets.empty() && (packets.back().state.sqn < correction.sqn))
packets.pop_back();
// apply the correction for that sequence number.
if(!packets.empty() && (packets.back().state.sqn == correction.sqn))
{
// that's the state we should be in at that sequence number.
packets.back().state = correction;
// correct states up to the latest, current state.
for(std::list<PredictionPacket>::reverse_iterator it = packets.rbegin(); it != packets.rend(); ++it)
{
correction.update((*it).inputs);
(*it).state = correction;
}
}
// that's the latest state we should be in.
return correction;
}
};
void UpdateClient(PlayerInputs inputs)
{
// player update
m_currentState.update(inputs);
m_prediction.recordUpdate(inputs, m_currentState);
// send prediction packets not yet acknowledge by server.
sendPrediction();
// process server acknwledgements, if we received any.
int ack;
if(receivedAcknowledgement(ack))
m_prediction.applyAcknowledgement(ack);
// process server corrections, if we received any.
PlayerState server_correction;
if(receiveCorrectionPacket(server_correction))
m_currentState = m_prediction.applyCorrection(server_correction);
}
struct ServerCorrection
{
// list of updates received from client.
std::list<PredictionPacket> packets;
// what we need to do after receiving a new update.
bool has_ack;
bool has_correction;
PlayerState server_correction;
// apply new client packet.
void applyPredicition(PredictionPacket client_prediction)
{
// discard older packets received from client.
while(!packets.empty() && (packets.back().state.sqn < ack))
packets.pop_back();
// empty queue.
if(packets.empty())
{
// not the first packet.
if(client_prediction.state.sqn != 0)
return;

// first packet in the list. always accept.
packets.push_front(client_prediction);

// reset flags.
has_ack = true;
has_correction = false;
}
// part of an unbroken queue.
else
{
// not the packet we expected. Block until the client sends us that packet.
if(client_prediction.state.sqn != (packets.front().state.sqn + 1))
return;
// calculate the state on server.
PlayerState server_prediction = packets.front().state;
server_prediction.update(client_prediction.inputs);

// no corrections currently pending.
// see if we can ack or re-correct.
if(!has_correction)
{
// new correction needed to be sent.
if(server_prediction != client_prediction.state)
{
has_correction = true;
server_correction = server_prediction;
}
// we can acknowledge the client prediction.
else
{
has_ack = true;
}
}
// add state at the end of the queue.
packets.push_front(PredictionPacket(client_prediction.inputs, server_prediction));
}
}
// get correction, if any are pending.
bool getCorrection(PlayerState& correction)
{
// no corrections pending.
if(!has_correction)
return false;
// send latest correction.
correction = server_correction;
has_correction = false;
return true;
}
// get ack, if any are pending.
bool getAcknowledgement(int& ack)
{
// no ack pending.
if(!has_ack)
return false;
// send latest ack.
ack = packets.front().state.sqn;
has_ack = false;
return true;
}
};
void UpdateServer()
{
// process client packets.
PredictionPacket client_prediction;
while(receivePredictionPacket(client_prediction))
{
// validate client side predcition.
m_server_correction.applyPredicition(client_prediction);
}
// we have corrected the client prediction.
PlayerState server_correction;
if(m_server_correction.getCorrection(server_correction))
sendCorrection(server_correction);
// client prediction is good.
int ack;
if(m_server_correction.getAcknowledgement(ack))
sendAcknowledgement(ack);
}


#1papalazaru

Posted 03 September 2012 - 08:34 AM

<p>A very rough example of what client-side prediction would look like (code not tested, just showing the process of how client prediction / server correction would work).</p>
<p> </p>
<p>[code=auto:0]</p>
<p> </p>
<p>// player actions.<br />
struct PlayerInputs<br />
{ <br />
    float dt; // frame timestep<br />
    Vector mouselook; <br />
    Vector keymove; <br />
    u_int actions;<br />
};</p>
<p>struct PlayerState<br />
{   <br />
    // current update sequence number.<br />
    int sqn;<br />
    Vector position;   <br />
    Vector velocity;   <br />
    Quaternion orientation;   <br />
    Vector angularVelocity;</p>
<p>    // update player state with a set of inputs.<br />
    void update(PlayerInputs inputs)<br />
    {<br />
        sqn++;   <br />
        // do stuff with inputs. Move player position, jump, ect...<br />
        //.....<br />
    }<br />
};</p>
<p>// a bundle of player inputs, and the subsequent player state calculated from those inputs.<br />
struct PredictionPacket<br />
{<br />
    PlayerInputs inputs;<br />
    PlayerState state;<br />
};</p>
<p>struct ClientPrediction<br />
{<br />
    // list of local predictions.<br />
    std::list&lt;PredictionPacket&gt; packets;</p>
<p>    // add a new update at the end of the queue.<br />
    void recordUpdate(PlayerInputs inputs, PlayerState state);<br />
    {<br />
        packets.push_front(PredictionPacket(Inputs, state))<br />
    }</p>
<p>    // server acknowledged our prediction.<br />
    void applyAcknowledgement(int ack)<br />
    {<br />
        // discard older packets that have been acknowledged.<br />
        while(!packets.empty() &amp;&amp; (packets.back().state.sqn &lt;= ack))<br />
            packets.pop_back();           <br />
    }<br />
       <br />
    // server sent us a correction.<br />
    PlayerState applyCorrection(PlayerState correction)<br />
    {<br />
        // discard older packets.<br />
        while(!packets.empty() &amp;&amp; (packets.back().state.sqn &lt; correction.sqn))<br />
            packets.pop_back();</p>
<p>        // apply the correction for that sequence number.<br />
        if(!packets.empty() &amp;&amp; (packets.back().state.sqn == correction.sqn))<br />
        {<br />
            // that&#39;s the state we should be in at that sequence number.<br />
            packets.back().state = correction;</p>
<p>            // correct states up to the latest, current state.<br />
            for(std::list&lt;PredictionPacket&gt;::reverse_iterator it = packets.rbegin(); it != packets.rend(); ++it)<br />
            {<br />
                correction.update((*it).inputs);<br />
                (*it).state = correction;<br />
            }<br />
        }<br />
        // that&#39;s the latest state we should be in.<br />
        return correction;<br />
    }<br />
};</p>
<p>void UpdateClient(PlayerInputs inputs)<br />
{<br />
    // player update<br />
    m_currentState.update(inputs);<br />
    m_prediction.recordUpdate(inputs, m_currentState);</p>
<p>    // send prediction packets not yet acknowledge by server.<br />
    sendPrediction();</p>
<p>    // process server acknwledgements, if we received any.<br />
    int ack;<br />
    if(receivedAcknowledgement(ack))<br />
        m_prediction.applyAcknowledgement(ack);</p>
<p>    // process server corrections, if we received any.<br />
    PlayerState server_correction;<br />
    if(receiveCorrectionPacket(server_correction))<br />
        m_currentState = m_prediction.applyCorrection(server_correction);<br />
}</p>
<p>struct ServerCorrection<br />
{<br />
    // list of updates received from client.<br />
    std::list&lt;PredictionPacket&gt; packets;</p>
<p>    // what we need to do after receiving a new update.<br />
    bool has_ack;<br />
    bool has_correction;<br />
    PlayerState server_correction;</p>
<p>    // apply new client packet.<br />
    void applyPredicition(PredictionPacket client_prediction)<br />
    {<br />
        // discard older packets received from client.<br />
        while(!packets.empty() &amp;&amp; (packets.back().state.sqn &lt; ack))<br />
            packets.pop_back();   </p>
<p>        // empty queue.<br />
        if(packets.empty())<br />
        {<br />
            // not the first packet.<br />
            if(client_prediction.state.sqn != 0)<br />
                return;<br />
       <br />
            // first packet in the list. always accept.<br />
            packets.push_front(client_prediction);<br />
           <br />
            // reset flags.<br />
            has_ack = true;<br />
            has_correction = false;<br />
        }<br />
        // part of an unbroken queue.<br />
        else<br />
        {<br />
            // not the packet we expected. Block until the client sends us that packet.<br />
            if(client_prediction.state.sqn != (packets.front().state.sqn + 1))<br />
                return;</p>
<p>            // calculate the state on server.<br />
            PlayerState server_prediction = packets.front().state;<br />
            server_prediction.update(client_prediction.inputs);<br />
           <br />
            // no corrections currently pending.<br />
            // see if we can ack or re-correct.<br />
            if(!has_correction)<br />
            {<br />
                // new correction needed to be sent.<br />
                if(server_prediction != client_prediction.state)<br />
                {<br />
                    has_correction = true;<br />
                    server_correction = server_prediction;<br />
                }<br />
                // we can acknowledge the client prediction.<br />
                else<br />
                {<br />
                    has_ack = true;<br />
                }<br />
            }<br />
            // add state at the end of the queue.<br />
            packets.push_front(PredictionPacket(client_prediction.inputs, server_prediction));<br />
&nbsp

PARTNERS