diff --git a/src/b_bot.c b/src/b_bot.c
index cdd74fc0757522ac2a7c30dfe3dec50237c446ea..ffdcace6d6d428b88d54b765df66169bcdba541b 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -127,17 +127,17 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 	// Update catchup_tics
 	if (mem->thinkstate == AI_SPINFOLLOW)
 	{
-		mem-> catchup_tics = 0;
+		mem->catchup_tics = 0;
 	}
 	else if (dist > followmax || zdist > comfortheight || stalled)
 	{
-		mem-> catchup_tics = min(mem-> catchup_tics + 2, 70);
-		if (mem-> catchup_tics >= 70)
+		mem->catchup_tics = min(mem->catchup_tics + 2, 70);
+		if (mem->catchup_tics >= 70)
 			mem->thinkstate = AI_CATCHUP;
 	}
 	else
 	{
-		mem-> catchup_tics = max(mem-> catchup_tics - 1, 0);
+		mem->catchup_tics = max(mem->catchup_tics - 1, 0);
 		if (mem->thinkstate == AI_CATCHUP)
 			mem->thinkstate = AI_FOLLOW;
 	}
@@ -171,7 +171,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			{
 				jump = true;
 				mem->thinkstate = AI_FLYSTANDBY;
-				bot->pflags |= PF_CANCARRY;
 			}
 		}
 
@@ -183,7 +182,10 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
 			&& !(player->pflags & PF_STASIS)
 			&& bot->charability == CA_FLY)
-				mem->thinkstate = AI_THINKFLY;
+		{
+			mem->thinkstate = AI_THINKFLY;
+			cmd->flags |= TCF_FLIGHTINDICATOR;
+		}
 		else if (mem->thinkstate == AI_THINKFLY)
 			mem->thinkstate = AI_FOLLOW;
 
@@ -204,6 +206,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			// Abort if the player moves away or spins
 			if (dist > followthres || player->dashspeed)
 				mem->thinkstate = AI_FOLLOW;
+			else
+				cmd->flags |= TCF_SETCARRY;
 		}
 		// Read player inputs while carrying
 		else if (mem->thinkstate == AI_FLYCARRY)
@@ -312,7 +316,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		{
 			// Copy inputs
 			cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
-			bot->drawangle = ang;
 			cmd->forwardmove = 8 * pcmd->forwardmove / 10;
 			cmd->sidemove = 8 * pcmd->sidemove / 10;
 		}
@@ -339,7 +342,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
 			&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
 				|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
-				|| (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
+				|| (stalled && mem->catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
 				//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
 				|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
 					jump = true;
@@ -385,7 +388,6 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 		return;
 
 	// Make sure we have a valid main character to follow
-	 B_UpdateBotleader(player);
 	if (!player->botleader)
 		return;
 
@@ -398,7 +400,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 {
 	player_t *player = mo->player;
 	// don't try to do stuff if your sonic is in a minecart or something
-	if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
+	if (player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
 		return;
 	// Turn the virtual keypresses into ticcmd_t.
 	if (twodlevel || mo->flags2 & MF2_TWOD) {
@@ -587,26 +589,43 @@ void B_RespawnBot(INT32 playernum)
 void B_HandleFlightIndicator(player_t *player)
 {
 	mobj_t *tails = player->mo;
-	botmem_t *mem = &player->botmem;
+	boolean shouldExist;
+
 	if (!tails)
 		return;
 
-	if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health)
+	shouldExist = (player->cmd.flags & TCF_FLIGHTINDICATOR) && player->botleader
+		&& player->bot == BOT_2PAI && player->playerstate == PST_LIVE;
+
+	// check whether the indicator doesn't exist
+	if (P_MobjWasRemoved(tails->hnext))
 	{
-		if (!tails->hnext)
-		{
-			P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
-			if (tails->hnext)
-			{
-				P_SetTarget(&tails->hnext->target, tails);
-				P_SetTarget(&tails->hnext->hprev, tails);
-				P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
-			}
-		}
+		// if it shouldn't exist, everything is fine
+		if (!shouldExist)
+			return;
+
+		// otherwise, spawn it
+		P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
+		P_SetTarget(&tails->hnext->target, tails);
+		P_SetTarget(&tails->hnext->hprev, tails);
+		P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
 	}
-	else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
+
+	// if the mobj isn't a flight indicator, let's not mess with it
+	if (tails->hnext->type != MT_OVERLAY || (tails->hnext->state != states+S_FLIGHTINDICATOR))
+		return;
+
+	// if it shouldn't exist, remove it
+	if (!shouldExist)
 	{
 		P_RemoveMobj(tails->hnext);
 		P_SetTarget(&tails->hnext, NULL);
+		return;
 	}
+
+	// otherwise, update its visibility
+	if (P_IsLocalPlayer(player->botleader))
+		tails->hnext->flags2 &= ~MF2_DONTDRAW;
+	else
+		tails->hnext->flags2 |= MF2_DONTDRAW;
 }
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 78a3ebe6cb961cff2fb22e5e0f4520559078865a..c70532494ca7b261eec594fec1875abb8014bc8b 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -46,6 +46,7 @@
 #include "lua_libs.h"
 #include "md5.h"
 #include "m_perfstats.h"
+#include "b_bot.h" // B_BuildTiccmd
 
 #ifndef NONET
 // cl loading screen
@@ -5166,6 +5167,25 @@ static void Local_Maketic(INT32 realtics)
 	localcmds2.angleturn |= TICCMD_RECEIVED;
 }
 
+static void SV_MakeBotTics(void)
+{
+	UINT8 i;
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+
+		if (players[i].bot == BOT_2PAI || players[i].bot == BOT_MPAI)
+		{
+			ticcmd_t *cmd = &netcmds[maketic % BACKUPTICS][i];
+
+			G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
+			B_BuildTiccmd(&players[i], cmd);
+			cmd->angleturn |= TICCMD_RECEIVED;
+		}
+	}
+}
+
 // create missed tic
 static void SV_Maketic(void)
 {
@@ -5409,6 +5429,15 @@ void NetUpdate(void)
 	if (client)
 		maketic = neededtic;
 
+	// update players' lastbuttons so they can be used in ticcmd generation
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (!playeringame[i])
+			continue;
+
+		players[i].lastbuttons = players[i].cmd.buttons;
+	}
+
 	Local_Maketic(realtics); // make local tic, and call menu?
 
 	if (server)
@@ -5452,6 +5481,8 @@ void NetUpdate(void)
 						Net_ConnectionTimeout(i);
 				}
 
+			SV_MakeBotTics();
+
 			// Don't erase tics not acknowledged
 			counts = realtics;
 
diff --git a/src/d_ticcmd.h b/src/d_ticcmd.h
index 182b30e6aef84b9e4148157594918be325c75095..480fbe2d4ca05e3e8e8c67ac63ed32e78d1285e0 100644
--- a/src/d_ticcmd.h
+++ b/src/d_ticcmd.h
@@ -45,6 +45,14 @@ typedef enum
 	BT_CUSTOM3    = 1<<15,
 } buttoncode_t;
 
+// ticcmd flags
+typedef enum
+{
+	TCF_FLIGHTINDICATOR = 1 << 0, // show flight indicator
+	TCF_SETCARRY        = 1 << 1, // set PF_CARRY upon activating flight
+	// free up to and including 1 << 7
+} ticcmdflag_t;
+
 // The data sampled per tick (single player)
 // and transmitted to other peers (multiplayer).
 // Mainly movements/button commands per game tick,
@@ -66,6 +74,7 @@ typedef struct
 	INT16 aiming; // vertical aiming, see G_BuildTicCmd
 	UINT16 buttons;
 	UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end?
+	UINT8 flags; // miscellaneous info
 } ATTRPACK ticcmd_t;
 
 #if defined(_MSC_VER)
diff --git a/src/deh_tables.c b/src/deh_tables.c
index f30f7c14dbca4ecf514976077977029c4149d86c..3aece916cbebbaf8220d6aa8a65c960ce96f16b8 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -5333,6 +5333,11 @@ struct int_const_s const INT_CONST[] = {
 	{"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable
 	{"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable
 
+	// Ticcmd flags (ticcmdflag_t)
+	// (maybe move these into their own table in the future but I cba when there's only 2 LOL)
+	{"TCF_FLIGHTINDICATOR", TCF_FLIGHTINDICATOR},
+	{"TCF_SETCARRY", TCF_SETCARRY},
+
 	// Lua command registration flags
 	{"COM_ADMIN",COM_ADMIN},
 	{"COM_SPLITSCREEN",COM_SPLITSCREEN},
diff --git a/src/g_demo.c b/src/g_demo.c
index c97dbcf9ee0559e23e6635e4a62f2de1cb843af2..2e2f6d56a6bc712038a2373ce4d42beaa67f7311 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -110,6 +110,7 @@ demoghost *ghosts = NULL;
 #define ZT_BUTTONS 0x08
 #define ZT_AIMING  0x10
 #define ZT_LATENCY 0x20
+#define ZT_FLAGS   0x40
 #define DEMOMARKER 0x80 // demoend
 #define METALDEATH 0x44
 #define METALSNICE 0x69
@@ -184,6 +185,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 		oldcmd.aiming = READINT16(demo_p);
 	if (ziptic & ZT_LATENCY)
 		oldcmd.latency = READUINT8(demo_p);
+	if (ziptic & ZT_FLAGS)
+		oldcmd.flags = READUINT8(demo_p);
 
 	G_CopyTiccmd(cmd, &oldcmd, 1);
 	players[playernum].angleturn = cmd->angleturn;
@@ -248,6 +251,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 		ziptic |= ZT_LATENCY;
 	}
 
+	if (cmd->flags != oldcmd.flags)
+	{
+		WRITEUINT8(demo_p, cmd->flags);
+		oldcmd.flags = cmd->flags;
+		ziptic |= ZT_FLAGS;
+	}
+
 	*ziptic_p = ziptic;
 
 	// attention here for the ticcmd size!
@@ -691,6 +701,8 @@ void G_GhostTicker(void)
 			g->p += 2;
 		if (ziptic & ZT_LATENCY)
 			g->p++;
+		if (ziptic & ZT_FLAGS)
+			g->p++;
 
 		// Grab ghost data.
 		ziptic = READUINT8(g->p);
diff --git a/src/g_game.c b/src/g_game.c
index 3955834b2170fa203222a5c76f8f9ead60e74fa6..050f2d714857f2927b8b69dfd150cf41aeaa851b 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1546,12 +1546,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	cmd->forwardmove = (SINT8)(cmd->forwardmove + forward);
 	cmd->sidemove = (SINT8)(cmd->sidemove + side);
 
-	// Note: Majority of botstuffs are handled in G_Ticker now.
-	if (player->bot == BOT_2PHUMAN) //Player-controlled bot
+	// Note: Majority of botstuffs are handled in G_Ticker and NetUpdate now.
+	if (player->bot == BOT_2PAI
+		&& !player->powers[pw_tailsfly]
+		&& (cmd->forwardmove || cmd->sidemove || cmd->buttons))
 	{
-		// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Strafe
-		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
-	}	
+		player->bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
+		CV_SetValue(&cv_analog[1], true);
+	}
+
+	if (player->bot == BOT_2PHUMAN)
+		cmd->angleturn = (localangle - *myangle) >> 16;
 	
 	*myangle += (cmd->angleturn<<16);
 
@@ -1705,6 +1710,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].latency = src[i].latency;
+		dest[i].flags = src[i].flags;
 	}
 	return dest;
 }
@@ -2312,57 +2318,32 @@ void G_Ticker(boolean run)
 		if (playeringame[i])
 		{
 			INT16 received;
-			// Save last frame's button readings
-			players[i].lastbuttons = players[i].cmd.buttons;
 
 			G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
-			// Bot ticcmd handling
-			// Yes, ordinarily this would be handled in G_BuildTiccmd...
-			// ...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information.
-			// Therefore, this has to be done after ticcmd sends are received.
-			if (players[i].bot == BOT_2PAI) { // Tailsbot for P2
-				if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons))
-				{
-					players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
-					CV_SetValue(&cv_analog[1], true);
-				}
-				else
-				{
-					B_BuildTiccmd(&players[i], &players[i].cmd);
-				}
-				B_HandleFlightIndicator(&players[i]);
-			}
-			else if (players[i].bot == BOT_MPAI) {
-				B_BuildTiccmd(&players[i], &players[i].cmd);
-			}
-			
-			// Do angle adjustments.
+
 			if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
 			{
+				// Use the leveltime sent in the player's ticcmd to determine control lag
+				players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
+
+				// Do angle adjustments.
 				received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
+
 				players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
 				players[i].oldrelangleturn = players[i].cmd.angleturn;
 				if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
 					P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
 				else
 					players[i].cmd.angleturn = players[i].angleturn;
-    			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
-    				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
-    			else
-    				players[i].cmd.angleturn = players[i].angleturn;
-    
-    			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
-				// Use the leveltime sent in the player's ticcmd to determine control lag
-    			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
 			}
 			else // Less work is required if we're building a bot ticcmd.
 			{
-    			// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
-    			received = 1;
-    			players[i].cmd.latency = 0;
-				players[i].angleturn = players[i].cmd.angleturn;
-				players[i].oldrelangleturn = players[i].cmd.angleturn;
+				// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
+				players[i].cmd.latency = 0;
+				P_SetPlayerAngle(&players[i], players[i].cmd.angleturn << 16);
 			}
+
+			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
 			players[i].cmd.angleturn |= received;
 		}
 	}
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 12ad4fee0549bbaad9d5ea2dd3106c9bd9b8f955..e0d09dee9cff518d6c8b4071cfbf270f016399f5 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -32,6 +32,7 @@
 #include "b_bot.h" // B_UpdateBotleader
 #include "d_clisrv.h" // CL_RemovePlayer
 #include "i_system.h" // I_GetPreciseTime, I_PreciseToMicros
+#include "i_net.h" // doomcom
 
 #include "lua_script.h"
 #include "lua_libs.h"
@@ -3463,6 +3464,8 @@ static int lib_gAddPlayer(lua_State *L)
 
 	playeringame[newplayernum] = true;
 	G_AddPlayer(newplayernum);
+	if (newplayernum+1 > doomcom->numslots)
+		doomcom->numslots = (INT16)(newplayernum+1);
 	newplayer = &players[newplayernum];
 
 	newplayer->jointime = 0;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index 1c634da45e233f5b55afdfe3320aedf508fc5cee..097fd94e4fe7138aced6ef06c0c65281c01a34ed 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -833,6 +833,8 @@ static int ticcmd_get(lua_State *L)
 		lua_pushinteger(L, cmd->buttons);
 	else if (fastcmp(field,"latency"))
 		lua_pushinteger(L, cmd->latency);
+	else if (fastcmp(field,"flags"))
+		lua_pushinteger(L, cmd->flags);
 	else
 		return NOFIELD;
 
@@ -861,6 +863,8 @@ static int ticcmd_set(lua_State *L)
 		cmd->buttons = (UINT16)luaL_checkinteger(L, 3);
 	else if (fastcmp(field,"latency"))
 		return NOSET;
+	else if (fastcmp(field,"flags"))
+		cmd->buttons = (UINT8)luaL_checkinteger(L, 3);
 	else
 		return NOFIELD;
 
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 1270064c01f1494abc582fc7a61c8a80791f4635..8a75013a3d0e2dfc8038b74a76da7319a6c05197 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -51,14 +51,15 @@ UINT8 *save_p;
 // than an UINT16
 typedef enum
 {
-//	RFLAGPOINT = 0x01,
-//	BFLAGPOINT = 0x02,
-	CAPSULE    = 0x04,
-	AWAYVIEW   = 0x08,
-	FIRSTAXIS  = 0x10,
-	SECONDAXIS = 0x20,
-	FOLLOW     = 0x40,
-	DRONE      = 0x80,
+//	RFLAGPOINT = 0x001,
+//	BFLAGPOINT = 0x002,
+	CAPSULE    = 0x004,
+	AWAYVIEW   = 0x008,
+	FIRSTAXIS  = 0x010,
+	SECONDAXIS = 0x020,
+	FOLLOW     = 0x040,
+	DRONE      = 0x080,
+	BOTLEADER  = 0x100,
 } player_saveflags;
 
 static inline void P_ArchivePlayer(void)
@@ -197,10 +198,11 @@ static void P_NetArchivePlayers(void)
 		// Bots //
 		//////////
 		WRITEUINT8(save_p, players[i].bot);
-		WRITEUINT8(save_p, players[i].botmem.lastForward);
-		WRITEUINT8(save_p, players[i].botmem.lastBlocked);
-		WRITEUINT8(save_p, players[i].botmem.catchup_tics);
-		WRITEUINT8(save_p, players[i].botmem.thinkstate);
+		// We no longer need to sync these since ticcmds are generated only by the server
+		//WRITEUINT8(save_p, players[i].botmem.lastForward);
+		//WRITEUINT8(save_p, players[i].botmem.lastBlocked);
+		//WRITEUINT8(save_p, players[i].botmem.catchup_tics);
+		//WRITEUINT8(save_p, players[i].botmem.thinkstate);
 		WRITEUINT8(save_p, players[i].removing);
 		
 		WRITEUINT8(save_p, players[i].blocked);
@@ -293,6 +295,9 @@ static void P_NetArchivePlayers(void)
 		if (players[i].drone)
 			flags |= DRONE;
 
+		if (players[i].botleader)
+			flags |= BOTLEADER;
+
 		WRITEINT16(save_p, players[i].lastsidehit);
 		WRITEINT16(save_p, players[i].lastlinehit);
 
@@ -325,6 +330,9 @@ static void P_NetArchivePlayers(void)
 		if (flags & DRONE)
 			WRITEUINT32(save_p, players[i].drone->mobjnum);
 
+		if (flags & BOTLEADER)
+			WRITEUINT8(save_p, (UINT8)(players[i].botleader - players));
+
 		WRITEFIXED(save_p, players[i].camerascale);
 		WRITEFIXED(save_p, players[i].shieldscale);
 
@@ -425,10 +433,10 @@ static void P_NetUnArchivePlayers(void)
 		//////////
 		players[i].bot = READUINT8(save_p);
 		
-		players[i].botmem.lastForward = READUINT8(save_p);
-		players[i].botmem.lastBlocked = READUINT8(save_p);
-		players[i].botmem.catchup_tics = READUINT8(save_p);
-		players[i].botmem.thinkstate = READUINT8(save_p);
+		//players[i].botmem.lastForward = READUINT8(save_p);
+		//players[i].botmem.lastBlocked = READUINT8(save_p);
+		//players[i].botmem.catchup_tics = READUINT8(save_p);
+		//players[i].botmem.thinkstate = READUINT8(save_p);
 		players[i].removing = READUINT8(save_p);
 
 		players[i].blocked = READUINT8(save_p);
@@ -535,6 +543,9 @@ static void P_NetUnArchivePlayers(void)
 		if (flags & DRONE)
 			players[i].drone = (mobj_t *)(size_t)READUINT32(save_p);
 
+		if (flags & BOTLEADER)
+			players[i].botleader = &players[READUINT8(save_p)];
+
 		players[i].camerascale = READFIXED(save_p);
 		players[i].shieldscale = READFIXED(save_p);
 
diff --git a/src/p_user.c b/src/p_user.c
index f21118a81fb2f4f088758a9b92ac52444d289e77..cc232d08d8b03c8f1337c91bc339b67b96e43ea9 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -5349,10 +5349,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
 
 						player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH);
-						if (player->bot == BOT_2PAI)
-							player->pflags |= PF_THOKKED;
-						else
+						if ((player->bot != BOT_2PAI) || (cmd->flags & TCF_SETCARRY))
 							player->pflags |= (PF_THOKKED|PF_CANCARRY);
+						else
+							player->pflags |= PF_THOKKED;
 					}
 					break;
 				case CA_GLIDEANDCLIMB:
@@ -11450,6 +11450,12 @@ void P_PlayerThink(player_t *player)
 		{
 			if (B_CheckRespawn(player))
 				player->playerstate = PST_REBORN;
+			else
+			{
+				if (player->bot == BOT_2PAI)
+					B_UpdateBotleader(player);
+				B_HandleFlightIndicator(player);
+			}
 		}
 		if (player->playerstate == PST_REBORN)
 		{