Index: /binary-improvements/7dtd-server-fixes/src/API.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/API.cs	(revision 368)
+++ /binary-improvements/7dtd-server-fixes/src/API.cs	(revision 369)
@@ -1,9 +1,9 @@
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
-using System;
+using Platform.Steam;
 
 namespace AllocsFixes {
 	public class API : IModApi {
-		public void InitMod () {
+		public void InitMod (Mod _modInstance) {
 			ModEvents.GameStartDone.RegisterHandler (GameAwake);
 			ModEvents.GameShutdown.RegisterHandler (GameShutdown);
@@ -16,64 +16,44 @@
 
 		public void GameAwake () {
-			try {
-				PersistentContainer.Load ();
-			} catch (Exception e) {
-				Log.Out ("Error in StateManager.Awake: " + e);
-			}
+			PersistentContainer.Load ();
 		}
 
 		public void GameShutdown () {
-			try {
-				Log.Out ("Server shutting down!");
-				PersistentContainer.Instance.Save ();
-			} catch (Exception e) {
-				Log.Out ("Error in StateManager.Shutdown: " + e);
-			}
 		}
 
 		public void SavePlayerData (ClientInfo _cInfo, PlayerDataFile _playerDataFile) {
-			try {
-				PersistentContainer.Instance.Players [_cInfo.playerId, true].Update (_playerDataFile);
-			} catch (Exception e) {
-				Log.Out ("Error in GM_SavePlayerData: " + e);
-			}
+			PersistentContainer.Instance.Players [_cInfo.PlatformId, true].Update (_playerDataFile);
 		}
 
 		public void PlayerSpawning (ClientInfo _cInfo, int _chunkViewDim, PlayerProfile _playerProfile) {
-			try {
-				Log.Out ("Player connected" +
-					", entityid=" + _cInfo.entityId +
-					", name=" + _cInfo.playerName +
-					", steamid=" + _cInfo.playerId +
-					", steamOwner=" + _cInfo.ownerId +
-					", ip=" + _cInfo.ip
-				);
-			} catch (Exception e) {
-				Log.Out ("Error in AllocsLogFunctions.RequestToSpawnPlayer: " + e);
+			string owner = null;
+			if (_cInfo.PlatformId is UserIdentifierSteam identifierSteam) {
+				owner = identifierSteam.OwnerId.ToString ();
 			}
+
+			Log.Out ("Player connected" +
+			         ", entityid=" + _cInfo.entityId +
+			         ", name=" + _cInfo.playerName +
+			         ", pltfmid=" + (_cInfo.PlatformId?.CombinedString ?? "<unknown>") +
+			         ", crossid=" + (_cInfo.CrossplatformId?.CombinedString ?? "<unknown/none>") +
+			         ", steamOwner=" + (owner ?? "<unknown/none>") +
+			         ", ip=" + _cInfo.ip
+			);
 		}
 
 		public void PlayerDisconnected (ClientInfo _cInfo, bool _bShutdown) {
-			try {
-				Player p = PersistentContainer.Instance.Players [_cInfo.playerId, false];
-				if (p != null) {
-					p.SetOffline ();
-				} else {
-					Log.Out ("Disconnected player not found in client list...");
-				}
+			Player p = PersistentContainer.Instance.Players [_cInfo.PlatformId, false];
+			if (p != null) {
+				p.SetOffline ();
+			} else {
+				Log.Out ("Disconnected player not found in client list...");
+			}
 
-				PersistentContainer.Instance.Save ();
-			} catch (Exception e) {
-				Log.Out ("Error in AllocsLogFunctions.PlayerDisconnected: " + e);
-			}
+			PersistentContainer.Instance.Save ();
 		}
 
 		public void PlayerSpawned (ClientInfo _cInfo, RespawnType _respawnReason, Vector3i _spawnPos) {
-			try {
-				PersistentContainer.Instance.Players [_cInfo.playerId, true].SetOnline (_cInfo);
-				PersistentContainer.Instance.Save ();
-			} catch (Exception e) {
-				Log.Out ("Error in AllocsLogFunctions.PlayerSpawnedInWorld: " + e);
-			}
+			PersistentContainer.Instance.Players [_cInfo.PlatformId, true].SetOnline (_cInfo);
+			PersistentContainer.Instance.Save ();
 		}
 
@@ -88,5 +68,5 @@
 
 			if (_cInfo != null) {
-				Log.Out ("Sent chat hook reply to {0}", _cInfo.playerId);
+				Log.Out ("Sent chat hook reply to {0}", _cInfo.InternalId);
 				_cInfo.SendPackage (NetPackageManager.GetPackage<NetPackageChat> ().Setup (EChatType.Whisper, -1, ANSWER, "", false, null));
 			} else {
Index: /binary-improvements/7dtd-server-fixes/src/LandClaimList.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/LandClaimList.cs	(revision 368)
+++ /binary-improvements/7dtd-server-fixes/src/LandClaimList.cs	(revision 369)
@@ -4,5 +4,5 @@
 
 namespace AllocsFixes {
-	public class LandClaimList {
+	public static class LandClaimList {
 		public delegate bool OwnerFilter (Player _owner);
 
@@ -41,7 +41,7 @@
 
 			foreach (KeyValuePair<PersistentPlayerData, List<Vector3i>> kvp in owners) {
-				Player p = PersistentContainer.Instance.Players [kvp.Key.PlayerId, false];
+				Player p = PersistentContainer.Instance.Players [kvp.Key.UserIdentifier, false];
 				if (p == null) {
-					p = new Player (kvp.Key.PlayerId);
+					p = new Player (kvp.Key.UserIdentifier);
 				}
 
@@ -67,6 +67,6 @@
 		}
 
-		public static OwnerFilter SteamIdFilter (string _steamId) {
-			return _p => _p.SteamID.Equals (_steamId);
+		public static OwnerFilter UserIdFilter (PlatformUserIdentifierAbs _userId) {
+			return _p => _p.PlatformId.Equals (_userId);
 		}
 
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs	(revision 368)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/PersistentContainer.cs	(revision 369)
@@ -46,5 +46,5 @@
 
 		public void Save () {
-			Stream stream = File.Open (GameUtils.GetSaveGameDir () + "/AllocsPeristentData.bin", FileMode.Create);
+			Stream stream = File.Open (GameIO.GetSaveGameDir () + "/AllocsPeristentData.bin", FileMode.Create);
 			BinaryFormatter bFormatter = new BinaryFormatter ();
 			bFormatter.Serialize (stream, this);
@@ -53,5 +53,5 @@
 
 		public static bool Load () {
-			if (!File.Exists (GameUtils.GetSaveGameDir () + "/AllocsPeristentData.bin")) {
+			if (!File.Exists (GameIO.GetSaveGameDir () + "/AllocsPeristentData.bin")) {
 				return false;
 			}
@@ -59,5 +59,5 @@
 			try {
 				PersistentContainer obj;
-				Stream stream = File.Open (GameUtils.GetSaveGameDir () + "/AllocsPeristentData.bin", FileMode.Open);
+				Stream stream = File.Open (GameIO.GetSaveGameDir () + "/AllocsPeristentData.bin", FileMode.Open);
 				BinaryFormatter bFormatter = new BinaryFormatter ();
 				obj = (PersistentContainer) bFormatter.Deserialize (stream);
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs	(revision 368)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Player.cs	(revision 369)
@@ -1,4 +1,3 @@
 using System;
-using System.Runtime.Serialization;
 using UnityEngine;
 
@@ -6,5 +5,5 @@
 	[Serializable]
 	public class Player {
-		private readonly string steamId;
+		private readonly PlatformUserIdentifierAbs platformId;
 		private int entityId;
 		private string name;
@@ -12,65 +11,34 @@
 		private long totalPlayTime;
 
-		[OptionalField] private DateTime lastOnline;
+		private DateTime lastOnline;
 
 		private Inventory inventory;
 
-		[OptionalField] private int lastPositionX, lastPositionY, lastPositionZ;
+		private int lastPositionX, lastPositionY, lastPositionZ;
 
-		[OptionalField] [Obsolete ("experience no longer available, use level and expToNextLevel instead")]
-		private uint experience;
-
-		[OptionalField] private bool chatMuted;
-		[OptionalField] private int maxChatLength;
-		[OptionalField] private string chatColor;
-		[OptionalField] private bool chatName;
-		[OptionalField] private uint expToNextLevel;
-		[OptionalField] private int level;
+		private bool chatMuted;
+		private int maxChatLength;
+		private string chatColor;
+		private bool chatName;
+		private uint expToNextLevel;
+		private int level;
 
 		[NonSerialized] private ClientInfo clientInfo;
 
-		public string SteamID {
-			get { return steamId; }
-		}
+		public PlatformUserIdentifierAbs PlatformId => platformId;
 
-        public int EntityID {
-            get { return entityId; }
-        }
+		public int EntityID => entityId;
 
-		public string Name {
-			get { return name == null ? string.Empty : name; }
-		}
+		public string Name => name ?? string.Empty;
 
-		public string IP {
-			get { return ip == null ? string.Empty : ip; }
-		}
+		public string IP => ip ?? string.Empty;
 
-		public Inventory Inventory {
-			get {
-				if (inventory == null) {
-					inventory = new Inventory ();
-				}
+		public Inventory Inventory => inventory ?? (inventory = new Inventory ());
 
-				return inventory;
-			}
-		}
+		public bool IsOnline => clientInfo != null;
 
-		public bool IsOnline {
-			get { return clientInfo != null; }
-		}
+		public ClientInfo ClientInfo => clientInfo;
 
-		public ClientInfo ClientInfo {
-			get { return clientInfo; }
-		}
-
-		public EntityPlayer Entity {
-			get {
-				if (IsOnline) {
-					return GameManager.Instance.World.Players.dict [clientInfo.entityId];
-				}
-
-				return null;
-			}
-		}
+		public EntityPlayer Entity => IsOnline ? GameManager.Instance.World.Players.dict [clientInfo.entityId] : null;
 
 		public long TotalPlayTime {
@@ -84,43 +52,15 @@
 		}
 
-		public DateTime LastOnline {
-			get {
-				if (IsOnline) {
-					return DateTime.Now;
-				}
+		public DateTime LastOnline => IsOnline ? DateTime.Now : lastOnline;
 
-				return lastOnline;
-			}
-		}
+		public Vector3i LastPosition => IsOnline ? new Vector3i (Entity.GetPosition ()) : new Vector3i (lastPositionX, lastPositionY, lastPositionZ);
 
-		public Vector3i LastPosition {
-			get {
-				if (IsOnline) {
-					return new Vector3i (Entity.GetPosition ());
-				}
+		public bool LandProtectionActive =>
+			GameManager.Instance.World.IsLandProtectionValidForPlayer (GameManager.Instance
+				.GetPersistentPlayerList ().GetPlayerData (PlatformId));
 
-				return new Vector3i (lastPositionX, lastPositionY, lastPositionZ);
-			}
-		}
-
-		public bool LandProtectionActive {
-			get {
-				return GameManager.Instance.World.IsLandProtectionValidForPlayer (GameManager.Instance
-					.GetPersistentPlayerList ().GetPlayerData (SteamID));
-			}
-		}
-
-		public float LandProtectionMultiplier {
-			get {
-				return GameManager.Instance.World.GetLandProtectionHardnessModifierForPlayer (GameManager.Instance
-					.GetPersistentPlayerList ().GetPlayerData (SteamID));
-			}
-		}
-
-
-		[Obsolete ("Experience no longer available, use Level instead")]
-		public uint Experience {
-			get { return 0; }
-		}
+		public float LandProtectionMultiplier =>
+			GameManager.Instance.World.GetLandProtectionHardnessModifierForPlayer (GameManager.Instance
+				.GetPersistentPlayerList ().GetPlayerData (PlatformId));
 
 		public float Level {
@@ -135,6 +75,6 @@
 
 		public bool IsChatMuted {
-			get { return chatMuted; }
-			set { chatMuted = value; }
+			get => chatMuted;
+			set => chatMuted = value;
 		}
 
@@ -147,10 +87,10 @@
 				return maxChatLength;
 			}
-			set { maxChatLength = value; }
+			set => maxChatLength = value;
 		}
 
 		public string ChatColor {
 			get {
-				if (chatColor == null || chatColor == "") {
+				if (string.IsNullOrEmpty (chatColor)) {
 					chatColor = "";
 				}
@@ -159,15 +99,14 @@
 			}
 
-			set { chatColor = value; }
+			set => chatColor = value;
 		}
 
 		public bool ChatName {
-			get { return chatName; }
-
-			set { chatName = value; }
+			get => chatName;
+			set => chatName = value;
 		}
 
-		public Player (string _steamId) {
-			steamId = _steamId;
+		public Player (PlatformUserIdentifierAbs _platformId) {
+			platformId = _platformId;
 			inventory = new Inventory ();
 		}
@@ -178,5 +117,5 @@
 			}
 
-			Log.Out ("Player set to offline: " + steamId);
+			Log.Out ("Player set to offline: " + platformId);
 			lastOnline = DateTime.Now;
 			try {
@@ -194,5 +133,5 @@
 
 		public void SetOnline (ClientInfo _ci) {
-			Log.Out ("Player set to online: " + steamId);
+			Log.Out ("Player set to online: " + platformId);
 			clientInfo = _ci;
             entityId = _ci.entityId;
@@ -222,4 +161,5 @@
 			}
 		}
+
 	}
 }
Index: /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs
===================================================================
--- /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs	(revision 368)
+++ /binary-improvements/7dtd-server-fixes/src/PersistentData/Players.cs	(revision 369)
@@ -2,57 +2,45 @@
 using System.Collections.Generic;
 using System.Text.RegularExpressions;
+using Platform.Steam;
 
 namespace AllocsFixes.PersistentData {
 	[Serializable]
 	public class Players {
-		public readonly Dictionary<string, Player> Dict = new Dictionary<string, Player> (StringComparer.OrdinalIgnoreCase);
+		public readonly Dictionary<PlatformUserIdentifierAbs, Player> Dict = new Dictionary<PlatformUserIdentifierAbs, Player> ();
 
-		public Player this [string _steamId, bool _create] {
+		public Player this [PlatformUserIdentifierAbs _platformId, bool _create] {
 			get {
-				if (string.IsNullOrEmpty (_steamId)) {
+				if (_platformId == null) {
 					return null;
 				}
 
-				if (Dict.ContainsKey (_steamId)) {
-					return Dict [_steamId];
+				if (Dict.TryGetValue (_platformId, out Player pOld)) {
+					return pOld;
 				}
 
-				if (!_create || _steamId.Length != 17) {
+				if (!_create) {
 					return null;
 				}
 
-				Log.Out ("Created new player entry for ID: " + _steamId);
-				Player p = new Player (_steamId);
-				Dict.Add (_steamId, p);
+				Log.Out ("Created new player entry for ID: " + _platformId);
+				Player p = new Player (_platformId);
+				Dict.Add (_platformId, p);
 				return p;
 			}
 		}
 
-		public int Count {
-			get { return Dict.Count; }
-		}
+		public int Count => Dict.Count;
 
-//		public Player GetPlayerByNameOrId (string _nameOrId, bool _ignoreColorCodes)
-//		{
-//			string sid = GetSteamID (_nameOrId, _ignoreColorCodes);
-//			if (sid != null)
-//				return this [sid];
-//			else
-//				return null;
-//		}
-
-		public string GetSteamID (string _nameOrId, bool _ignoreColorCodes) {
-			if (_nameOrId == null || _nameOrId.Length == 0) {
+		public PlatformUserIdentifierAbs GetSteamID (string _nameOrId, bool _ignoreColorCodes) {
+			if (string.IsNullOrEmpty (_nameOrId)) {
 				return null;
 			}
 
-			long tempLong;
-			if (_nameOrId.Length == 17 && long.TryParse (_nameOrId, out tempLong)) {
-				return _nameOrId;
+			if (PlatformUserIdentifierAbs.TryFromCombinedString (_nameOrId, out PlatformUserIdentifierAbs userId)) {
+				return userId;
 			}
 
-			int entityId;
-			if (int.TryParse (_nameOrId, out entityId)) {
-				foreach (KeyValuePair<string, Player> kvp in Dict) {
+			if (int.TryParse (_nameOrId, out int entityId)) {
+				foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in Dict) {
 					if (kvp.Value.IsOnline && kvp.Value.EntityID == entityId) {
 						return kvp.Key;
@@ -61,5 +49,5 @@
 			}
 
-			foreach (KeyValuePair<string, Player> kvp in Dict) {
+			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in Dict) {
 				string name = kvp.Value.Name;
 				if (_ignoreColorCodes) {
Index: /binary-improvements/AllocsCommands/API.cs
===================================================================
--- /binary-improvements/AllocsCommands/API.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/API.cs	(revision 369)
@@ -1,5 +1,5 @@
 namespace AllocsFixes.CustomCommands {
 	public class API : IModApi {
-		public void InitMod () {
+		public void InitMod (Mod _modInstance) {
 		}
 	}
Index: /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/Commands/ListKnownPlayers.cs	(revision 369)
@@ -1,3 +1,2 @@
-using System;
 using System.Collections.Generic;
 using AllocsFixes.PersistentData;
@@ -14,9 +13,9 @@
 			       "  2. listknownplayers -online\n" +
 			       "  3. listknownplayers -notbanned\n" +
-			       "  4. listknownplayers <player name / steamid>\n" +
+			       "  4. listknownplayers <player name / userid>\n" +
 			       "1. Lists all players that have ever been online\n" +
 			       "2. Lists only the players that are currently online\n" +
 			       "3. Lists only the players that are not banned\n" +
-			       "4. Lists all players whose name contains the given string or matches the given SteamID";
+			       "4. Lists all players whose name contains the given string or matches the given UserID";
 		}
 
@@ -31,14 +30,13 @@
 			bool notBannedOnly = false;
 			string nameFilter = string.Empty;
-			bool isSteamId = false;
+			PlatformUserIdentifierAbs userIdFilter = null;
 
 			if (_params.Count == 1) {
-				long steamid;
 				if (_params [0].EqualsCaseInsensitive ("-online")) {
 					onlineOnly = true;
 				} else if (_params [0].EqualsCaseInsensitive ("-notbanned")) {
 					notBannedOnly = true;
-				} else if (_params [0].Length == 17 && long.TryParse (_params [0], out steamid)) {
-					isSteamId = true;
+				} else if (PlatformUserIdentifierAbs.TryFromCombinedString (_params [0], out userIdFilter)) {
+					// if true nothing to do, set by the out parameter
 				} else {
 					nameFilter = _params [0];
@@ -46,37 +44,31 @@
 			}
 
-			if (isSteamId) {
-				Player p = PersistentContainer.Instance.Players [_params [0], false];
+			if (userIdFilter != null) {
+				Player p = PersistentContainer.Instance.Players [userIdFilter, false];
 
 				if (p != null) {
-					SdtdConsole.Instance.Output (string.Format (
-						"{0}. {1}, id={2}, steamid={3}, online={4}, ip={5}, playtime={6} m, seen={7}",
-						0, p.Name, p.EntityID, _params [0], p.IsOnline, p.IP,
-						p.TotalPlayTime / 60,
-						p.LastOnline.ToString ("yyyy-MM-dd HH:mm"))
+					SdtdConsole.Instance.Output (
+						$"{0}. {p.Name}, id={p.EntityID}, steamid={_params [0]}, online={p.IsOnline}, ip={p.IP}, playtime={p.TotalPlayTime / 60} m, seen={p.LastOnline:yyyy-MM-dd HH:mm}"
 					);
 				} else {
-					SdtdConsole.Instance.Output (string.Format ("SteamID {0} unknown!", _params [0]));
+					SdtdConsole.Instance.Output ($"SteamID {_params [0]} unknown!");
 				}
 			} else {
 				int num = 0;
-				foreach (KeyValuePair<string, Player> kvp in PersistentContainer.Instance.Players.Dict) {
+				foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in PersistentContainer.Instance.Players.Dict) {
 					Player p = kvp.Value;
 
 					if (
 						(!onlineOnly || p.IsOnline)
-						&& (!notBannedOnly || !admTools.IsBanned (kvp.Key))
+						&& (!notBannedOnly || !admTools.IsBanned (kvp.Key, out _, out _))
 						&& (nameFilter.Length == 0 || p.Name.ContainsCaseInsensitive (nameFilter))
 					) {
-						SdtdConsole.Instance.Output (string.Format (
-							"{0}. {1}, id={2}, steamid={3}, online={4}, ip={5}, playtime={6} m, seen={7}",
-							++num, p.Name, p.EntityID, kvp.Key, p.IsOnline, p.IP,
-							p.TotalPlayTime / 60,
-							p.LastOnline.ToString ("yyyy-MM-dd HH:mm"))
+						SdtdConsole.Instance.Output (
+							$"{++num}. {p.Name}, id={p.EntityID}, steamid={kvp.Key}, online={p.IsOnline}, ip={p.IP}, playtime={p.TotalPlayTime / 60} m, seen={p.LastOnline:yyyy-MM-dd HH:mm}"
 						);
 					}
 				}
 
-				SdtdConsole.Instance.Output ("Total of " + PersistentContainer.Instance.Players.Count + " known");
+				SdtdConsole.Instance.Output ($"Total of {PersistentContainer.Instance.Players.Count} known");
 			}
 		}
Index: /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/Commands/ListLandProtection.cs	(revision 369)
@@ -12,8 +12,8 @@
 			return "Usage:\n" +
 			       "  1. listlandprotection summary\n" +
-			       "  2. listlandprotection <steam id / player name / entity id> [parseable]\n" +
+			       "  2. listlandprotection <user id / player name / entity id> [parseable]\n" +
 			       "  3. listlandprotection nearby [length]\n" +
 			       "1. Lists only players that own claimstones, the number they own and the protection status\n" +
-			       "2. Lists only the claims of the player given by his SteamID / entity id / playername, including the individual claim positions.\n" +
+			       "2. Lists only the claims of the player given by his UserID / entity id / playername, including the individual claim positions.\n" +
 			       "   If \"parseable\" is specified the output of the individual claims will be in a format better suited for programmatical readout.\n" +
 			       "3. Lists claims in a square with edge length of 64 (or the optionally specified size) around the executing player\n";
@@ -25,7 +25,9 @@
 
 		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			if (_senderInfo.RemoteClientInfo != null) {
-				if (_params.Count >= 1 && _params [0].EqualsCaseInsensitive ("nearby")) {
-					_params.Add (_senderInfo.RemoteClientInfo.playerId);
+			if (_params.Count >= 1 && _params [0].EqualsCaseInsensitive ("nearby")) {
+				if (_senderInfo.RemoteClientInfo != null) {
+					_params.Add (_senderInfo.RemoteClientInfo.entityId.ToString ());
+				} else if (_senderInfo.IsLocalGame && !GameManager.IsDedicatedServer) {
+					_params.Add (GameManager.Instance.World.GetPrimaryPlayerId ().ToString ());
 				}
 			}
@@ -35,5 +37,5 @@
 
 			bool summaryOnly = false;
-			string steamIdFilter = string.Empty;
+			PlatformUserIdentifierAbs userIdFilter = null;
 			Vector3i closeTo = default (Vector3i);
 			bool onlyCloseToPlayer = false;
@@ -47,14 +49,11 @@
 
 			if (_params.Count == 1) {
-				long tempLong;
-
 				if (_params [0].EqualsCaseInsensitive ("summary")) {
 					summaryOnly = true;
-				} else if (_params [0].Length == 17 && long.TryParse (_params [0], out tempLong)) {
-					steamIdFilter = _params [0];
+				} else if (PlatformUserIdentifierAbs.TryFromCombinedString (_params[0], out userIdFilter)) {
 				} else {
 					ClientInfo ci = ConsoleHelper.ParseParamIdOrName (_params [0]);
 					if (ci != null) {
-						steamIdFilter = ci.playerId;
+						userIdFilter = ci.PlatformId;
 					} else {
 						SdtdConsole.Instance.Output ("Player name or entity id \"" + _params [0] + "\" not found.");
@@ -74,6 +73,6 @@
 						}
 
-						ClientInfo ci = ConsoleHelper.ParseParamSteamIdOnline (_params [_params.Count - 1]);
-						EntityPlayer ep = w.Players.dict [ci.entityId];
+						int entityId = int.Parse (_params [_params.Count - 1]);
+						EntityPlayer ep = w.Players.dict [entityId];
 						closeTo = new Vector3i (ep.GetPosition ());
 						onlyCloseToPlayer = true;
@@ -91,6 +90,6 @@
 
 			LandClaimList.OwnerFilter[] ownerFilters = null;
-			if (!string.IsNullOrEmpty (steamIdFilter)) {
-				ownerFilters = new[] {LandClaimList.SteamIdFilter (steamIdFilter)};
+			if (userIdFilter != null) {
+				ownerFilters = new[] {LandClaimList.UserIdFilter (userIdFilter)};
 			}
 
@@ -106,5 +105,5 @@
 					"Player \"{0} ({1})\" owns {4} keystones (protected: {2}, current hardness multiplier: {3})",
 					kvp.Key.Name,
-					kvp.Key.SteamID,
+					kvp.Key.PlatformId,
 					kvp.Key.LandProtectionActive,
 					kvp.Key.LandProtectionMultiplier,
@@ -113,5 +112,5 @@
 					foreach (Vector3i v in kvp.Value) {
 						if (parseableOutput) {
-							SdtdConsole.Instance.Output ("LandProtectionOf: id=" + kvp.Key.SteamID +
+							SdtdConsole.Instance.Output ("LandProtectionOf: id=" + kvp.Key.PlatformId +
 							                             ", playerName=" + kvp.Key.Name + ", location=" + v);
 						} else {
@@ -122,5 +121,5 @@
 			}
 
-			if (string.IsNullOrEmpty (steamIdFilter)) {
+			if (userIdFilter == null) {
 				SdtdConsole.Instance.Output ("Total of " + ppl.m_lpBlockMap.Count + " keystones in the game");
 			}
Index: /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/Commands/RemoveLandProtection.cs	(revision 369)
@@ -11,8 +11,8 @@
 		public override string GetHelp () {
 			return "Usage:" +
-			       "  1. removelandprotection <steamid>\n" +
+			       "  1. removelandprotection <userid>\n" +
 			       "  2. removelandprotection <x> <y> <z>\n" +
 			       "  3. removelandprotection nearby [length]\n" +
-			       "1. Remove all land claims owned by the user with the given SteamID\n" +
+			       "1. Remove all land claims owned by the user with the given UserID\n" +
 			       "2. Remove only the claim block on the exactly given block position\n" +
 			       "3. Remove all claims in a square with edge length of 64 (or the optionally specified size) around the executing player";
@@ -25,13 +25,13 @@
 		private void removeById (string _id) {
 			try {
-				PersistentPlayerList ppl = GameManager.Instance.GetPersistentPlayerList ();
-
-				if (_id.Length < 1 || !ppl.Players.ContainsKey (_id)) {
+				if (!PlatformUserIdentifierAbs.TryFromCombinedString (_id, out PlatformUserIdentifierAbs userId)) {
 					SdtdConsole.Instance.Output (
 						"Not a valid Steam ID or user has never logged on. Use \"listlandprotection\" to get a list of keystones.");
 					return;
 				}
+				
+				PersistentPlayerList ppl = GameManager.Instance.GetPersistentPlayerList ();
 
-				if (ppl.Players [_id].LPBlocks == null || ppl.Players [_id].LPBlocks.Count == 0) {
+				if (ppl.Players [userId].LPBlocks == null || ppl.Players [userId].LPBlocks.Count == 0) {
 					SdtdConsole.Instance.Output (
 						"Player does not own any keystones. Use \"listlandprotection\" to get a list of keystones.");
@@ -40,5 +40,5 @@
 
 				List<BlockChangeInfo> changes = new List<BlockChangeInfo> ();
-				foreach (Vector3i pos in ppl.Players [_id].LPBlocks) {
+				foreach (Vector3i pos in ppl.Players [userId].LPBlocks) {
 					BlockChangeInfo bci = new BlockChangeInfo (pos, new BlockValue (0), true, false);
 					changes.Add (bci);
@@ -58,8 +58,7 @@
 
 		private void removeByPosition (List<string> _coords) {
-			int x, y, z;
-			int.TryParse (_coords [0], out x);
-			int.TryParse (_coords [1], out y);
-			int.TryParse (_coords [2], out z);
+			int.TryParse (_coords [0], out int x);
+			int.TryParse (_coords [1], out int y);
+			int.TryParse (_coords [2], out int z);
 
 			if (x == int.MinValue || y == int.MinValue || z == int.MinValue) {
@@ -81,6 +80,5 @@
 			BlockChangeInfo bci = new BlockChangeInfo (v, new BlockValue (0), true, false);
 
-			List<BlockChangeInfo> changes = new List<BlockChangeInfo> ();
-			changes.Add (bci);
+			List<BlockChangeInfo> changes = new List<BlockChangeInfo> {bci};
 
 			GameManager.Instance.SetBlocksRPC (changes);
@@ -90,57 +88,53 @@
 
 		public override void Execute (List<string> _params, CommandSenderInfo _senderInfo) {
-			try {
+			if (_params.Count > 0 && _params [0].EqualsCaseInsensitive ("nearby")) {
 				if (_senderInfo.RemoteClientInfo != null) {
-					if (_params.Count >= 1 && _params [0].EqualsCaseInsensitive ("nearby")) {
-						_params.Add (_senderInfo.RemoteClientInfo.playerId);
-					}
+					_params.Add (_senderInfo.RemoteClientInfo.entityId.ToString ());
+				} else if (_senderInfo.IsLocalGame && !GameManager.IsDedicatedServer) {
+					_params.Add (GameManager.Instance.World.GetPrimaryPlayerId ().ToString ());
 				}
 
-				if (_params.Count > 0 && _params [0].EqualsCaseInsensitive ("nearby")) {
-					try {
-						int closeToDistance = 32;
-						if (_params.Count == 3) {
-							if (!int.TryParse (_params [1], out closeToDistance)) {
-								SdtdConsole.Instance.Output ("Given length is not an integer!");
-								return;
-							}
-
-							closeToDistance /= 2;
+				try {
+					int closeToDistance = 32;
+					if (_params.Count == 3) {
+						if (!int.TryParse (_params [1], out closeToDistance)) {
+							SdtdConsole.Instance.Output ("Given length is not an integer!");
+							return;
 						}
 
-						ClientInfo ci = ConsoleHelper.ParseParamSteamIdOnline (_params [_params.Count - 1]);
-						EntityPlayer ep = GameManager.Instance.World.Players.dict [ci.entityId];
-						Vector3i closeTo = new Vector3i (ep.GetPosition ());
-						LandClaimList.PositionFilter[] posFilters =
-							{LandClaimList.CloseToFilter2dRect (closeTo, closeToDistance)};
-						Dictionary<Player, List<Vector3i>> claims = LandClaimList.GetLandClaims (null, posFilters);
+						closeToDistance /= 2;
+					}
 
-						try {
-							List<BlockChangeInfo> changes = new List<BlockChangeInfo> ();
-							foreach (KeyValuePair<Player, List<Vector3i>> kvp in claims) {
-								foreach (Vector3i v in kvp.Value) {
-									BlockChangeInfo bci = new BlockChangeInfo (v, new BlockValue (0), true, false);
-									changes.Add (bci);
-								}
+					int entityId = int.Parse (_params [_params.Count - 1]);
+					EntityPlayer ep = GameManager.Instance.World.Players.dict [entityId];
+					Vector3i closeTo = new Vector3i (ep.GetPosition ());
+					LandClaimList.PositionFilter[] posFilters =
+						{LandClaimList.CloseToFilter2dRect (closeTo, closeToDistance)};
+					Dictionary<Player, List<Vector3i>> claims = LandClaimList.GetLandClaims (null, posFilters);
+
+					try {
+						List<BlockChangeInfo> changes = new List<BlockChangeInfo> ();
+						foreach (KeyValuePair<Player, List<Vector3i>> kvp in claims) {
+							foreach (Vector3i v in kvp.Value) {
+								BlockChangeInfo bci = new BlockChangeInfo (v, new BlockValue (0), true, false);
+								changes.Add (bci);
 							}
+						}
 
-							GameManager.Instance.SetBlocksRPC (changes);
-						} catch (Exception e) {
-							SdtdConsole.Instance.Output ("Error removing claims");
-							Log.Out ("Error in RemoveLandProtection.Run: " + e);
-						}
+						GameManager.Instance.SetBlocksRPC (changes);
 					} catch (Exception e) {
-						SdtdConsole.Instance.Output ("Error getting current player's position");
+						SdtdConsole.Instance.Output ("Error removing claims");
 						Log.Out ("Error in RemoveLandProtection.Run: " + e);
 					}
-				} else if (_params.Count == 1) {
-					removeById (_params [0]);
-				} else if (_params.Count == 3) {
-					removeByPosition (_params);
-				} else {
-					SdtdConsole.Instance.Output ("Illegal parameters");
+				} catch (Exception e) {
+					SdtdConsole.Instance.Output ("Error getting current player's position");
+					Log.Out ("Error in RemoveLandProtection.Run: " + e);
 				}
-			} catch (Exception e) {
-				Log.Out ("Error in RemoveLandProtection.Run: " + e);
+			} else if (_params.Count == 1) {
+				removeById (_params [0]);
+			} else if (_params.Count == 3) {
+				removeByPosition (_params);
+			} else {
+				SdtdConsole.Instance.Output ("Illegal parameters");
 			}
 		}
Index: /binary-improvements/AllocsCommands/Commands/ShowInventory.cs
===================================================================
--- /binary-improvements/AllocsCommands/Commands/ShowInventory.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/Commands/ShowInventory.cs	(revision 369)
@@ -11,6 +11,6 @@
 		public override string GetHelp () {
 			return "Usage:\n" +
-			       "   showinventory <steam id / player name / entity id> [tag]\n" +
-			       "Show the inventory of the player given by his SteamID, player name or\n" +
+			       "   showinventory <user id / player name / entity id> [tag]\n" +
+			       "Show the inventory of the player given by his UserID, player name or\n" +
 			       "entity id (as given by e.g. \"lpi\").\n" +
 			       "Optionally specify a tag that is included in each line of the output. In\n" +
@@ -30,5 +30,5 @@
 			}
 
-			string steamid = PersistentContainer.Instance.Players.GetSteamID (_params [0], true);
+			PlatformUserIdentifierAbs steamid = PersistentContainer.Instance.Players.GetSteamID (_params [0], true);
 			if (steamid == null) {
 				SdtdConsole.Instance.Output (
Index: /binary-improvements/AllocsCommands/PrivateMessageConnections.cs
===================================================================
--- /binary-improvements/AllocsCommands/PrivateMessageConnections.cs	(revision 368)
+++ /binary-improvements/AllocsCommands/PrivateMessageConnections.cs	(revision 369)
@@ -4,17 +4,16 @@
 namespace AllocsFixes.CustomCommands {
 	public class PrivateMessageConnections {
-		private static readonly Dictionary<CSteamID, CSteamID> senderOfLastPM = new Dictionary<CSteamID, CSteamID> ();
+		private static readonly Dictionary<PlatformUserIdentifierAbs, PlatformUserIdentifierAbs> senderOfLastPM = new Dictionary<PlatformUserIdentifierAbs, PlatformUserIdentifierAbs> ();
 
 		public static void SetLastPMSender (ClientInfo _sender, ClientInfo _receiver) {
-			senderOfLastPM [_receiver.steamId] = _sender.steamId;
+			senderOfLastPM [_receiver.InternalId] = _sender.InternalId;
 		}
 
 		public static ClientInfo GetLastPMSenderForPlayer (ClientInfo _player) {
-			if (!senderOfLastPM.ContainsKey (_player.steamId)) {
+			if (!senderOfLastPM.TryGetValue (_player.InternalId, out PlatformUserIdentifierAbs recUserId)) {
 				return null;
 			}
 
-			CSteamID recSteamId = senderOfLastPM [_player.steamId];
-			ClientInfo recInfo = ConnectionManager.Instance.Clients.ForSteamId (recSteamId);
+			ClientInfo recInfo = ConnectionManager.Instance.Clients.ForUserId (recUserId);
 			return recInfo;
 		}
Index: /binary-improvements/MapRendering/API.cs
===================================================================
--- /binary-improvements/MapRendering/API.cs	(revision 368)
+++ /binary-improvements/MapRendering/API.cs	(revision 369)
@@ -6,5 +6,5 @@
 		private Web webInstance;
 		
-		public void InitMod () {
+		public void InitMod (Mod _modInstance) {
 			ModEvents.GameStartDone.RegisterHandler (GameStartDone);
 			ModEvents.GameShutdown.RegisterHandler (GameShutdown);
@@ -14,4 +14,8 @@
 		private void GameStartDone () {
 			// ReSharper disable once ObjectCreationAsStatement
+			if (!ConnectionManager.Instance.IsServer) {
+				return;
+			}
+			
 			webInstance = new Web ();
 			LogBuffer.Init ();
@@ -23,5 +27,5 @@
 
 		private void GameShutdown () {
-			webInstance.Shutdown ();
+			webInstance?.Shutdown ();
 			MapRendering.MapRendering.Shutdown ();
 		}
Index: /binary-improvements/MapRendering/MapRendering/MapRendering.cs
===================================================================
--- /binary-improvements/MapRendering/MapRendering/MapRendering.cs	(revision 368)
+++ /binary-improvements/MapRendering/MapRendering/MapRendering.cs	(revision 369)
@@ -26,5 +26,5 @@
 
 		private MapRendering () {
-			Constants.MAP_DIRECTORY = GameUtils.GetSaveGameDir () + "/map";
+			Constants.MAP_DIRECTORY = GameIO.GetSaveGameDir () + "/map";
 
 			lock (lockObject) {
@@ -59,5 +59,5 @@
 
 		public static void Shutdown () {
-			if (Instance.renderCoroutineRef != null) {
+			if (Instance?.renderCoroutineRef != null) {
 				ThreadManager.StopCoroutine (Instance.renderCoroutineRef);
 				Instance.renderCoroutineRef = null;
@@ -66,5 +66,5 @@
 
 		public static void RenderSingleChunk (Chunk _chunk) {
-			if (renderingEnabled) {
+			if (renderingEnabled && Instance != null) {
 				// TODO: Replace with regular thread and a blocking queue / set
 				ThreadPool.UnsafeQueueUserWorkItem (_o => {
@@ -101,5 +101,5 @@
 			MicroStopwatch microStopwatch = new MicroStopwatch ();
 
-			string regionSaveDir = GameUtils.GetSaveGameRegionDir ();
+			string regionSaveDir = GameIO.GetSaveGameRegionDir ();
 			RegionFileManager rfm = new RegionFileManager (regionSaveDir, regionSaveDir, 0, false);
 			Texture2D fullMapTexture = null;
Index: /binary-improvements/MapRendering/Web/API/GetLandClaims.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetLandClaims.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetLandClaims.cs	(revision 369)
@@ -8,12 +8,9 @@
 		public override void HandleRequest (HttpListenerRequest _req, HttpListenerResponse _resp, WebConnection _user,
 			int _permissionLevel) {
-			string requestedSteamID = string.Empty;
-
-			if (_req.QueryString ["steamid"] != null) {
-				ulong lViewersSteamID;
-				requestedSteamID = _req.QueryString ["steamid"];
-				if (requestedSteamID.Length != 17 || !ulong.TryParse (requestedSteamID, out lViewersSteamID)) {
+			PlatformUserIdentifierAbs requestedUserId = null;
+			if (_req.QueryString ["userid"] != null) {
+				if (!PlatformUserIdentifierAbs.TryFromCombinedString (_req.QueryString ["userid"], out requestedUserId)) {
 					_resp.StatusCode = (int) HttpStatusCode.BadRequest;
-					Web.SetResponseTextContent (_resp, "Invalid SteamID given");
+					Web.SetResponseTextContent (_resp, "Invalid user id given");
 					return;
 				}
@@ -21,5 +18,5 @@
 
 			// default user, cheap way to avoid 'null reference exception'
-			_user = _user ?? new WebConnection ("", IPAddress.None, 0L);
+			PlatformUserIdentifierAbs userId = _user?.UserId;
 
 			bool bViewAll = WebConnection.CanViewAllClaims (_permissionLevel);
@@ -32,14 +29,14 @@
 
 			LandClaimList.OwnerFilter[] ownerFilters = null;
-			if (!string.IsNullOrEmpty (requestedSteamID) || !bViewAll) {
-				if (!string.IsNullOrEmpty (requestedSteamID) && !bViewAll) {
+			if (requestedUserId != null || !bViewAll) {
+				if (requestedUserId != null && !bViewAll) {
 					ownerFilters = new[] {
-						LandClaimList.SteamIdFilter (_user.SteamID.ToString ()),
-						LandClaimList.SteamIdFilter (requestedSteamID)
+						LandClaimList.UserIdFilter (userId),
+						LandClaimList.UserIdFilter (requestedUserId)
 					};
 				} else if (!bViewAll) {
-					ownerFilters = new[] {LandClaimList.SteamIdFilter (_user.SteamID.ToString ())};
+					ownerFilters = new[] {LandClaimList.UserIdFilter (userId)};
 				} else {
-					ownerFilters = new[] {LandClaimList.SteamIdFilter (requestedSteamID)};
+					ownerFilters = new[] {LandClaimList.UserIdFilter (requestedUserId)};
 				}
 			}
@@ -50,30 +47,27 @@
 
 			foreach (KeyValuePair<Player, List<Vector3i>> kvp in claims) {
-//				try {
-					JSONObject owner = new JSONObject ();
-					claimOwners.Add (owner);
+				JSONObject owner = new JSONObject ();
+				claimOwners.Add (owner);
 
-					owner.Add ("steamid", new JSONString (kvp.Key.SteamID));
-					owner.Add ("claimactive", new JSONBoolean (kvp.Key.LandProtectionActive));
+				owner.Add ("steamid", new JSONString (kvp.Key.PlatformId.CombinedString));
+				owner.Add ("claimactive", new JSONBoolean (kvp.Key.LandProtectionActive));
 
-					if (kvp.Key.Name.Length > 0) {
-						owner.Add ("playername", new JSONString (kvp.Key.Name));
-					} else {
-						owner.Add ("playername", new JSONNull ());
-					}
+				if (kvp.Key.Name.Length > 0) {
+					owner.Add ("playername", new JSONString (kvp.Key.Name));
+				} else {
+					owner.Add ("playername", new JSONNull ());
+				}
 
-					JSONArray claimsJson = new JSONArray ();
-					owner.Add ("claims", claimsJson);
+				JSONArray claimsJson = new JSONArray ();
+				owner.Add ("claims", claimsJson);
 
-					foreach (Vector3i v in kvp.Value) {
-						JSONObject claim = new JSONObject ();
-						claim.Add ("x", new JSONNumber (v.x));
-						claim.Add ("y", new JSONNumber (v.y));
-						claim.Add ("z", new JSONNumber (v.z));
+				foreach (Vector3i v in kvp.Value) {
+					JSONObject claim = new JSONObject ();
+					claim.Add ("x", new JSONNumber (v.x));
+					claim.Add ("y", new JSONNumber (v.y));
+					claim.Add ("z", new JSONNumber (v.z));
 
-						claimsJson.Add (claim);
-					}
-//				} catch {
-//				}
+					claimsJson.Add (claim);
+				}
 			}
 
Index: /binary-improvements/MapRendering/Web/API/GetPlayerInventories.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetPlayerInventories.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetPlayerInventories.cs	(revision 369)
@@ -8,11 +8,9 @@
 		public override void HandleRequest (HttpListenerRequest _req, HttpListenerResponse _resp, WebConnection _user,
 			int _permissionLevel) {
-
-			bool showIconColor, showIconName;
-			GetPlayerInventory.GetInventoryArguments (_req, out showIconColor, out showIconName);
+			GetPlayerInventory.GetInventoryArguments (_req, out bool showIconColor, out bool showIconName);
 
 			JSONArray AllInventoriesResult = new JSONArray ();
 
-			foreach (KeyValuePair<string, Player> kvp in PersistentContainer.Instance.Players.Dict) {
+			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in PersistentContainer.Instance.Players.Dict) {
 				Player p = kvp.Value;
 
@@ -22,5 +20,5 @@
 
 				if (p.IsOnline) {
-					AllInventoriesResult.Add (GetPlayerInventory.DoPlayer (kvp.Key, p, showIconColor, showIconName));
+					AllInventoriesResult.Add (GetPlayerInventory.DoPlayer (kvp.Key.CombinedString, p, showIconColor, showIconName));
 				}
 			}
Index: /binary-improvements/MapRendering/Web/API/GetPlayerInventory.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetPlayerInventory.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetPlayerInventory.cs	(revision 369)
@@ -8,23 +8,27 @@
 		public override void HandleRequest (HttpListenerRequest _req, HttpListenerResponse _resp, WebConnection _user,
 			int _permissionLevel) {
-			if (_req.QueryString ["steamid"] == null) {
+			if (_req.QueryString ["userid"] == null) {
 				_resp.StatusCode = (int) HttpStatusCode.BadRequest;
-				Web.SetResponseTextContent (_resp, "No SteamID given");
+				Web.SetResponseTextContent (_resp, "No user id given");
 				return;
 			}
 
-			string steamId = _req.QueryString ["steamid"];
-
-			Player p = PersistentContainer.Instance.Players [steamId, false];
-			if (p == null) {
-				_resp.StatusCode = (int) HttpStatusCode.NotFound;
-				Web.SetResponseTextContent (_resp, "Invalid or unknown SteamID given");
+			string userIdString = _req.QueryString ["userid"];
+			if (!PlatformUserIdentifierAbs.TryFromCombinedString (userIdString, out PlatformUserIdentifierAbs userId)) {
+				_resp.StatusCode = (int) HttpStatusCode.BadRequest;
+				Web.SetResponseTextContent (_resp, "Invalid user id given");
 				return;
 			}
 
-			bool showIconColor, showIconName;
-			GetInventoryArguments (_req, out showIconColor, out showIconName);
+			Player p = PersistentContainer.Instance.Players [userId, false];
+			if (p == null) {
+				_resp.StatusCode = (int) HttpStatusCode.NotFound;
+				Web.SetResponseTextContent (_resp, "Unknown user id given");
+				return;
+			}
 
-			JSONObject result = DoPlayer (steamId, p, showIconColor, showIconName);
+			GetInventoryArguments (_req, out bool showIconColor, out bool showIconName);
+
+			JSONObject result = DoPlayer (userIdString, p, showIconColor, showIconName);
 
 			WriteJSON (_resp, result);
@@ -49,5 +53,5 @@
 			JSONArray belt = new JSONArray ();
 			JSONObject equipment = new JSONObject ();
-			result.Add ("steamid", new JSONString (_steamId));
+			result.Add ("userid", new JSONString (_steamId));
 			result.Add ("entityid", new JSONNumber (_player.EntityID));
 			result.Add ("playername", new JSONString (_player.Name));
@@ -86,5 +90,5 @@
 
 			for (int i = 0; i < slotindices.Length; i++) {
-				if (_items != null && _items [slotindices [i]] != null) {
+				if (_items? [slotindices [i]] != null) {
 					InvItem item = _items [slotindices [i]];
 					_eq.Add (_slotname, GetJsonForItem (item, _showIconColor, _showIconName));
Index: /binary-improvements/MapRendering/Web/API/GetPlayerList.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetPlayerList.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetPlayerList.cs	(revision 369)
@@ -6,5 +6,4 @@
 using AllocsFixes.JSON;
 using AllocsFixes.PersistentData;
-using UnityEngine.Profiling;
 
 namespace AllocsFixes.NetConnections.Servers.Web.API {
@@ -14,5 +13,5 @@
 
 #if ENABLE_PROFILER
-		private static readonly CustomSampler jsonSerializeSampler = CustomSampler.Create ("JSON_Build");
+		private static readonly UnityEngine.Profiling.CustomSampler jsonSerializeSampler = UnityEngine.Profiling.CustomSampler.Create ("JSON_Build");
 #endif
 
@@ -20,5 +19,5 @@
 			int _permissionLevel) {
 			AdminTools admTools = GameManager.Instance.adminTools;
-			_user = _user ?? new WebConnection ("", IPAddress.None, 0L);
+			PlatformUserIdentifierAbs userId = _user?.UserId;
 
 			bool bViewAll = WebConnection.CanViewAllPlayers (_permissionLevel);
@@ -47,13 +46,8 @@
 #endif
 
-			foreach (KeyValuePair<string, Player> kvp in playersList.Dict) {
+			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
 				Player p = kvp.Value;
 
-				ulong player_steam_ID;
-				if (!ulong.TryParse (kvp.Key, out player_steam_ID)) {
-					player_steam_ID = 0L;
-				}
-
-				if (player_steam_ID == _user.SteamID || bViewAll) {
+				if (bViewAll || p.PlatformId.Equals (userId)) {
 					JSONObject pos = new JSONObject ();
 					pos.Add ("x", new JSONNumber (p.LastPosition.x));
@@ -62,5 +56,5 @@
 
 					JSONObject pJson = new JSONObject ();
-					pJson.Add ("steamid", new JSONString (kvp.Key));
+					pJson.Add ("steamid", new JSONString (kvp.Key.CombinedString));
 					pJson.Add ("entityid", new JSONNumber (p.EntityID));
 					pJson.Add ("ip", new JSONString (p.IP));
@@ -74,10 +68,5 @@
 					pJson.Add ("ping", new JSONNumber (p.IsOnline ? p.ClientInfo.ping : -1));
 
-					JSONBoolean banned;
-					if (admTools != null) {
-						banned = new JSONBoolean (admTools.IsBanned (kvp.Key));
-					} else {
-						banned = new JSONBoolean (false);
-					}
+					JSONBoolean banned = admTools != null ? new JSONBoolean (admTools.IsBanned (kvp.Key, out _, out _)) : new JSONBoolean (false);
 
 					pJson.Add ("banned", banned);
Index: /binary-improvements/MapRendering/Web/API/GetPlayersLocation.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetPlayersLocation.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetPlayersLocation.cs	(revision 369)
@@ -9,5 +9,5 @@
 			int _permissionLevel) {
 			AdminTools admTools = GameManager.Instance.adminTools;
-			_user = _user ?? new WebConnection ("", IPAddress.None, 0L);
+			PlatformUserIdentifierAbs userId = _user?.UserId;
 
 			bool listOffline = false;
@@ -22,7 +22,7 @@
 			Players playersList = PersistentContainer.Instance.Players;
 
-			foreach (KeyValuePair<string, Player> kvp in playersList.Dict) {
+			foreach (KeyValuePair<PlatformUserIdentifierAbs, Player> kvp in playersList.Dict) {
 				if (admTools != null) {
-					if (admTools.IsBanned (kvp.Key)) {
+					if (admTools.IsBanned (kvp.Key, out _, out _)) {
 						continue;
 					}
@@ -32,10 +32,5 @@
 
 				if (listOffline || p.IsOnline) {
-					ulong player_steam_ID;
-					if (!ulong.TryParse (kvp.Key, out player_steam_ID)) {
-						player_steam_ID = 0L;
-					}
-
-					if (player_steam_ID == _user.SteamID || bViewAll) {
+					if (bViewAll || p.PlatformId.Equals (userId)) {
 						JSONObject pos = new JSONObject ();
 						pos.Add ("x", new JSONNumber (p.LastPosition.x));
@@ -44,5 +39,5 @@
 
 						JSONObject pJson = new JSONObject ();
-						pJson.Add ("steamid", new JSONString (kvp.Key));
+						pJson.Add ("steamid", new JSONString (kvp.Key.CombinedString));
 
 						//					pJson.Add("entityid", new JSONNumber (p.EntityID));
Index: /binary-improvements/MapRendering/Web/API/GetPlayersOnline.cs
===================================================================
--- /binary-improvements/MapRendering/Web/API/GetPlayersOnline.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/API/GetPlayersOnline.cs	(revision 369)
@@ -13,5 +13,5 @@
 			foreach (KeyValuePair<int, EntityPlayer> current in w.Players.dict) {
 				ClientInfo ci = ConnectionManager.Instance.Clients.ForEntityId (current.Key);
-				Player player = PersistentContainer.Instance.Players [ci.playerId, false];
+				Player player = PersistentContainer.Instance.Players [ci.PlatformId, false];
 
 				JSONObject pos = new JSONObject ();
@@ -21,5 +21,5 @@
 
 				JSONObject p = new JSONObject ();
-				p.Add ("steamid", new JSONString (ci.playerId));
+				p.Add ("steamid", new JSONString (ci.PlatformId.CombinedString));
 				p.Add ("entityid", new JSONNumber (ci.entityId));
 				p.Add ("ip", new JSONString (ci.ip));
@@ -28,8 +28,5 @@
 				p.Add ("position", pos);
 
-				// Deprecated!
-				p.Add ("experience", new JSONNumber (-1));
-
-				p.Add ("level", new JSONNumber (player != null ? player.Level : -1));
+				p.Add ("level", new JSONNumber (player?.Level ?? -1));
 				p.Add ("health", new JSONNumber (current.Value.Health));
 				p.Add ("stamina", new JSONNumber (current.Value.Stamina));
@@ -39,5 +36,5 @@
 				p.Add ("score", new JSONNumber (current.Value.Score));
 
-				p.Add ("totalplaytime", new JSONNumber (player != null ? player.TotalPlayTime : -1));
+				p.Add ("totalplaytime", new JSONNumber (player?.TotalPlayTime ?? -1));
 				p.Add ("lastonline", new JSONString (player != null ? player.LastOnline.ToString ("s") : string.Empty));
 				p.Add ("ping", new JSONNumber (ci.ping));
Index: /binary-improvements/MapRendering/Web/ConnectionHandler.cs
===================================================================
--- /binary-improvements/MapRendering/Web/ConnectionHandler.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/ConnectionHandler.cs	(revision 369)
@@ -2,4 +2,5 @@
 using System.Collections.Generic;
 using System.Net;
+using Platform.Steam;
 
 namespace AllocsFixes.NetConnections.Servers.Web {
@@ -36,5 +37,6 @@
 		public WebConnection LogIn (ulong _steamId, IPAddress _ip) {
 			string sessionId = Guid.NewGuid ().ToString ();
-			WebConnection con = new WebConnection (sessionId, _ip, _steamId);
+			PlatformUserIdentifierAbs userId = new UserIdentifierSteam (_steamId);
+			WebConnection con = new WebConnection (sessionId, _ip, userId);
 			connections.Add (sessionId, con);
 			return con;
Index: /binary-improvements/MapRendering/Web/Handlers/ItemIconHandler.cs
===================================================================
--- /binary-improvements/MapRendering/Web/Handlers/ItemIconHandler.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/Handlers/ItemIconHandler.cs	(revision 369)
@@ -77,5 +77,5 @@
 
 				try {
-					loadIconsFromFolder (Utils.GetGameDir ("Data/ItemIcons"), tintedIcons);
+					loadIconsFromFolder (GameIO.GetGameDir ("Data/ItemIcons"), tintedIcons);
 				} catch (Exception e) {
 					Log.Error ("Failed loading icons from base game");
Index: /binary-improvements/MapRendering/Web/Handlers/UserStatusHandler.cs
===================================================================
--- /binary-improvements/MapRendering/Web/Handlers/UserStatusHandler.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/Handlers/UserStatusHandler.cs	(revision 369)
@@ -13,5 +13,5 @@
 
 			result.Add ("loggedin", new JSONBoolean (_user != null));
-			result.Add ("username", new JSONString (_user != null ? _user.SteamID.ToString () : string.Empty));
+			result.Add ("username", new JSONString (_user != null ? _user.UserId.ToString () : string.Empty));
 
 			JSONArray perms = new JSONArray ();
Index: /binary-improvements/MapRendering/Web/LogBuffer.cs
===================================================================
--- /binary-improvements/MapRendering/Web/LogBuffer.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/LogBuffer.cs	(revision 369)
@@ -1,5 +1,4 @@
 using System;
 using System.Collections.Generic;
-using System.Text.RegularExpressions;
 using UnityEngine;
 
@@ -8,7 +7,4 @@
 		private const int MAX_ENTRIES = 3000;
 		private static LogBuffer instance;
-
-		private static readonly Regex logMessageMatcher =
-			new Regex (@"^([0-9]{4}-[0-9]{2}-[0-9]{2})T([0-9]{2}:[0-9]{2}:[0-9]{2}) ([0-9]+[,.][0-9]+) [A-Z]+ (.*)$");
 
 		private readonly List<LogEntry> logEntries = new List<LogEntry> ();
@@ -23,5 +19,5 @@
 
 		private LogBuffer () {
-			Logger.Main.LogCallbacks += LogCallback;
+			Log.LogCallbacksExtended += LogCallback;
 		}
 
@@ -72,21 +68,11 @@
 		}
 
-		private void LogCallback (string _msg, string _trace, LogType _type) {
+		private void LogCallback (string _formattedMsg, string _plainMsg, string _trace, LogType _type, DateTime _timestamp, long _uptime) {
 			LogEntry le = new LogEntry ();
 
-			Match match = logMessageMatcher.Match (_msg);
-			if (match.Success) {
-				le.date = match.Groups [1].Value;
-				le.time = match.Groups [2].Value;
-				le.uptime = match.Groups [3].Value;
-				le.message = match.Groups [4].Value;
-			} else {
-				DateTime dt = DateTime.Now;
-				le.date = string.Format ("{0:0000}-{1:00}-{2:00}", dt.Year, dt.Month, dt.Day);
-				le.time = string.Format ("{0:00}:{1:00}:{2:00}", dt.Hour, dt.Minute, dt.Second);
-				le.uptime = "";
-				le.message = _msg;
-			}
-
+			le.date = $"{_timestamp.Year:0000}-{_timestamp.Month:00}-{_timestamp.Day:00}";
+			le.time = $"{_timestamp.Hour:00}:{_timestamp.Minute:00}:{_timestamp.Second:00}";
+			le.uptime = _uptime.ToString ();
+			le.message = _plainMsg;
 			le.trace = _trace;
 			le.type = _type;
Index: /binary-improvements/MapRendering/Web/SSE/EventLog.cs
===================================================================
--- /binary-improvements/MapRendering/Web/SSE/EventLog.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/SSE/EventLog.cs	(revision 369)
@@ -1,6 +1,3 @@
 using System;
-using System.Net;
-using System.Text;
-using System.Text.RegularExpressions;
 using AllocsFixes.JSON;
 using UnityEngine;
@@ -8,31 +5,13 @@
 namespace AllocsFixes.NetConnections.Servers.Web.SSE {
 	public class EventLog : EventBase {
-		private static readonly Regex logMessageMatcher =
-			new Regex (@"^([0-9]{4}-[0-9]{2}-[0-9]{2})T([0-9]{2}:[0-9]{2}:[0-9]{2}) ([0-9]+[,.][0-9]+) [A-Z]+ (.*)$");
-
 		public EventLog (SseHandler _parent) : base (_parent, _name: "log") {
-			Logger.Main.LogCallbacks += LogCallback;
+			Log.LogCallbacksExtended += LogCallback;
 		}
 
-
-		private void LogCallback (string _msg, string _trace, LogType _type) {
-			Match match = logMessageMatcher.Match (_msg);
-
-			string date;
-			string time;
-			string uptime;
-			string message;
-			if (match.Success) {
-				date = match.Groups [1].Value;
-				time = match.Groups [2].Value;
-				uptime = match.Groups [3].Value;
-				message = match.Groups [4].Value;
-			} else {
-				DateTime dt = DateTime.Now;
-				date = $"{dt.Year:0000}-{dt.Month:00}-{dt.Day:00}";
-				time = $"{dt.Hour:00}:{dt.Minute:00}:{dt.Second:00}";
-				uptime = "";
-				message = _msg;
-			}
+		private void LogCallback (string _formattedMsg, string _plainMsg, string _trace, LogType _type, DateTime _timestamp, long _uptime) {
+			string date = $"{_timestamp.Year:0000}-{_timestamp.Month:00}-{_timestamp.Day:00}";
+			string time = $"{_timestamp.Hour:00}:{_timestamp.Minute:00}:{_timestamp.Second:00}";
+			string uptime = _uptime.ToString ();
+			string message = _plainMsg;
 
 			JSONObject data = new JSONObject ();
@@ -46,5 +25,4 @@
 			SendData ("logLine", data);
 		}
-		
 	}
 }
Index: /binary-improvements/MapRendering/Web/Web.cs
===================================================================
--- /binary-improvements/MapRendering/Web/Web.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/Web.cs	(revision 369)
@@ -59,5 +59,5 @@
 				RegisterPathHandler ("/itemicons/", new ItemIconHandler (true));
 				RegisterPathHandler ("/map/", new StaticHandler (
-						GameUtils.GetSaveGameDir () + "/map",
+						GameIO.GetSaveGameDir () + "/map",
 						MapRendering.MapRendering.GetTileCache (),
 						false,
@@ -111,5 +111,5 @@
 		}
 
-		public void SendLog (string _text, string _trace, LogType _type) {
+		public void SendLog (string _formattedMessage, string _plainMessage, string _trace, LogType _type, DateTime _timestamp, long _uptime) {
 			// Do nothing, handled by LogBuffer internally
 		}
@@ -248,5 +248,5 @@
 				if (con != null) {
 					_con = con;
-					return GameManager.Instance.adminTools.GetUserPermissionLevel (_con.SteamID.ToString ());
+					return GameManager.Instance.adminTools.GetUserPermissionLevel (_con.UserId);
 				}
 			}
@@ -270,7 +270,7 @@
 						WebConnection con = connectionHandler.LogIn (id, _req.RemoteEndPoint.Address);
 						_con = con;
-						int level = GameManager.Instance.adminTools.GetUserPermissionLevel (id.ToString ());
+						int level = GameManager.Instance.adminTools.GetUserPermissionLevel (con.UserId);
 						Log.Out ("Steam OpenID login from {0} with ID {1}, permission level {2}",
-							remoteEndpointString, con.SteamID, level);
+							remoteEndpointString, con.UserId, level);
 						return level;
 					}
Index: /binary-improvements/MapRendering/Web/WebCommandResult.cs
===================================================================
--- /binary-improvements/MapRendering/Web/WebCommandResult.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/WebCommandResult.cs	(revision 369)
@@ -95,5 +95,5 @@
 		}
 
-		public void SendLog (string _msg, string _trace, LogType _type) {
+		public void SendLog (string _formattedMessage, string _plainMessage, string _trace, LogType _type, DateTime _timestamp, long _uptime) {
 			//throw new NotImplementedException ();
 		}
Index: /binary-improvements/MapRendering/Web/WebConnection.cs
===================================================================
--- /binary-improvements/MapRendering/Web/WebConnection.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/WebConnection.cs	(revision 369)
@@ -11,8 +11,8 @@
 		private readonly string conDescription;
 
-		public WebConnection (string _sessionId, IPAddress _endpoint, ulong _steamId) {
+		public WebConnection (string _sessionId, IPAddress _endpoint, PlatformUserIdentifierAbs _userId) {
 			SessionID = _sessionId;
 			Endpoint = _endpoint;
-			SteamID = _steamId;
+			UserId = _userId;
 			login = DateTime.Now;
 			lastAction = login;
@@ -20,13 +20,11 @@
 		}
 
-		public string SessionID { get; private set; }
+		public string SessionID { get; }
 
-		public IPAddress Endpoint { get; private set; }
+		public IPAddress Endpoint { get; }
 
-		public ulong SteamID { get; private set; }
+		public PlatformUserIdentifierAbs UserId { get; }
 
-		public TimeSpan Age {
-			get { return DateTime.Now - lastAction; }
-		}
+		public TimeSpan Age => DateTime.Now - lastAction;
 
 		public static bool CanViewAllPlayers (int _permissionLevel) {
@@ -54,5 +52,5 @@
 		}
 
-		public override void SendLog (string _msg, string _trace, LogType _type) {
+		public override void SendLog (string _formattedMsg, string _plainMsg, string _trace, LogType _type, DateTime _timestamp, long _uptime) {
 			// Do nothing, handled by LogBuffer
 		}
Index: /binary-improvements/MapRendering/Web/WebPermissions.cs
===================================================================
--- /binary-improvements/MapRendering/Web/WebPermissions.cs	(revision 368)
+++ /binary-improvements/MapRendering/Web/WebPermissions.cs	(revision 369)
@@ -191,5 +191,5 @@
 			modules.Clear ();
 
-			if (!Utils.FileExists (GetFullPath ())) {
+			if (!File.Exists (GetFullPath ())) {
 				Log.Out (string.Format ("Permissions file '{0}' not found, creating.", GetFileName ()));
 				Save ();
Index: /binary-improvements/MapRendering/WebAndMapRendering.csproj
===================================================================
--- /binary-improvements/MapRendering/WebAndMapRendering.csproj	(revision 368)
+++ /binary-improvements/MapRendering/WebAndMapRendering.csproj	(revision 369)
@@ -31,4 +31,8 @@
   </PropertyGroup>
   <ItemGroup>
+    <Reference Include="Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
+      <HintPath>..\7dtd-binaries\Assembly-CSharp-firstpass.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
     <Reference Include="LogLibrary">
       <HintPath>..\7dtd-binaries\LogLibrary.dll</HintPath>
Index: /binary-improvements/webserver/index.html
===================================================================
--- /binary-improvements/webserver/index.html	(revision 368)
+++ /binary-improvements/webserver/index.html	(revision 369)
@@ -159,5 +159,5 @@
 	<div id="playerInventoryDialog" title="Player inventory">
 		Player: <span id="invPlayerName"></span><br/>
-		SteamID: <span id="invSteamId"></span><br/>
+		UserID: <span id="invSteamId"></span><br/>
 		<br/>
 		<table>
Index: /binary-improvements/webserver/js/inventory_dialog.js
===================================================================
--- /binary-improvements/webserver/js/inventory_dialog.js	(revision 368)
+++ /binary-improvements/webserver/js/inventory_dialog.js	(revision 369)
@@ -44,5 +44,5 @@
 //	}
 	
-	$.getJSON( "../api/getplayerinventory", { steamid: steamid  })
+	$.getJSON( "../api/getplayerinventory", { userid: steamid  })
 	.done(function(data) {
 		$("#invPlayerName").text(data.playername);
Index: /binary-improvements/webserver/js/players.js
===================================================================
--- /binary-improvements/webserver/js/players.js	(revision 368)
+++ /binary-improvements/webserver/js/players.js	(revision 369)
@@ -24,5 +24,5 @@
 	// Define columns to be shown
 	var columns = [
-		[ "steamid", "SteamID" ],
+		[ "steamid", "UserID" ],
 		[ "entityid", "EntityID" ],
 		[ "ip", "IP" ],
