diff --git a/source/core/gameinput.cpp b/source/core/gameinput.cpp
index abf235326..b13d38916 100644
--- a/source/core/gameinput.cpp
+++ b/source/core/gameinput.cpp
@@ -194,6 +194,7 @@ void getInput(const double scaleAdjust, PlayerAngles* const plrAngles, InputPack
 
 	if (packet)
 	{
+		inputBuffer.actions |= gi->GetNeededInputBits();
 		*packet = inputBuffer;
 		clearLocalInputBuffer();
 	}
diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h
index 7eda22169..001276c50 100644
--- a/source/core/gamestruct.h
+++ b/source/core/gamestruct.h
@@ -120,6 +120,7 @@ struct GameInterface
 	virtual void RemoveQAVInterpProps(const int res_id) { }
 	virtual bool WantEscape() { return false; }
 	virtual void StartSoundEngine() = 0;
+	virtual ESyncBits GetNeededInputBits() = 0;
 	virtual void GetInput(HIDInput* const hidInput, InputPacket* const inputBuffer, InputPacket* const currInput, const double scaleAdjust)
 	{
 		processMovement(hidInput, inputBuffer, currInput, scaleAdjust);
diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp
index 086f589e8..20dff8e1d 100644
--- a/source/games/blood/src/blood.cpp
+++ b/source/games/blood/src/blood.cpp
@@ -412,18 +412,6 @@ int GameInterface::GetCurrentSkill()
 
 void GameInterface::Ticker()
 {
-	for (int i = connecthead; i >= 0; i = connectpoint2[i])
-	{
-		auto& inp = gPlayer[i].input;
-		auto oldactions = inp.actions;
-
-		inp = playercmds[i].ucmd;
-		inp.actions |= oldactions & ~(SB_BUTTON_MASK | SB_RUN | SB_WEAPONMASK_BITS);  // should be everything non-button and non-weapon
-
-		int newweap = inp.getNewWeapon();
-		if (newweap > 0 && newweap <= WeaponSel_MaxBlood) gPlayer[i].newWeapon = newweap;
-	}
-
 	BloodSpriteIterator it;
 	while (DBloodActor* act = it.Next()) act->interpolated = false;
 
@@ -442,6 +430,7 @@ void GameInterface::Ticker()
 
 		for (int i = connecthead; i >= 0; i = connectpoint2[i])
 		{
+			gPlayer[i].input = playercmds[i].ucmd;
 			gPlayer[i].Angles.resetCameraAngles();
 			viewBackupView(i);
 			playerProcess(&gPlayer[i]);
diff --git a/source/games/blood/src/blood.h b/source/games/blood/src/blood.h
index 797eb61e9..b88235542 100644
--- a/source/games/blood/src/blood.h
+++ b/source/games/blood/src/blood.h
@@ -145,6 +145,7 @@ struct GameInterface : public ::GameInterface
 	void AddQAVInterpProps(const int res_id, const FString& interptype, const bool loopable, const TMap<int, TArray<int>>&& ignoredata) override;
 	void RemoveQAVInterpProps(const int res_id) override;
 	void StartSoundEngine() override;
+	ESyncBits GetNeededInputBits() override { return gPlayer[myconnectindex].input.actions & ~(SB_BUTTON_MASK | SB_RUN | SB_WEAPONMASK_BITS); }
 
 	GameStats getStats() override;
 };
diff --git a/source/games/blood/src/weapon.cpp b/source/games/blood/src/weapon.cpp
index 16786f05e..0e66d4adc 100644
--- a/source/games/blood/src/weapon.cpp
+++ b/source/games/blood/src/weapon.cpp
@@ -2382,6 +2382,9 @@ void WeaponProcess(PLAYER* pPlayer) {
 	}
 #endif
 
+	int newweap = pPlayer->input.getNewWeapon();
+	if (newweap > 0 && newweap <= WeaponSel_MaxBlood) pPlayer->newWeapon = newweap;
+
 	if (pPlayer->actor->xspr.health == 0)
 	{
 		pPlayer->qavLoop = 0;
diff --git a/source/games/duke/src/duke3d.h b/source/games/duke/src/duke3d.h
index 060d8bed0..1f2ee82e6 100644
--- a/source/games/duke/src/duke3d.h
+++ b/source/games/duke/src/duke3d.h
@@ -39,6 +39,7 @@ struct GameInterface : public ::GameInterface
 	void SerializeGameState(FSerializer& arc) override;
 	void ExitFromMenu() override;
 	void DrawPlayerSprite(const DVector2& origin, bool onteam) override;
+	ESyncBits GetNeededInputBits() override { return ps[myconnectindex].sync.actions & SB_CENTERVIEW; }
 	void GetInput(HIDInput* const hidInput, InputPacket* const inputBuffer, InputPacket* const currInput, const double scaleAdjust) override;
 	void UpdateSounds() override;
 	void Startup() override;
diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp
index 21fb1f371..4dc73dd31 100644
--- a/source/games/duke/src/gameloop.cpp
+++ b/source/games/duke/src/gameloop.cpp
@@ -46,13 +46,6 @@ BEGIN_DUKE_NS
 
 void GameInterface::Ticker()
 {
-	// Make copies so that the originals do not have to be modified.
-	for (int i = 0; i < MAXPLAYERS; i++)
-	{
-		auto oldactions = ps[i].sync.actions;
-		ps[i].sync = playercmds[i].ucmd;
-		if (oldactions & SB_CENTERVIEW) ps[i].sync.actions |= SB_CENTERVIEW;
-	}
 	if (rtsplaying > 0) rtsplaying--;
 
 	if (show_shareware > 0)
@@ -72,6 +65,7 @@ void GameInterface::Ticker()
 		for (int i = connecthead; i >= 0; i = connectpoint2[i])
 		{
 			ps[i].Angles.resetCameraAngles();
+			ps[i].sync = playercmds[i].ucmd;
 		}
 
 		// disable synchronised input if set by game.
diff --git a/source/games/exhumed/src/exhumed.cpp b/source/games/exhumed/src/exhumed.cpp
index 8f9e5cae8..22e4696ad 100644
--- a/source/games/exhumed/src/exhumed.cpp
+++ b/source/games/exhumed/src/exhumed.cpp
@@ -377,10 +377,8 @@ void GameInterface::Ticker()
         // disable synchronised input if set by game.
         resetForcedSyncInput();
 
-        // set new player input, factoring in previous view centering.
-        const auto oldactions = pInput.actions;
+        // set new player input.
         pInput = playercmds[nLocalPlayer].ucmd;
-        if (oldactions & SB_CENTERVIEW) pInput.actions |= SB_CENTERVIEW;   
 
         const auto inputvect = DVector2(pInput.fvel, pInput.svel).Rotated(pPlayer->pActor->spr.Angles.Yaw) * 0.375;
 
diff --git a/source/games/exhumed/src/exhumed.h b/source/games/exhumed/src/exhumed.h
index 519c98561..7215be6b9 100644
--- a/source/games/exhumed/src/exhumed.h
+++ b/source/games/exhumed/src/exhumed.h
@@ -239,6 +239,7 @@ struct GameInterface : public ::GameInterface
     void processSprites(tspriteArray& tsprites, const DVector3& view, DAngle viewang, double interpfrac) override;
     int GetCurrentSkill() override;
     void StartSoundEngine() override;
+    ESyncBits GetNeededInputBits() override { return PlayerList[nLocalPlayer].input.actions & SB_CENTERVIEW; }
 
 	::GameStats getStats() override;
 };
diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp
index 724659674..051604fdf 100644
--- a/source/games/sw/src/game.cpp
+++ b/source/games/sw/src/game.cpp
@@ -715,15 +715,6 @@ int GameInterface::GetCurrentSkill()
 
 void GameInterface::Ticker(void)
 {
-    int i;
-    TRAVERSE_CONNECT(i)
-    {
-        auto pp = Player + i;
-        pp->lastinput = pp->input;
-        pp->input = playercmds[i].ucmd;
-        if (pp->lastinput.actions & SB_CENTERVIEW) pp->input.actions |= SB_CENTERVIEW;
-    }
-
     domovethings();
     r_NoInterpolate = paused;
 }
diff --git a/source/games/sw/src/game.h b/source/games/sw/src/game.h
index 560677e74..49a1b069f 100644
--- a/source/games/sw/src/game.h
+++ b/source/games/sw/src/game.h
@@ -1888,6 +1888,7 @@ struct GameInterface : public ::GameInterface
     void ExitFromMenu() override;
     int GetCurrentSkill() override;
     void StartSoundEngine() override;
+    ESyncBits GetNeededInputBits() override { return Player[myconnectindex].input.actions & SB_CENTERVIEW; }
     void GetInput(HIDInput* const hidInput, InputPacket* const inputBuffer, InputPacket* const currInput, const double scaleAdjust) override
     {
         processMovement(hidInput, inputBuffer, currInput, scaleAdjust, 0, !Player[myconnectindex].sop, Player[myconnectindex].sop_control ? 3. / 1.40625 : 1.);
diff --git a/source/games/sw/src/player.cpp b/source/games/sw/src/player.cpp
index 97c93c4e8..39d6d4d0f 100644
--- a/source/games/sw/src/player.cpp
+++ b/source/games/sw/src/player.cpp
@@ -6887,7 +6887,7 @@ void PauseMultiPlay(void)
 
 void domovethings(void)
 {
-    short i, pnum;
+    short pnum;
 
     PLAYER* pp;
     extern int FinishTimer;
@@ -6935,6 +6935,9 @@ void domovethings(void)
         pp = Player + pnum;
         GlobPlayerP = pp;
 
+        pp->lastinput = pp->input;
+        pp->input = playercmds[pnum].ucmd;
+
         if (pp->cookieTime)
         {
             pp->cookieTime -= synctics;