diff --git a/src/include/net_message.h b/src/include/net_message.h
index a7630c07a..d1edda490 100644
--- a/src/include/net_message.h
+++ b/src/include/net_message.h
@@ -65,6 +65,7 @@ public:
 	uint32_t Host;         /// Host address
 	uint16_t Port;         /// Port on host
 	uint16_t PlyNr;        /// Player number
+	int16_t  PlayerIndex;  /// Desired player index in game, or -1 for default
 	char PlyName[NetPlayerNameSize];  /// Name of player
 };
 
diff --git a/src/network/net_message.cpp b/src/network/net_message.cpp
index d63dc4fb5..0ca08aec4 100644
--- a/src/network/net_message.cpp
+++ b/src/network/net_message.cpp
@@ -224,6 +224,7 @@ void CNetworkHost::Clear()
 	this->Host = 0;
 	this->Port = 0;
 	this->PlyNr = 0;
+	this->PlayerIndex = -1;
 	memset(this->PlyName, 0, sizeof(this->PlyName));
 }
 
diff --git a/src/network/netconnect.cpp b/src/network/netconnect.cpp
index 3242ed026..1a3b0a72a 100644
--- a/src/network/netconnect.cpp
+++ b/src/network/netconnect.cpp
@@ -37,6 +37,10 @@
 #include <fstream>
 #include <set>
 #include <vector>
+#include <functional>
+#include <algorithm>
+#include <array>
+#include <utility>
 #if __has_include(<filesystem>)
 #include <filesystem>
 namespace fs = std::filesystem;
@@ -1720,29 +1724,29 @@ void NetworkServerStartGame()
 	LocalSetupState = ServerSetupState;
 
 	// Make a list of the available player slots.
-	int num[PlayerMax];
-	int rev[PlayerMax];
-	int h = 0;
+	int humanPlayerIndexToMapPlayerIndex[PlayerMax];
+	int mapPlayerIndexToHumanPlayerIndexAndBeyond[PlayerMax];
+	int humanPlayerCount = 0;
 	for (int i = 0; i < PlayerMax; ++i) {
 		if (Map.Info.PlayerType[i] == PlayerTypes::PlayerPerson) {
-			rev[i] = h;
-			num[h++] = i;
-			DebugPrint("Slot %d is available for an interactive player (%d)\n" _C_ i _C_ rev[i]);
+			mapPlayerIndexToHumanPlayerIndexAndBeyond[i] = humanPlayerCount;
+			humanPlayerIndexToMapPlayerIndex[humanPlayerCount++] = i;
+			DebugPrint("Slot %d is available for an interactive player (%d)\n" _C_ i _C_ mapPlayerIndexToHumanPlayerIndexAndBeyond[i]);
 		}
 	}
 	// Make a list of the available computer slots.
-	int n = h;
+	int n = humanPlayerCount;
 	for (int i = 0; i < PlayerMax; ++i) {
 		if (Map.Info.PlayerType[i] == PlayerTypes::PlayerComputer) {
-			rev[i] = n++;
-			DebugPrint("Slot %d is available for an ai computer player (%d)\n" _C_ i _C_ rev[i]);
+			mapPlayerIndexToHumanPlayerIndexAndBeyond[i] = n++;
+			DebugPrint("Slot %d is available for an ai computer player (%d)\n" _C_ i _C_ mapPlayerIndexToHumanPlayerIndexAndBeyond[i]);
 		}
 	}
 	// Make a list of the remaining slots.
 	for (int i = 0; i < PlayerMax; ++i) {
 		if (Map.Info.PlayerType[i] != PlayerTypes::PlayerPerson
 			&& Map.Info.PlayerType[i] != PlayerTypes::PlayerComputer) {
-			rev[i] = n++;
+			mapPlayerIndexToHumanPlayerIndexAndBeyond[i] = n++;
 			// PlayerTypes::PlayerNobody - not available to anything..
 		}
 	}
@@ -1757,25 +1761,27 @@ void NetworkServerStartGame()
 		printf("\n");
 	}
 
-	int org[PlayerMax];
+	int slotIndexToMapPlayerIndex[PlayerMax];
 	// Reverse to assign slots to menu setup state positions.
 	for (int i = 0; i < PlayerMax; ++i) {
-		org[i] = -1;
+		slotIndexToMapPlayerIndex[i] = -1;
 		for (int j = 0; j < PlayerMax; ++j) {
-			if (rev[j] == i) {
-				org[i] = j;
+			if (mapPlayerIndexToHumanPlayerIndexAndBeyond[j] == i) {
+				slotIndexToMapPlayerIndex[i] = j;
 				break;
 			}
 		}
 	}
 
 	// Calculate NetPlayers
-	NetPlayers = h;
+	NetPlayers = humanPlayerCount;
 	int compPlayers = ServerSetupState.ServerGameSettings.Opponents;
-	for (int i = 1; i < h; ++i) {
+	for (int i = 1; i < humanPlayerCount; ++i) {
 		if (Hosts[i].PlyNr == 0 && ServerSetupState.CompOpt[i] != SlotOption::Available) {
+			// no host connectd and the slot is not available, not a net player
 			NetPlayers--;
 		} else if (Hosts[i].PlyName[0] == 0) {
+			// the slot is available, but no host connected to it
 			NetPlayers--;
 			if (--compPlayers >= 0) {
 				// Unused slot gets a computer player
@@ -1789,8 +1795,9 @@ void NetworkServerStartGame()
 	}
 
 	// Compact host list.. (account for computer/closed slots in the middle..)
-	for (int i = 1; i < h; ++i) {
+	for (int i = 1; i < humanPlayerCount; ++i) {
 		if (Hosts[i].PlyNr == 0) {
+			// this host is not connected, so we can move some later host down
 			int j;
 			for (j = i + 1; j < PlayerMax - 1; ++j) {
 				if (Hosts[j].PlyNr) {
@@ -1802,6 +1809,7 @@ void NetworkServerStartGame()
 					break;
 				}
 			}
+			// early return, in case there are no more hosts later
 			if (j == PlayerMax - 1) {
 				break;
 			}
@@ -1810,42 +1818,95 @@ void NetworkServerStartGame()
 
 	// Randomize the position.
 	// It can be disabled by writing NoRandomPlacementMultiplayer() in lua files.
-	// Players slots are then mapped to players numbers(and colors).
+	// Players slots are then mapped to players numbers(and colors) or by PlayerIndex preference
+	// (set in lua via Hosts[i].PlyNr, which is not really changing it, just "requesting" the change).
 
 	if (NoRandomPlacementMultiplayer) {
+		std::array<std::pair<int, CNetworkHost>, PlayerMax> s;
 		for (int i = 0; i < PlayerMax; ++i) {
-			if (Map.Info.PlayerType[i] != PlayerTypes::PlayerComputer) {
-				org[i] = Hosts[i].PlyNr;
+			s[i] = std::make_pair(i, Hosts[i]);
+		}
+		std::sort(s.begin(), s.end(), [](std::pair<int, CNetworkHost> &a, std::pair<int, CNetworkHost> &b) {
+			if (std::get<1>(a).PlayerIndex < 0 && std::get<1>(b).PlayerIndex < 0) {
+				// no preferences here, first slot first
+				return std::get<0>(a) < std::get<0>(b);
+			} else {
+				if (std::get<1>(b).PlayerIndex < 0) {
+					return true; // a has preference, b not, a first
+				} else if (std::get<1>(a).PlayerIndex < 0) {
+					return false; // b has preference, a not, b first
+				} else {
+					return std::get<1>(a).PlayerIndex < std::get<1>(b).PlayerIndex;
+				}
+			}
+		});
+		int j = 0;
+		for (int i = 0; i < PlayerMax; ++i) {
+			if (Map.Info.PlayerType[i] == PlayerTypes::PlayerPerson) {
+				std::get<1>(s[j]).PlyNr = i;
+				int netPlayer = std::get<0>(s[j]);
+				int k = slotIndexToMapPlayerIndex[netPlayer];
+				// k is the in-game PlayerIndex for the slot i.
+				// we need to swap it with the in-game PlayerIndex
+				// that we just assigned
+				if (k != i) {
+					for (int o = 0; o < PlayerMax; ++o) {
+						if (slotIndexToMapPlayerIndex[o] == i) {
+							slotIndexToMapPlayerIndex[o] = k;
+							break;
+						}
+					}
+					slotIndexToMapPlayerIndex[i] = i;
+				}
+				DebugPrint("Assigning player %d to slot %d (%d)\n" _C_ netPlayer _C_ i _C_ slotIndexToMapPlayerIndex[netPlayer]);
+				j++;
 			}
 		}
 	} else {
-		int j = h;
+		// j is the number of human player slots the map info provides
+		int j = humanPlayerCount;
 		for (int i = 0; i < NetPlayers; ++i) {
 			Assert(j > 0);
-			int chosen = MyRand() % j;
-
-			n = num[chosen];
+			int chosenHumanPlayerNumber = MyRand() % j;
+			// we've chosen a slot on the map to assign
+			n = humanPlayerIndexToMapPlayerIndex[chosenHumanPlayerNumber];
+			// n is now the in-game PlayerIndex for this slot
 			Hosts[i].PlyNr = n;
-			int k = org[i];
+
+			int k = slotIndexToMapPlayerIndex[i];
+			// k is the in-game PlayerIndex for the slot i.
+			// we need to swap it with the in-game PlayerIndex
+			// that we just assigned
 			if (k != n) {
 				for (int o = 0; o < PlayerMax; ++o) {
-					if (org[o] == n) {
-						org[o] = k;
+					if (slotIndexToMapPlayerIndex[o] == n) {
+						slotIndexToMapPlayerIndex[o] = k;
 						break;
 					}
 				}
-				org[i] = n;
+				slotIndexToMapPlayerIndex[i] = n;
 			}
-			DebugPrint("Assigning player %d to slot %d (%d)\n" _C_ i _C_ n _C_ org[i]);
+			DebugPrint("Assigning player %d to slot %d (%d)\n" _C_ i _C_ n _C_ slotIndexToMapPlayerIndex[i]);
 
-			num[chosen] = num[--j];
+			// we replace the in-game PlayerIndex we have just assigned
+			// with the last one available to human players in our map
+			// for the next MyRand() % j call. We won't be needing this
+			// mapping to be correct anymore afterwards
+			humanPlayerIndexToMapPlayerIndex[chosenHumanPlayerNumber] = humanPlayerIndexToMapPlayerIndex[--j];
 		}
 	}
 
+	int mapPlayerIndexToAckStatus[PlayerMax];
 	// Complete all setup states for the assigned slots.
+	// At this point, LocalSetupState has all the info
+	// assigned in-order, that is, LocalSetupState first has
+	// the settings for the human players, then computers, then
+	// nothing. We now go through the players and set it up through
+	// the mapping that the server assigns each slot index to the
+	// correct map player index
 	for (int i = 0; i < PlayerMax; ++i) {
-		num[i] = 1;
-		n = org[i];
+		mapPlayerIndexToAckStatus[i] = 1;
+		n = slotIndexToMapPlayerIndex[i];
 		ServerSetupState.CompOpt[n] = LocalSetupState.CompOpt[i];
 		ServerSetupState.ServerGameSettings.Presets[n].Race = LocalSetupState.ServerGameSettings.Presets[i].Race;
 	}
@@ -1892,10 +1953,10 @@ breakout:
 		for (int i = 0; i < HostsCount; ++i) {
 			const CHost host(message.hosts[i].Host, message.hosts[i].Port);
 
-			if (num[Hosts[i].PlyNr] == 1) { // not acknowledged yet
+			if (mapPlayerIndexToAckStatus[Hosts[i].PlyNr] == 1) { // not acknowledged yet
 				message.clientIndex = i;
 				NetworkSendICMessage_Log(NetworkFildes, host, message);
-			} else if (num[Hosts[i].PlyNr] == 2) {
+			} else if (mapPlayerIndexToAckStatus[Hosts[i].PlyNr] == 2) {
 				NetworkSendICMessage_Log(NetworkFildes, host, statemsg);
 			}
 		}
@@ -1922,8 +1983,8 @@ breakout:
 						DebugPrint("Got ack for InitConfig from %s\n" _C_ hostStr.c_str());
 						const int index = FindHostIndexBy(host);
 						if (index != -1) {
-							if (num[Hosts[index].PlyNr] == 1) {
-								num[Hosts[index].PlyNr]++;
+							if (mapPlayerIndexToAckStatus[Hosts[index].PlyNr] == 1) {
+								mapPlayerIndexToAckStatus[Hosts[index].PlyNr]++;
 							}
 							goto breakout;
 						}
@@ -1934,8 +1995,8 @@ breakout:
 						DebugPrint("Got ack for InitState from %s\n" _C_ hostStr.c_str());
 						const int index = FindHostIndexBy(host);
 						if (index != -1) {
-							if (num[Hosts[index].PlyNr] == 2) {
-								num[Hosts[index].PlyNr] = 0;
+							if (mapPlayerIndexToAckStatus[Hosts[index].PlyNr] == 2) {
+								mapPlayerIndexToAckStatus[Hosts[index].PlyNr] = 0;
 								--j;
 								DebugPrint("Removing host %d from waiting list\n" _C_ j);
 							}
diff --git a/src/tolua/network.pkg b/src/tolua/network.pkg
index a6ed44171..46e3f9511 100644
--- a/src/tolua/network.pkg
+++ b/src/tolua/network.pkg
@@ -45,7 +45,7 @@ tolua_readonly int NetPlayerNameSize;
 struct CNetworkHost {
 	unsigned long  Host;         /// Host address
 	unsigned short Port;         /// Port on host
-	unsigned short PlyNr;        /// Player nummer
+	short PlayerIndex @ PlyNr;   /// Desired player number when in game
 	char           PlyName[NetPlayerNameSize];  /// Name of player
 };
 extern CNetworkHost Hosts[PlayerMax];