public void ClientAdminRead(IReadMessage incMsg) { bool hasPermission = incMsg.ReadBoolean(); if (!hasPermission) { incMsg.ReadPadBits(); return; } bool isOwner = incMsg.ReadBoolean(); incMsg.ReadPadBits(); bannedPlayers.Clear(); UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32(); for (int i = 0; i < (int)bannedPlayerCount; i++) { string name = incMsg.ReadString(); UInt16 uniqueIdentifier = incMsg.ReadUInt16(); bool isRangeBan = incMsg.ReadBoolean(); bool includesExpiration = incMsg.ReadBoolean(); incMsg.ReadPadBits(); DateTime?expiration = null; if (includesExpiration) { double hoursFromNow = incMsg.ReadDouble(); expiration = DateTime.Now + TimeSpan.FromHours(hoursFromNow); } string reason = incMsg.ReadString(); string endPoint = ""; UInt64 steamID = 0; if (isOwner) { endPoint = incMsg.ReadString(); steamID = incMsg.ReadUInt64(); } else { endPoint = "Endpoint concealed by host"; steamID = 0; } bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, endPoint, steamID, reason, expiration)); } if (banFrame != null) { var parent = banFrame.Parent; parent.RemoveChild(banFrame); CreateBanFrame(parent); } }
public void ClientAdminRead(IReadMessage incMsg) { bool hasPermission = incMsg.ReadBoolean(); if (!hasPermission) { incMsg.ReadPadBits(); return; } bool isOwner = incMsg.ReadBoolean(); incMsg.ReadPadBits(); bannedPlayers.Clear(); UInt32 bannedPlayerCount = incMsg.ReadVariableUInt32(); for (int i = 0; i < (int)bannedPlayerCount; i++) { string name = incMsg.ReadString(); UInt16 uniqueIdentifier = incMsg.ReadUInt16(); bool isRangeBan = incMsg.ReadBoolean(); incMsg.ReadPadBits(); string ip = ""; UInt64 steamID = 0; if (isOwner) { ip = incMsg.ReadString(); steamID = incMsg.ReadUInt64(); } else { ip = "IP concealed by host"; steamID = 0; } bannedPlayers.Add(new BannedPlayer(name, uniqueIdentifier, isRangeBan, ip, steamID)); } if (banFrame != null) { var parent = banFrame.Parent; parent.RemoveChild(banFrame); CreateBanFrame(parent); } }
private void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc) { if (netServer == null) { return; } pendingClient.TimeOut = NetworkConnection.TimeoutThreshold; ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); //DebugConsole.NewMessage(initializationStep+" "+pendingClient.InitializationStep); if (pendingClient.InitializationStep != initializationStep) { return; } pendingClient.UpdateTime = Timing.TotalTime + Timing.Step; switch (initializationStep) { case ConnectionInitialization.SteamTicketAndVersion: string name = Client.SanitizeName(inc.ReadString()); UInt64 steamId = inc.ReadUInt64(); UInt16 ticketLength = inc.ReadUInt16(); inc.BitPosition += ticketLength * 8; //skip ticket, owner handles steam authentication if (!Client.IsValidName(name, serverSettings)) { RemovePendingClient(pendingClient, DisconnectReason.InvalidName, "The name \"" + name + "\" is invalid"); return; } string version = inc.ReadString(); bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false; if (!isCompatibleVersion) { RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion, $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version.ToString()}~[clientversion]={version}"); GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); return; } int contentPackageCount = (int)inc.ReadVariableUInt32(); List <ClientContentPackage> clientContentPackages = new List <ClientContentPackage>(); for (int i = 0; i < contentPackageCount; i++) { string packageName = inc.ReadString(); string packageHash = inc.ReadString(); clientContentPackages.Add(new ClientContentPackage(packageName, packageHash)); } //check if the client is missing any of our packages List <ContentPackage> missingPackages = new List <ContentPackage>(); foreach (ContentPackage serverContentPackage in GameMain.SelectedPackages) { if (!serverContentPackage.HasMultiplayerIncompatibleContent) { continue; } bool packageFound = clientContentPackages.Any(cp => cp.Name == serverContentPackage.Name && cp.Hash == serverContentPackage.MD5hash.Hash); if (!packageFound) { missingPackages.Add(serverContentPackage); } } //check if the client is using packages we don't have List <ClientContentPackage> redundantPackages = new List <ClientContentPackage>(); foreach (ClientContentPackage clientContentPackage in clientContentPackages) { bool packageFound = GameMain.SelectedPackages.Any(cp => cp.Name == clientContentPackage.Name && cp.MD5hash.Hash == clientContentPackage.Hash); if (!packageFound) { redundantPackages.Add(clientContentPackage); } } if (missingPackages.Count == 1) { RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content package " + GetPackageStr(missingPackages[0]) + ")", ServerLog.MessageType.Error); return; } else if (missingPackages.Count > 1) { List <string> packageStrs = new List <string>(); missingPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); RemovePendingClient(pendingClient, DisconnectReason.MissingContentPackage, $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (missing content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } if (redundantPackages.Count == 1) { RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage, $"DisconnectMessage.IncompatibleContentPackage~[incompatiblecontentpackage]={GetPackageStr(redundantPackages[0])}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using an incompatible content package " + GetPackageStr(redundantPackages[0]) + ")", ServerLog.MessageType.Error); return; } if (redundantPackages.Count > 1) { List <string> packageStrs = new List <string>(); redundantPackages.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); RemovePendingClient(pendingClient, DisconnectReason.IncompatibleContentPackage, $"DisconnectMessage.IncompatibleContentPackages~[incompatiblecontentpackages]={string.Join(", ", packageStrs)}"); GameServer.Log(name + " (" + pendingClient.SteamID + ") couldn't join the server (using incompatible content packages " + string.Join(", ", packageStrs) + ")", ServerLog.MessageType.Error); return; } if (!pendingClient.AuthSessionStarted) { pendingClient.InitializationStep = serverSettings.HasPassword ? ConnectionInitialization.Password: ConnectionInitialization.Success; pendingClient.Name = name; pendingClient.AuthSessionStarted = true; } break; case ConnectionInitialization.Password: int pwLength = inc.ReadByte(); byte[] incPassword = inc.ReadBytes(pwLength); if (pendingClient.PasswordSalt == null) { DebugConsole.ThrowError("Received password message from client without salt"); return; } if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value)) { pendingClient.InitializationStep = ConnectionInitialization.Success; } else { pendingClient.Retries++; if (pendingClient.Retries >= 3) { string banMsg = "Failed to enter correct password too many times"; serverSettings.BanList.BanPlayer(pendingClient.Name, pendingClient.SteamID, banMsg, null); RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg); return; } } pendingClient.UpdateTime = Timing.TotalTime; break; } }
protected void ReadConnectionInitializationStep(IReadMessage inc) { ConnectionInitialization step = (ConnectionInitialization)inc.ReadByte(); IWriteMessage outMsg; switch (step) { case ConnectionInitialization.SteamTicketAndVersion: if (initializationStep != ConnectionInitialization.SteamTicketAndVersion) { return; } outMsg = new WriteOnlyMessage(); outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep); outMsg.Write((byte)ConnectionInitialization.SteamTicketAndVersion); outMsg.Write(Name); outMsg.Write(ownerKey); outMsg.Write(SteamManager.GetSteamID()); if (steamAuthTicket == null) { outMsg.Write((UInt16)0); } else { outMsg.Write((UInt16)steamAuthTicket.Data.Length); outMsg.Write(steamAuthTicket.Data, 0, steamAuthTicket.Data.Length); } outMsg.Write(GameMain.Version.ToString()); outMsg.Write(GameMain.Config.Language); SendMsgInternal(DeliveryMethod.Reliable, outMsg); break; case ConnectionInitialization.ContentPackageOrder: if (initializationStep == ConnectionInitialization.SteamTicketAndVersion || initializationStep == ConnectionInitialization.Password) { initializationStep = ConnectionInitialization.ContentPackageOrder; } if (initializationStep != ConnectionInitialization.ContentPackageOrder) { return; } outMsg = new WriteOnlyMessage(); outMsg.Write((byte)PacketHeader.IsConnectionInitializationStep); outMsg.Write((byte)ConnectionInitialization.ContentPackageOrder); string serverName = inc.ReadString(); UInt32 cpCount = inc.ReadVariableUInt32(); ServerContentPackage corePackage = null; List <ServerContentPackage> regularPackages = new List <ServerContentPackage>(); List <ServerContentPackage> missingPackages = new List <ServerContentPackage>(); for (int i = 0; i < cpCount; i++) { string name = inc.ReadString(); string hash = inc.ReadString(); UInt64 workshopId = inc.ReadUInt64(); var pkg = new ServerContentPackage(name, hash, workshopId); if (pkg.CorePackage != null) { corePackage = pkg; } else if (pkg.RegularPackage != null) { regularPackages.Add(pkg); } else { missingPackages.Add(pkg); } } if (missingPackages.Count > 0) { var nonDownloadable = missingPackages.Where(p => p.WorkshopId == 0); if (nonDownloadable.Any()) { string disconnectMsg; if (nonDownloadable.Count() == 1) { disconnectMsg = $"DisconnectMessage.MissingContentPackage~[missingcontentpackage]={GetPackageStr(missingPackages[0])}"; } else { List <string> packageStrs = new List <string>(); nonDownloadable.ForEach(cp => packageStrs.Add(GetPackageStr(cp))); disconnectMsg = $"DisconnectMessage.MissingContentPackages~[missingcontentpackages]={string.Join(", ", packageStrs)}"; } Close(disconnectMsg, disableReconnect: true); OnDisconnectMessageReceived?.Invoke(DisconnectReason.MissingContentPackage + "/" + disconnectMsg); } else { Close(disableReconnect: true); string missingModNames = "\n"; int displayedModCount = 0; foreach (ServerContentPackage missingPackage in missingPackages) { missingModNames += "\n- " + GetPackageStr(missingPackage); displayedModCount++; if (GUI.Font.MeasureString(missingModNames).Y > GameMain.GraphicsHeight * 0.5f) { missingModNames += "\n\n" + TextManager.GetWithVariable("workshopitemdownloadprompttruncated", "[number]", (missingPackages.Count - displayedModCount).ToString()); break; } } missingModNames += "\n\n"; var msgBox = new GUIMessageBox( TextManager.Get("WorkshopItemDownloadTitle"), TextManager.GetWithVariable("WorkshopItemDownloadPrompt", "[items]", missingModNames), new string[] { TextManager.Get("Yes"), TextManager.Get("No") }); msgBox.Buttons[0].OnClicked = (yesBtn, userdata) => { GameMain.ServerListScreen.Select(); GameMain.ServerListScreen.DownloadWorkshopItems(missingPackages.Select(p => p.WorkshopId), serverName, ServerConnection.EndPointString); return(true); }; msgBox.Buttons[0].OnClicked += msgBox.Close; msgBox.Buttons[1].OnClicked = msgBox.Close; } return; } if (!contentPackageOrderReceived) { GameMain.Config.BackUpModOrder(); GameMain.Config.SwapPackages(corePackage.CorePackage, regularPackages.Select(p => p.RegularPackage).ToList()); contentPackageOrderReceived = true; } SendMsgInternal(DeliveryMethod.Reliable, outMsg); break; case ConnectionInitialization.Password: if (initializationStep == ConnectionInitialization.SteamTicketAndVersion) { initializationStep = ConnectionInitialization.Password; } if (initializationStep != ConnectionInitialization.Password) { return; } bool incomingSalt = inc.ReadBoolean(); inc.ReadPadBits(); int retries = 0; if (incomingSalt) { passwordSalt = inc.ReadInt32(); } else { retries = inc.ReadInt32(); } OnRequestPassword?.Invoke(passwordSalt, retries); break; } }
public static void ClientRead(IReadMessage msg) { UInt16 id = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None; string txt = ""; string styleSetting = string.Empty; if (type != ChatMessageType.Order) { changeType = (PlayerConnectionChangeType)msg.ReadByte(); txt = msg.ReadString(); } string senderName = msg.ReadString(); Character senderCharacter = null; Client senderClient = null; bool hasSenderClient = msg.ReadBoolean(); if (hasSenderClient) { UInt64 clientId = msg.ReadUInt64(); senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId); if (senderClient != null) { senderName = senderClient.Name; } } bool hasSenderCharacter = msg.ReadBoolean(); if (hasSenderCharacter) { senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; if (senderCharacter != null) { senderName = senderCharacter.Name; } } msg.ReadPadBits(); switch (type) { case ChatMessageType.Default: break; case ChatMessageType.Order: int orderIndex = msg.ReadByte(); UInt16 targetCharacterID = msg.ReadUInt16(); Character targetCharacter = Entity.FindEntityByID(targetCharacterID) as Character; Entity targetEntity = Entity.FindEntityByID(msg.ReadUInt16()); Order orderPrefab = null; int? optionIndex = null; string orderOption = null; // The option of a Dismiss order is written differently so we know what order we target // now that the game supports multiple current orders simultaneously if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count) { orderPrefab = Order.PrefabList[orderIndex]; if (orderPrefab.Identifier != "dismissed") { optionIndex = msg.ReadByte(); } // Does the dismiss order have a specified target? else if (msg.ReadBoolean()) { int identifierCount = msg.ReadByte(); if (identifierCount > 0) { int dismissedOrderIndex = msg.ReadByte(); Order dismissedOrderPrefab = null; if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count) { dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex]; orderOption = dismissedOrderPrefab.Identifier; } if (identifierCount > 1) { int dismissedOrderOptionIndex = msg.ReadByte(); if (dismissedOrderPrefab != null) { var options = dismissedOrderPrefab.Options; if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length) { orderOption += $".{options[dismissedOrderOptionIndex]}"; } } } } } } else { optionIndex = msg.ReadByte(); } int orderPriority = msg.ReadByte(); OrderTarget orderTargetPosition = null; Order.OrderTargetType orderTargetType = (Order.OrderTargetType)msg.ReadByte(); int wallSectionIndex = 0; if (msg.ReadBoolean()) { var x = msg.ReadSingle(); var y = msg.ReadSingle(); var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, creatingFromExistingData: true); } else if (orderTargetType == Order.OrderTargetType.WallSection) { wallSectionIndex = msg.ReadByte(); } if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError("Invalid order message - order index out of bounds."); if (NetIdUtils.IdMoreRecent(id, LastID)) { LastID = id; } return; } else { orderPrefab ??= Order.PrefabList[orderIndex]; } orderOption ??= optionIndex.HasValue && optionIndex >= 0 && optionIndex < orderPrefab.Options.Length ? orderPrefab.Options[optionIndex.Value] : ""; txt = orderPrefab.GetChatMessage(targetCharacter?.Name, senderCharacter?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == senderCharacter, orderOption: orderOption); if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen) { Order order = null; switch (orderTargetType) { case Order.OrderTargetType.Entity: order = new Order(orderPrefab, targetEntity, orderPrefab.GetTargetItemComponent(targetEntity as Item), orderGiver: senderCharacter); break; case Order.OrderTargetType.Position: order = new Order(orderPrefab, orderTargetPosition, orderGiver: senderCharacter); break; case Order.OrderTargetType.WallSection: order = new Order(orderPrefab, targetEntity as Structure, wallSectionIndex, orderGiver: senderCharacter); break; } if (order != null) { if (order.TargetAllCharacters) { var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null; GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime); } else if (targetCharacter != null) { targetCharacter.SetOrder(order, orderOption, orderPriority, senderCharacter); } } } if (NetIdUtils.IdMoreRecent(id, LastID)) { GameMain.Client.AddChatMessage( new OrderChatMessage(orderPrefab, orderOption, orderPriority, txt, orderTargetPosition ?? targetEntity as ISpatialEntity, targetCharacter, senderCharacter)); LastID = id; } return; case ChatMessageType.ServerMessageBox: txt = TextManager.GetServerMessage(txt); break; case ChatMessageType.ServerMessageBoxInGame: styleSetting = msg.ReadString(); txt = TextManager.GetServerMessage(txt); break; } if (NetIdUtils.IdMoreRecent(id, LastID)) { switch (type) { case ChatMessageType.MessageBox: case ChatMessageType.ServerMessageBox: //only show the message box if the text differs from the text in the currently visible box if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt) { new GUIMessageBox("", txt); } break; case ChatMessageType.ServerMessageBoxInGame: new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting); break; case ChatMessageType.Console: DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]); break; case ChatMessageType.ServerLog: if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType)) { return; } GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType); break; default: GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType); break; } LastID = id; } }
private void HandleDataMessage(IReadMessage inc) { if (!started) { return; } UInt64 senderSteamId = inc.ReadUInt64(); byte incByte = inc.ReadByte(); bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0; bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0; bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; if (isServerMessage) { DebugConsole.ThrowError("Got server message from" + senderSteamId.ToString()); return; } if (senderSteamId != OwnerSteamID) //sender is remote, handle disconnects and heartbeats { PendingClient pendingClient = pendingClients.Find(c => c.SteamID == senderSteamId); SteamP2PConnection connectedClient = connectedClients.Find(c => c.SteamID == senderSteamId); pendingClient?.Heartbeat(); connectedClient?.Heartbeat(); if (serverSettings.BanList.IsBanned(senderSteamId, out string banReason)) { if (pendingClient != null) { RemovePendingClient(pendingClient, DisconnectReason.Banned, banReason); } else if (connectedClient != null) { Disconnect(connectedClient, DisconnectReason.Banned.ToString() + "/ " + banReason); } return; } else if (isDisconnectMessage) { if (pendingClient != null) { string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={pendingClient.Name}"; RemovePendingClient(pendingClient, DisconnectReason.Unknown, disconnectMsg); } else if (connectedClient != null) { string disconnectMsg = $"ServerMessage.HasDisconnected~[client]={connectedClient.Name}"; Disconnect(connectedClient, disconnectMsg, false); } return; } else if (isHeartbeatMessage) { //message exists solely as a heartbeat, ignore its contents return; } else if (isConnectionInitializationStep) { if (pendingClient != null) { ReadConnectionInitializationStep(pendingClient, new ReadOnlyMessage(inc.Buffer, false, inc.BytePosition, inc.LengthBytes - inc.BytePosition, null)); } else { ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); if (initializationStep == ConnectionInitialization.ConnectionStarted) { pendingClients.Add(new PendingClient(senderSteamId)); } } } else if (connectedClient != null) { UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, connectedClient); OnMessageReceived?.Invoke(connectedClient, msg); } } else //sender is owner { if (OwnerConnection != null) { (OwnerConnection as SteamP2PConnection).Heartbeat(); } if (isDisconnectMessage) { DebugConsole.ThrowError("Received disconnect message from owner"); return; } if (isServerMessage) { DebugConsole.ThrowError("Received server message from owner"); return; } if (isConnectionInitializationStep) { if (OwnerConnection == null) { string ownerName = inc.ReadString(); OwnerConnection = new SteamP2PConnection(ownerName, OwnerSteamID) { Status = NetworkConnectionStatus.Connected }; OnInitializationComplete?.Invoke(OwnerConnection); } return; } if (isHeartbeatMessage) { return; } else { UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, OwnerConnection); OnMessageReceived?.Invoke(OwnerConnection, msg); } } }
private void HandleDataMessage(IReadMessage inc) { if (!isActive) { return; } UInt64 recipientSteamId = inc.ReadUInt64(); DeliveryMethod deliveryMethod = (DeliveryMethod)inc.ReadByte(); int p2pDataStart = inc.BytePosition; byte incByte = inc.ReadByte(); bool isCompressed = (incByte & (byte)PacketHeader.IsCompressed) != 0; bool isConnectionInitializationStep = (incByte & (byte)PacketHeader.IsConnectionInitializationStep) != 0; bool isDisconnectMessage = (incByte & (byte)PacketHeader.IsDisconnectMessage) != 0; bool isServerMessage = (incByte & (byte)PacketHeader.IsServerMessage) != 0; bool isHeartbeatMessage = (incByte & (byte)PacketHeader.IsHeartbeatMessage) != 0; if (recipientSteamId != selfSteamID) { if (!isServerMessage) { DebugConsole.ThrowError("Received non-server message meant for remote peer"); return; } RemotePeer peer = remotePeers.Find(p => p.SteamID == recipientSteamId); if (peer == null) { return; } if (isDisconnectMessage) { DisconnectPeer(peer, inc.ReadString()); return; } Steamworks.P2PSend sendType; switch (deliveryMethod) { case DeliveryMethod.Reliable: case DeliveryMethod.ReliableOrdered: //the documentation seems to suggest that the Reliable send type //enforces packet order (TODO: verify) sendType = Steamworks.P2PSend.Reliable; break; default: sendType = Steamworks.P2PSend.Unreliable; break; } byte[] p2pData; if (isConnectionInitializationStep) { p2pData = new byte[inc.LengthBytes - p2pDataStart + 8]; p2pData[0] = inc.Buffer[p2pDataStart]; Lidgren.Network.NetBitWriter.WriteUInt64(SteamManager.CurrentLobbyID, 64, p2pData, 8); Array.Copy(inc.Buffer, p2pDataStart + 1, p2pData, 9, inc.LengthBytes - p2pDataStart - 1); } else { p2pData = new byte[inc.LengthBytes - p2pDataStart]; Array.Copy(inc.Buffer, p2pDataStart, p2pData, 0, p2pData.Length); } if (p2pData.Length + 4 >= MsgConstants.MTU) { DebugConsole.Log("WARNING: message length comes close to exceeding MTU, forcing reliable send (" + p2pData.Length.ToString() + " bytes)"); sendType = Steamworks.P2PSend.Reliable; } bool successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); sentBytes += p2pData.Length; if (!successSend) { if (sendType != Steamworks.P2PSend.Reliable) { DebugConsole.Log("WARNING: message couldn't be sent unreliably, forcing reliable send (" + p2pData.Length.ToString() + " bytes)"); sendType = Steamworks.P2PSend.Reliable; successSend = Steamworks.SteamNetworking.SendP2PPacket(recipientSteamId, p2pData, p2pData.Length, 0, sendType); sentBytes += p2pData.Length; } if (!successSend) { DebugConsole.ThrowError("Failed to send message to remote peer! (" + p2pData.Length.ToString() + " bytes)"); } } } else { if (isDisconnectMessage) { DebugConsole.ThrowError("Received disconnect message from owned server"); return; } if (!isServerMessage) { DebugConsole.ThrowError("Received non-server message from owned server"); return; } if (isHeartbeatMessage) { return; //timeout is handled by Lidgren, ignore this message } if (isConnectionInitializationStep) { IWriteMessage outMsg = new WriteOnlyMessage(); outMsg.Write(selfSteamID); outMsg.Write((byte)(PacketHeader.IsConnectionInitializationStep)); outMsg.Write(Name); byte[] msgToSend = (byte[])outMsg.Buffer.Clone(); Array.Resize(ref msgToSend, outMsg.LengthBytes); ChildServerRelay.Write(msgToSend); return; } else { if (initializationStep != ConnectionInitialization.Success) { OnInitializationComplete?.Invoke(); initializationStep = ConnectionInitialization.Success; } UInt16 length = inc.ReadUInt16(); IReadMessage msg = new ReadOnlyMessage(inc.Buffer, isCompressed, inc.BytePosition, length, ServerConnection); OnMessageReceived?.Invoke(msg); return; } } }
public static void ClientRead(IReadMessage msg) { UInt16 id = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); PlayerConnectionChangeType changeType = PlayerConnectionChangeType.None; string txt = ""; string styleSetting = string.Empty; if (type != ChatMessageType.Order) { changeType = (PlayerConnectionChangeType)msg.ReadByte(); txt = msg.ReadString(); } string senderName = msg.ReadString(); Character senderCharacter = null; Client senderClient = null; bool hasSenderClient = msg.ReadBoolean(); if (hasSenderClient) { UInt64 clientId = msg.ReadUInt64(); senderClient = GameMain.Client.ConnectedClients.Find(c => c.SteamID == clientId || c.ID == clientId); if (senderClient != null) { senderName = senderClient.Name; } } bool hasSenderCharacter = msg.ReadBoolean(); if (hasSenderCharacter) { senderCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; if (senderCharacter != null) { senderName = senderCharacter.Name; } } msg.ReadPadBits(); switch (type) { case ChatMessageType.Default: break; case ChatMessageType.Order: var orderMessageInfo = OrderChatMessage.ReadOrder(msg); if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError("Invalid order message - order index out of bounds."); if (NetIdUtils.IdMoreRecent(id, LastID)) { LastID = id; } return; } var orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex]; string orderOption = orderMessageInfo.OrderOption; orderOption ??= orderMessageInfo.OrderOptionIndex.HasValue && orderMessageInfo.OrderOptionIndex >= 0 && orderMessageInfo.OrderOptionIndex < orderPrefab.Options.Length ? orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value] : ""; string targetRoom; if (orderMessageInfo.TargetEntity is Hull targetHull) { targetRoom = targetHull.DisplayName; } else { targetRoom = senderCharacter?.CurrentHull?.DisplayName; } txt = orderPrefab.GetChatMessage(orderMessageInfo.TargetCharacter?.Name, targetRoom, givingOrderToSelf: orderMessageInfo.TargetCharacter == senderCharacter, orderOption: orderOption, priority: orderMessageInfo.Priority); if (GameMain.Client.GameStarted && Screen.Selected == GameMain.GameScreen) { Order order = null; switch (orderMessageInfo.TargetType) { case Order.OrderTargetType.Entity: order = new Order(orderPrefab, orderMessageInfo.TargetEntity, orderPrefab.GetTargetItemComponent(orderMessageInfo.TargetEntity as Item), orderGiver: senderCharacter); break; case Order.OrderTargetType.Position: order = new Order(orderPrefab, orderMessageInfo.TargetPosition, orderGiver: senderCharacter); break; case Order.OrderTargetType.WallSection: order = new Order(orderPrefab, orderMessageInfo.TargetEntity as Structure, orderMessageInfo.WallSectionIndex, orderGiver: senderCharacter); break; } if (order != null) { if (order.TargetAllCharacters) { var fadeOutTime = !orderPrefab.IsIgnoreOrder ? (float?)orderPrefab.FadeOutTime : null; GameMain.GameSession?.CrewManager?.AddOrder(order, fadeOutTime); } else { orderMessageInfo.TargetCharacter?.SetOrder(order, orderOption, orderMessageInfo.Priority, senderCharacter); } } } if (NetIdUtils.IdMoreRecent(id, LastID)) { GameMain.Client.AddChatMessage( new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, txt, orderMessageInfo.TargetPosition ?? orderMessageInfo.TargetEntity as ISpatialEntity, orderMessageInfo.TargetCharacter, senderCharacter)); LastID = id; } return; case ChatMessageType.ServerMessageBox: txt = TextManager.GetServerMessage(txt); break; case ChatMessageType.ServerMessageBoxInGame: styleSetting = msg.ReadString(); txt = TextManager.GetServerMessage(txt); break; } if (NetIdUtils.IdMoreRecent(id, LastID)) { switch (type) { case ChatMessageType.MessageBox: case ChatMessageType.ServerMessageBox: //only show the message box if the text differs from the text in the currently visible box if ((GUIMessageBox.VisibleBox as GUIMessageBox)?.Text?.Text != txt) { new GUIMessageBox("", txt); } break; case ChatMessageType.ServerMessageBoxInGame: new GUIMessageBox("", txt, new string[0], type: GUIMessageBox.Type.InGame, iconStyle: styleSetting); break; case ChatMessageType.Console: DebugConsole.NewMessage(txt, MessageColor[(int)ChatMessageType.Console]); break; case ChatMessageType.ServerLog: if (!Enum.TryParse(senderName, out ServerLog.MessageType messageType)) { return; } GameMain.Client.ServerSettings.ServerLog?.WriteLine(txt, messageType); break; default: GameMain.Client.AddChatMessage(txt, type, senderName, senderClient, senderCharacter, changeType); break; } LastID = id; } }
protected void ReadConnectionInitializationStep(PendingClient pendingClient, IReadMessage inc) { pendingClient.TimeOut = NetworkConnection.TimeoutThreshold; ConnectionInitialization initializationStep = (ConnectionInitialization)inc.ReadByte(); if (pendingClient.InitializationStep != initializationStep) { return; } pendingClient.UpdateTime = Timing.TotalTime + Timing.Step; switch (initializationStep) { case ConnectionInitialization.SteamTicketAndVersion: string name = Client.SanitizeName(inc.ReadString()); int ownerKey = inc.ReadInt32(); UInt64 steamId = inc.ReadUInt64(); UInt16 ticketLength = inc.ReadUInt16(); byte[] ticketBytes = inc.ReadBytes(ticketLength); if (!Client.IsValidName(name, serverSettings)) { RemovePendingClient(pendingClient, DisconnectReason.InvalidName, ""); return; } string version = inc.ReadString(); bool isCompatibleVersion = NetworkMember.IsCompatible(version, GameMain.Version.ToString()) ?? false; if (!isCompatibleVersion) { RemovePendingClient(pendingClient, DisconnectReason.InvalidVersion, $"DisconnectMessage.InvalidVersion~[version]={GameMain.Version}~[clientversion]={version}"); GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", ServerLog.MessageType.Error); DebugConsole.NewMessage(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (incompatible game version)", Microsoft.Xna.Framework.Color.Red); return; } string language = inc.ReadString(); pendingClient.Connection.Language = language; Client nameTaken = GameMain.Server.ConnectedClients.Find(c => Homoglyphs.Compare(c.Name.ToLower(), name.ToLower())); if (nameTaken != null) { RemovePendingClient(pendingClient, DisconnectReason.NameTaken, ""); GameServer.Log(name + " (" + pendingClient.SteamID.ToString() + ") couldn't join the server (name too similar to the name of the client \"" + nameTaken.Name + "\").", ServerLog.MessageType.Error); return; } if (!pendingClient.AuthSessionStarted) { ProcessAuthTicket(name, ownerKey, steamId, pendingClient, ticketBytes); } break; case ConnectionInitialization.Password: int pwLength = inc.ReadByte(); byte[] incPassword = inc.ReadBytes(pwLength); if (pendingClient.PasswordSalt == null) { DebugConsole.ThrowError("Received password message from client without salt"); return; } if (serverSettings.IsPasswordCorrect(incPassword, pendingClient.PasswordSalt.Value)) { pendingClient.InitializationStep = ConnectionInitialization.ContentPackageOrder; } else { pendingClient.Retries++; if (serverSettings.BanAfterWrongPassword && pendingClient.Retries > serverSettings.MaxPasswordRetriesBeforeBan) { string banMsg = "Failed to enter correct password too many times"; BanPendingClient(pendingClient, banMsg, null); RemovePendingClient(pendingClient, DisconnectReason.Banned, banMsg); return; } } pendingClient.UpdateTime = Timing.TotalTime; break; case ConnectionInitialization.ContentPackageOrder: pendingClient.InitializationStep = ConnectionInitialization.Success; pendingClient.UpdateTime = Timing.TotalTime; break; } }