Skip to content

Commit

Permalink
Added Smoother: calculating player movement paths
Browse files Browse the repository at this point in the history
de::Smoother is a class whose purpose is to reproduce a smooth
movement path out of a handful of distinct points.

The current implementation uses a simple linear interpolation.
It is used on the server to reproduce the client movement,
and on the client to smooth out the movement of clmobjs
belonging to remote players.

The Smoother class is part of the public API.
  • Loading branch information
skyjake committed Jul 22, 2011
1 parent 7de69a3 commit 45c6ab7
Show file tree
Hide file tree
Showing 16 changed files with 449 additions and 73 deletions.
2 changes: 2 additions & 0 deletions doomsday/engine/api/doomsday.h
Expand Up @@ -67,6 +67,7 @@ extern "C" {

#include "dd_share.h"
#include "dd_plugin.h"
#include "smoother.h"

// Base.
void DD_AddIWAD(const char* path);
Expand Down Expand Up @@ -209,6 +210,7 @@ extern "C" {
int Net_GetTicCmd(void* command, int player);
const char* Net_GetPlayerName(int player);
ident_t Net_GetPlayerID(int player);
Smoother* Net_PlayerSmoother(int player);

// Play.
float P_AccurateDistance(float dx, float dy);
Expand Down
94 changes: 94 additions & 0 deletions doomsday/engine/api/smoother.h
@@ -0,0 +1,94 @@
/**\file
*\section License
* License: GPL
* Online License Link: http://www.gnu.org/licenses/gpl.html
*
*\author Copyright © 2011 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/

#ifndef __DOOMSDAY_SMOOTHER_H__
#define __DOOMSDAY_SMOOTHER_H__

#ifdef __cplusplus
extern "C" {
#endif

struct smoother_s; // The smoother instance (opaque).
typedef struct smoother_s Smoother;

/**
* Construct a new smoother instance.
*
* @return The smoother.
*/
Smoother* Smoother_New();

/**
* Destructs a smoother. This must be called when the smoother is no
* longer needed.
*/
void Smoother_Destruct(Smoother* sm);

/**
* Resets the smoother instance.
*/
void Smoother_Clear(Smoother* sm);

/**
* Defines a new point in the future of the smoother.
*
* @param sm Smoother instance.
* @param time Point in time (game tick).
* @param x Cooordinate.
* @param y Cooordinate.
* @param z Cooordinate.
* @param onFloor @c true if the z coordinate should be on the floor plane.
*/
void Smoother_AddPos(Smoother* sm, float time, float x, float y, float z, boolean onFloor);

/**
* Calculates the coordinates for the current point in time.
*
* @param sm Smoother instance.
* @param xyz The coordinates are written here. Must have room for 3 values.
*
* @return @c true if the evaluation was successful. When @c false is returned,
* the values in @a xyz are not valid.
*/
boolean Smoother_Evaluate(const Smoother* sm, float* xyz);

/**
* Determines whether the smoother's Z coordinate is currently on the floor plane.
*/
boolean Smoother_IsOnFloor(const Smoother* sm);

/**
* Determines whether the smoother is currently undergoing movement.
*/
boolean Smoother_IsMoving(const Smoother* sm);

/**
* Advances the smoother @a sm by @a period amount of time.
*/
void Smoother_Advance(Smoother* sm, float period);

#ifdef __cplusplus
} // extern "C"
#endif

#endif // __DOOMSDAY_SMOOTHER_H__
2 changes: 1 addition & 1 deletion doomsday/engine/portable/include/cl_def.h
Expand Up @@ -44,7 +44,7 @@ extern int clientPaused;
void Cl_InitID(void);
void Cl_CleanUp(void);
void Cl_GetPackets(void);
void Cl_Ticker(void);
void Cl_Ticker(timespan_t ticLength);
int Cl_GameReady(void);
void Cl_SendHello(void);

Expand Down
1 change: 1 addition & 0 deletions doomsday/engine/portable/include/de_misc.h
Expand Up @@ -44,5 +44,6 @@
#include "m_binarytree.h"
#include "m_gridmap.h"
#include "m_decomp64.h"
#include "smoother.h"

#endif
12 changes: 8 additions & 4 deletions doomsday/engine/portable/include/net_main.h
Expand Up @@ -36,6 +36,7 @@
#include "net_msg.h"
#include "p_mapdata.h"
#include "con_decl.h"
#include "smoother.h"

#define BIT(x) (1 << (x))

Expand Down Expand Up @@ -175,19 +176,19 @@ typedef struct {

// Client-reported time of the last processed ticcmd.
// Older or as old tics than this are discarded.
int runTime;
//int runTime;

// Bandwidth rating for connection. Determines how much information
// can be sent to the client. Determined dynamically.
int bandwidthRating;

// During the adjust period, raising the BWR is allowed (hitting max
// frame size).
int bwrAdjustTime;
//int bwrAdjustTime;

// A record of the past few acknowledgement times.
uint ackTimes[NUM_ACK_TIMES];
int ackIdx;
//uint ackTimes[NUM_ACK_TIMES];
//int ackIdx;

// Clients use this to determine how long ago they received the
// last update of this client.
Expand Down Expand Up @@ -226,6 +227,9 @@ typedef struct {
boolean recording;
boolean recordPaused;

// Movement smoother.
Smoother* smoother;

// View console. Which player this client is viewing?
int viewConsole;
} client_t;
Expand Down
6 changes: 3 additions & 3 deletions doomsday/engine/portable/include/sv_def.h
Expand Up @@ -32,8 +32,8 @@
#include "dd_def.h"
#include "m_string.h"

#define SV_VERSION 10
#define SV_WELCOME_STRING "Doomsday "DOOMSDAY_VERSION_TEXT" Server (R10)"
#define SV_VERSION 11
#define SV_WELCOME_STRING "Doomsday "DOOMSDAY_VERSION_TEXT" Server (R11)"

// Anything closer than this is always taken into consideration when
// deltas are being generated.
Expand All @@ -56,7 +56,7 @@ void Sv_Handshake(int playernum, boolean newplayer);
void Sv_GetPackets(void);
void Sv_SendText(int to, int conFlags, char* text);
//void Sv_FixLocalAngles(boolean clearFixAnglesFlag);
void Sv_Ticker(void);
void Sv_Ticker(timespan_t ticLength);
int Sv_Latency(byte cmdTime);
void Sv_Kick(int who);
void Sv_GetInfo(serverinfo_t* info);
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/portable/include/sv_frame.h
Expand Up @@ -29,7 +29,7 @@
#ifndef __DOOMSDAY_SERVER_FRAME_H__
#define __DOOMSDAY_SERVER_FRAME_H__

extern int bwrAdjustTime;
//extern int bwrAdjustTime;

void Sv_TransmitFrame(void);
size_t Sv_GetMaxFrameSize(int playerNumber);
Expand Down
2 changes: 1 addition & 1 deletion doomsday/engine/portable/src/cl_main.c
Expand Up @@ -406,7 +406,7 @@ void Cl_Assertions(int plrNum)
/**
* Client-side game ticker.
*/
void Cl_Ticker(void)
void Cl_Ticker(timespan_t ticLength)
{
int i;

Expand Down
17 changes: 5 additions & 12 deletions doomsday/engine/portable/src/dd_loop.c
Expand Up @@ -347,21 +347,14 @@ void DD_Ticker(timespan_t time)
// Game logic.
gx.Ticker(time);

if(isClient)
Cl_Ticker(time);
else
Sv_Ticker(time);

if(DD_IsSharpTick())
{
// A new 35 Hz tick begins.
/**
* Server ticks.
*
* These are placed here because they still rely on fixed ticks
* and thus it's best to keep them in sync with the fixed game
* ticks.
*/
if(isClient)
Cl_Ticker( /* time */ );
else
Sv_Ticker( /* time */ );

// Set frametime back by one tick (to stay in the 0..1 range).
realFrameTimePos -= 1;
//assert(realFrameTimePos < 1);
Expand Down
65 changes: 33 additions & 32 deletions doomsday/engine/portable/src/net_main.c
Expand Up @@ -285,6 +285,17 @@ boolean Net_GetPacket(void)
return true;
}

/**
* Provides access to the player's movement smoother.
*/
Smoother* Net_PlayerSmoother(int player)
{
if(player < 0 || player >= DDMAXPLAYERS)
return 0;

return clients[player].smoother;
}

/**
* This is the public interface of the message sender.
*/
Expand Down Expand Up @@ -484,8 +495,9 @@ static void Net_DoUpdate(void)
{
mobj_t *mo = ddPlayers[consolePlayer].shared.mo;

coordTimer = 1; //netCoordTime; // 35/2
coordTimer = 2; //netCoordTime; // 35/2
Msg_Begin(PKT_COORDS);
Msg_WriteFloat(gameTime);
Msg_WriteFloat(mo->pos[VX]);
Msg_WriteFloat(mo->pos[VY]);
if(mo->pos[VZ] == mo->floorZ)
Expand Down Expand Up @@ -566,46 +578,27 @@ void Net_BuildLocalCommands(timespan_t time)
*/
void Net_AllocArrays(void)
{
int i;
int i;

#if 0
// Local ticcmds are stored into this array before they're copied
// to netplayer[0]'s ticcmds buffer.
localticcmds = M_Calloc(LOCALTICS * TICCMD_SIZE);
numlocal = 0; // Nothing in the buffer.
#endif
memset(clients, 0, sizeof(clients));

for(i = 0; i < DDMAXPLAYERS; ++i)
{
memset(clients + i, 0, sizeof(clients[i]));
// The server stores ticcmds sent by the clients to these
// buffers.
//clients[i].ticCmds = M_Calloc(BACKUPTICS * TICCMD_SIZE);
// The last cmd that was executed is stored here.
//clients[i].lastCmd = M_Calloc(TICCMD_SIZE);
//clients[i].aggregateCmd = M_Calloc(TICCMD_SIZE);
clients[i].runTime = -1;
// Movement smoother.
clients[i].smoother = Smoother_New();
}
}

void Net_DestroyArrays(void)
{
int i;

#if 0
M_Free(localticcmds);
localticcmds = NULL;
#endif
int i;

for(i = 0; i < DDMAXPLAYERS; ++i)
{
//M_Free(clients[i].ticCmds);
//M_Free(clients[i].lastCmd);
//M_Free(clients[i].aggregateCmd);
//clients[i].ticCmds = NULL;
//clients[i].lastCmd = NULL;
//clients[i].aggregateCmd = NULL;
Smoother_Destruct(clients[i].smoother);
}

memset(clients, 0, sizeof(clients));
}

/**
Expand Down Expand Up @@ -854,6 +847,7 @@ void Net_Drawer(void)
*/
void Net_SetAckTime(int clientNumber, uint period)
{
/*
client_t *client = &clients[clientNumber];
// Add the new time into the array.
Expand All @@ -864,13 +858,15 @@ void Net_SetAckTime(int clientNumber, uint period)
VERBOSE( Con_Printf("Net_SetAckTime: Client %i, new ack sample of %05u ms.\n",
clientNumber, period) );
#endif
*/
}

/**
* @return The average ack time of the client.
*/
uint Net_GetAckTime(int clientNumber)
{
/*
client_t *client = &clients[clientNumber];
uint average = 0;
int i, count = 0;
Expand Down Expand Up @@ -904,20 +900,23 @@ uint Net_GetAckTime(int clientNumber)
else
{
return client->ackTimes[0];
}
}*/
return 0;
}

/**
* Sets all the ack times. Used to initial the ack times for new clients.
*/
void Net_SetInitialAckTime(int clientNumber, uint period)
{
/*
int i;
for(i = 0; i < NUM_ACK_TIMES; ++i)
{
clients[clientNumber].ackTimes[i] = period;
}
*/
}

/**
Expand All @@ -926,6 +925,7 @@ void Net_SetInitialAckTime(int clientNumber, uint period)
*/
uint Net_GetAckThreshold(int clientNumber)
{
/*
uint threshold =
Net_GetAckTime(clientNumber) * ACK_THRESHOLD_MUL;
Expand All @@ -934,7 +934,8 @@ uint Net_GetAckThreshold(int clientNumber)
threshold = ACK_MINIMUM_THRESHOLD;
}
return threshold;
return threshold;*/
return 0;
}

void Net_Ticker(void /*timespan_t time*/)
Expand All @@ -957,11 +958,11 @@ void Net_Ticker(void /*timespan_t time*/)
if(Sv_IsFrameTarget(i))
{
Con_Message("%i(rdy%i): avg=%05ims thres=%05ims "
"bwr=%05i (adj:%i) maxfs=%05lub unakd=%05i\n", i,
"bwr=%05i maxfs=%05lub unakd=%05i\n", i,
clients[i].ready, Net_GetAckTime(i),
Net_GetAckThreshold(i),
clients[i].bandwidthRating,
clients[i].bwrAdjustTime,
/*clients[i].bwrAdjustTime,*/
(unsigned long) Sv_GetMaxFrameSize(i),
Sv_CountUnackedDeltas(i));
}
Expand Down

0 comments on commit 45c6ab7

Please sign in to comment.