diff --git a/SRB2.cbp b/SRB2.cbp
index 2a1eb87b..acdc61c7 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -1545,6 +1545,10 @@ HW3SOUND for 3D hardware sound support
+
+
+
+
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 764fbcbe..5a016268 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -3025,6 +3025,9 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum)
}
}
+static CV_PossibleValue_t netticbuffer_cons_t[] = {{0, "MIN"}, {3, "MAX"}};
+consvar_t cv_netticbuffer = {"netticbuffer", "1", CV_SAVE, netticbuffer_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+
consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL };
#ifdef VANILLAJOINNEXTROUND
consvar_t cv_joinnextround = {"joinnextround", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; /// \todo not done
@@ -3076,6 +3079,7 @@ void D_ClientServerInit(void)
RegisterNetXCmd(XD_REMOVEPLAYER, Got_RemovePlayer);
#ifndef NONET
CV_RegisterVar(&cv_allownewplayer);
+ CV_RegisterVar(&cv_netticbuffer);
#ifdef VANILLAJOINNEXTROUND
CV_RegisterVar(&cv_joinnextround);
#endif
@@ -3978,7 +3982,8 @@ FILESTAMP
// Check ticcmd for "speed hacks"
if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
- || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+ || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE ||
+ netcmds[maketic%BACKUPTICS][netconsole].driftturn > KART_FULLTURN || netcmds[maketic%BACKUPTICS][netconsole].driftturn < -KART_FULLTURN)
{
XBOXSTATIC char buf[2];
CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
@@ -4933,6 +4938,10 @@ void TryRunTics(tic_t realtics)
ExtraDataTicker();
gametic++;
consistancy[gametic%BACKUPTICS] = Consistancy();
+
+ // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame.
+ if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value)
+ break;
}
}
else
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index f1abffaf..5cf72bbc 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -433,10 +433,10 @@ typedef struct
UINT8 reserved; // Padding
union
{
- clientcmd_pak clientpak; // 144 bytes
- client2cmd_pak client2pak; // 200 bytes
- client3cmd_pak client3pak; // 256 bytes(?)
- client4cmd_pak client4pak; // 312 bytes(?)
+ clientcmd_pak clientpak; // 145 bytes
+ client2cmd_pak client2pak; // 202 bytes
+ client3cmd_pak client3pak; // 258 bytes(?)
+ client4cmd_pak client4pak; // 316 bytes(?)
servertics_pak serverpak; // 132495 bytes (more around 360, no?)
serverconfig_pak servercfg; // 773 bytes
resynchend_pak resynchend; //
diff --git a/src/d_player.h b/src/d_player.h
index 2d9ed5c8..27fdef8d 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -416,6 +416,8 @@ typedef struct player_s
// SRB2kart stuff
INT32 kartstuff[NUMKARTSTUFF];
angle_t frameangle; // for the player add the ability to have the sprite only face other angles
+ INT16 lturn_max[MAXPREDICTTICS]; // What's the expected turn value for full-left for a number of frames back (to account for netgame latency)?
+ INT16 rturn_max[MAXPREDICTTICS]; // Ditto but for full-right
// Bit flags.
// See pflags_t, above.
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 7df2d412..dab758f8 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -21,6 +21,8 @@
#pragma interface
#endif
+#define MAXPREDICTTICS 12
+
// Button/action code definitions.
typedef enum
{
@@ -60,6 +62,7 @@ typedef struct
INT16 aiming; // vertical aiming, see G_BuildTicCmd
UINT16 buttons;
INT16 driftturn; // SRB2Kart: Used for getting drift turn speed
+ UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end?
} ATTRPACK ticcmd_t;
#if defined(_MSC_VER)
diff --git a/src/f_finale.c b/src/f_finale.c
index b398c0ef..0fe13a8c 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -438,7 +438,7 @@ static const char *credits[] = {
"\"ZarroTsu\"",
"",
"\1Support Programming",
- "\"fickle\"",
+ "Colette \"fickleheart\" Bordelon",
"\"Lat\'\"",
"\"Monster Iestyn\"",
"\"Shuffle\"",
diff --git a/src/g_game.c b/src/g_game.c
index 1e0c7e46..004ff62d 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1209,7 +1209,7 @@ boolean camspin, camspin2, camspin3, camspin4;
static fixed_t forwardmove[2] = {25<>16, 50<>16};
static fixed_t sidemove[2] = {2<>16, 4<>16};
-static fixed_t angleturn[3] = {400, 800, 200}; // + slow turn
+static fixed_t angleturn[3] = {KART_FULLTURN/2, KART_FULLTURN, KART_FULLTURN/4}; // + slow turn
void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
{
@@ -1352,27 +1352,27 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
// let movement keys cancel each other out
if (turnright && !(turnleft))
{
- cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed] * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn - (angleturn[tspeed] * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn - (angleturn[tspeed]));
+ cmd->driftturn = (INT16)(cmd->driftturn - (angleturn[tspeed]));
}
else if (turnleft && !(turnright))
{
- cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed] * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn + (angleturn[tspeed] * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn + (angleturn[tspeed]));
+ cmd->driftturn = (INT16)(cmd->driftturn + (angleturn[tspeed]));
}
if (analogjoystickmove && axis != 0)
{
// JOYAXISRANGE should be 1023 (divide by 1024)
- cmd->angleturn = (INT16)(cmd->angleturn - (((axis * angleturn[1]) >> 10) * realtics)); // ANALOG!
- cmd->driftturn = (INT16)(cmd->driftturn - (((axis * angleturn[1]) >> 10) * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn - (((axis * angleturn[1]) >> 10))); // ANALOG!
+ cmd->driftturn = (INT16)(cmd->driftturn - (((axis * angleturn[1]) >> 10)));
}
// Specator mouse turning
if (player->spectator)
{
- cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8) * realtics));
- cmd->driftturn = (INT16)(cmd->driftturn - ((mousex*(encoremode ? -1 : 1)*8) * realtics));
+ cmd->angleturn = (INT16)(cmd->angleturn - ((mousex*(encoremode ? -1 : 1)*8)));
+ cmd->driftturn = (INT16)(cmd->driftturn - ((mousex*(encoremode ? -1 : 1)*8)));
}
// Speed bump strafing
@@ -1549,19 +1549,21 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
//{ SRB2kart - Drift support
// Not grouped with the rest of turn stuff because it needs to know what buttons you're pressing for rubber-burn turn
// limit turning to angleturn[1] to stop mouselook letting you look too fast
- if (cmd->angleturn > (angleturn[1] * realtics))
- cmd->angleturn = (angleturn[1] * realtics);
- else if (cmd->angleturn < (-angleturn[1] * realtics))
- cmd->angleturn = (-angleturn[1] * realtics);
+ if (cmd->angleturn > (angleturn[1]))
+ cmd->angleturn = (angleturn[1]);
+ else if (cmd->angleturn < (-angleturn[1]))
+ cmd->angleturn = (-angleturn[1]);
- if (cmd->driftturn > (angleturn[1] * realtics))
- cmd->driftturn = (angleturn[1] * realtics);
- else if (cmd->driftturn < (-angleturn[1] * realtics))
- cmd->driftturn = (-angleturn[1] * realtics);
+ if (cmd->driftturn > (angleturn[1]))
+ cmd->driftturn = (angleturn[1]);
+ else if (cmd->driftturn < (-angleturn[1]))
+ cmd->driftturn = (-angleturn[1]);
if (player->mo)
cmd->angleturn = K_GetKartTurnValue(player, cmd->angleturn);
+ cmd->angleturn *= realtics;
+
// SRB2kart - no additional angle if not moving
if (((player->mo && player->speed > 0) // Moving
|| (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn
@@ -1571,6 +1573,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
lang += (cmd->angleturn<<16);
cmd->angleturn = (INT16)(lang >> 16);
+ cmd->latency = modeattacking ? 0 : (leveltime & 0xFF); // Send leveltime when this tic was generated to the server for control lag calculations
if (!hu_stopped)
{
@@ -2154,6 +2157,9 @@ void G_Ticker(boolean run)
players[i].kartstuff[k_throwdir] = 0;
G_CopyTiccmd(cmd, &netcmds[buf][i], 1);
+
+ // Use the leveltime sent in the player's ticcmd to determine control lag
+ cmd->latency = modeattacking ? 0 : min((leveltime & 0xFF) - cmd->latency, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max
}
}
@@ -4561,6 +4567,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
dest[i].aiming = (INT16)SHORT(src[i].aiming);
dest[i].buttons = (UINT16)SHORT(src[i].buttons);
dest[i].driftturn = (INT16)SHORT(src[i].driftturn);
+ dest[i].latency = (INT16)SHORT(src[i].latency);
}
return dest;
}
diff --git a/src/k_kart.c b/src/k_kart.c
index 89a974cf..f58be44a 100644
--- a/src/k_kart.c
+++ b/src/k_kart.c
@@ -5499,7 +5499,12 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (leveltime >= starttime-(2*TICRATE) && leveltime <= starttime)
{
if (cmd->buttons & BT_ACCELERATE)
+ {
+ if (player->kartstuff[k_boostcharge] == 0)
+ player->kartstuff[k_boostcharge] = cmd->latency;
+
player->kartstuff[k_boostcharge]++;
+ }
else
player->kartstuff[k_boostcharge] = 0;
}
@@ -8484,7 +8489,7 @@ void K_drawKartHUD(void)
{
UINT8 p;
for (p = 0; p < MAXPLAYERS; p++)
- V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d", p, playernode[p]));
+ V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency));
}
}
diff --git a/src/k_kart.h b/src/k_kart.h
index fed490db..3f712091 100644
--- a/src/k_kart.h
+++ b/src/k_kart.h
@@ -9,6 +9,8 @@
#include "doomdef.h"
#include "d_player.h" // Need for player_t
+#define KART_FULLTURN 800
+
UINT8 colortranslations[MAXSKINCOLORS][16];
extern const char *KartColor_Names[MAXSKINCOLORS];
extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2];
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 555a2614..975a4a5d 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -282,6 +282,12 @@ static void P_NetArchivePlayers(void)
WRITEUINT8(save_p, players[i].accelstart);
WRITEUINT8(save_p, players[i].acceleration);
WRITEFIXED(save_p, players[i].jumpfactor);
+
+ for (j = 0; j < MAXPREDICTTICS; j++)
+ {
+ WRITEINT16(save_p, players[i].lturn_max[j]);
+ WRITEINT16(save_p, players[i].rturn_max[j]);
+ }
}
}
@@ -456,6 +462,12 @@ static void P_NetUnArchivePlayers(void)
players[i].accelstart = READUINT8(save_p);
players[i].acceleration = READUINT8(save_p);
players[i].jumpfactor = READFIXED(save_p);
+
+ for (j = 0; j < MAXPREDICTTICS; j++)
+ {
+ players[i].lturn_max[j] = READINT16(save_p);
+ players[i].rturn_max[j] = READINT16(save_p);
+ }
}
}
diff --git a/src/p_user.c b/src/p_user.c
index ab6c61dc..65beab32 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -6633,8 +6633,53 @@ static void P_MovePlayer(player_t *player)
P_2dMovement(player);
else*/
{
- if (!player->climbing && (!P_AnalogMove(player)))
- player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
+ INT16 angle_diff, max_left_turn, max_right_turn;
+ boolean add_delta = true;
+
+ // Kart: store the current turn range for later use
+ if (((player->mo && player->speed > 0) // Moving
+ || (leveltime > starttime && (cmd->buttons & BT_ACCELERATE && cmd->buttons & BT_BRAKE)) // Rubber-burn turn
+ || (player->kartstuff[k_respawn]) // Respawning
+ || (player->spectator || objectplacing)) // Not a physical player
+ && !(player->kartstuff[k_spinouttimer] && player->kartstuff[k_sneakertimer])) // Spinning and boosting cancels out turning
+ {
+ player->lturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, KART_FULLTURN)+1;
+ player->rturn_max[leveltime%MAXPREDICTTICS] = K_GetKartTurnValue(player, -KART_FULLTURN)-1;
+ } else {
+ player->lturn_max[leveltime%MAXPREDICTTICS] = player->rturn_max[leveltime%MAXPREDICTTICS] = 0;
+ }
+
+ if (leveltime >= starttime)
+ {
+ // KART: Don't directly apply angleturn! It may have been either A) forged by a malicious client, or B) not be a smooth turn due to a player dropping frames.
+ // Instead, turn the player only up to the amount they're supposed to turn accounting for latency. Allow exactly 1 extra turn unit to try to keep old replays synced.
+ angle_diff = cmd->angleturn - (player->mo->angle>>16);
+ max_left_turn = player->lturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
+ max_right_turn = player->rturn_max[(leveltime + MAXPREDICTTICS - cmd->latency) % MAXPREDICTTICS];
+
+ //CONS_Printf("----------------\nangle diff: %d - turning options: %d to %d - ", angle_diff, max_left_turn, max_right_turn);
+
+ if (angle_diff > max_left_turn)
+ angle_diff = max_left_turn;
+ else if (angle_diff < max_right_turn)
+ angle_diff = max_right_turn;
+ else
+ {
+ // Try to keep normal turning as accurate to 1.0.1 as possible to reduce replay desyncs.
+ player->mo->angle = cmd->angleturn<<16;
+ add_delta = false;
+ }
+ //CONS_Printf("applied turn: %d\n", angle_diff);
+
+ if (add_delta) {
+ player->mo->angle += angle_diff<<16;
+ player->mo->angle &= ~0xFFFF; // Try to keep the turning somewhat similar to how it was before?
+ //CONS_Printf("leftover turn (%s): %5d or %4d%%\n",
+ // player_names[player-players],
+ // (INT16) (cmd->angleturn - (player->mo->angle>>16)),
+ // (INT16) (cmd->angleturn - (player->mo->angle>>16)) * 100 / (angle_diff ?: 1));
+ }
+ }
ticruned++;
if ((cmd->angleturn & TICCMD_RECEIVED) == 0)