private bool ReceiveMachineDisconnectedMessage(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } byte id; try { id = msg.ReadByte(); } catch { return(false); } if (id == localMachine.id) { // Some race condition occured, we should already be disconnected from host // TODO: Suitable place to end session as we were probably kicked? return(true); } if (isHost || !machineFromId.ContainsKey(id)) { // Host already removed machine and machine might have been disconnected before it fully connected return(true); } RemoveMachine(machineFromId[id]); return(true); }
private bool ReceiveGamerIdRequest(NetBuffer msg, NetworkMachine originMachine) { if (!isHost) { return(false); } if (originMachine.gamers.Count >= MaxSupportedLocalGamers) { // A single requester should not request too many gamer ids return(false); } bool available = false; byte id = 255; if (GetOpenSlotsForMachine(originMachine) > 0) { available = GetUniqueId(gamerFromId, out id); // Let host create (but not add) remote gamer directly if (available && isHost && originMachine != localMachine) { var isPrivateSlot = originMachine.isHost && GetOpenPrivateGamerSlots() > 0; var pendingGamer = new NetworkGamer(originMachine, id, isPrivateSlot, false, LoadingGamertag, LoadingGamertag); gamerFromId.Add(id, pendingGamer); } } SendGamerIdResponse(originMachine, available, id); return(true); }
private bool ReceiveGamerLeft(NetBuffer msg, NetworkMachine originMachine) { byte id; try { id = msg.ReadByte(); } catch { return(false); } if (id == 255) { return(false); } if (!gamerFromId.ContainsKey(id)) { return(false); } var gamer = gamerFromId[id]; if (gamer.machine != originMachine) { return(false); } RemoveGamer(gamer); return(true); }
private NetOutgoingMessage CreateMessageWithHeader(MessageType type, NetworkMachine recipientMachine) { var msg = peer.CreateMessage(); msg.Write((byte)type); msg.Write((byte)(recipientMachine != null ? recipientMachine.id : 255)); msg.Write((byte)localMachine.id); return(msg); }
private void AddMachine(NetworkMachine machine, NetConnection connection) { allMachines.Add(machine); machineFromId.Add(machine.id, machine); if (connection != null) { connectionFromMachine.Add(machine, connection); } }
private bool ReceiveMachineConnectedMessage(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } byte id; try { id = msg.ReadByte(); } catch { return(false); } if (id == localMachine.id) { // The host is broadcasting our local machine to everyone return(true); } NetworkMachine newMachine; if (isHost) { // Host has already added machine newMachine = machineFromId[id]; // Tell new machine about the current state of the game SendSessionStateChanged(newMachine); // Tell new machine about the machines already in the session foreach (var existingMachine in allMachines) { if (existingMachine == localMachine || existingMachine == newMachine) { continue; } SendMachineConnectedMessage(existingMachine, newMachine); } } else { newMachine = new NetworkMachine(this, false, false, id); AddMachine(newMachine, hostConnection); } // Tell new machine about our gamers foreach (var localGamer in localGamers) { SendGamerJoined(localGamer, newMachine); } return(true); }
private int GetOpenSlotsForMachine(NetworkMachine machine) { int slots = GetOpenPublicGamerSlots(); if (machine.isHost) { slots = Math.Max(slots, GetOpenPrivateGamerSlots()); } return(slots); }
private void RemoveMachine(NetworkMachine machine) { for (int i = machine.gamers.Count - 1; i >= 0; i--) { RemoveGamer(machine.gamers[i]); } allMachines.Remove(machine); machineFromId.Remove(machine.id); connectionFromMachine.Remove(machine); }
private bool ReceiveStartGame(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } state = NetworkSessionState.Playing; InvokeGameStartedEvent(new GameStartedEventArgs()); return(true); }
private void SendGamerJoined(LocalNetworkGamer localGamer, NetworkMachine recipient) { var msg = CreateMessageWithHeader(MessageType.GamerJoined, recipient); msg.Write(localGamer.id); msg.Write(localGamer.DisplayName); msg.Write(localGamer.Gamertag); msg.Write(localGamer.isPrivateSlot); msg.Write(localGamer.isReady); SendMessage(msg, NetDeliveryMethod.ReliableOrdered); }
private void SendMachineDisconnectedMessage(NetworkMachine machine) { if (!isHost) { throw new InvalidOperationException(); } var msg = CreateMessageWithHeader(MessageType.MachineDisconnected, null); msg.Write(machine.id); SendMessage(msg, NetDeliveryMethod.ReliableOrdered); }
internal NetworkGamer(NetworkMachine machine, byte id, bool isPrivateSlot, bool isReady, string displayName, string gamertag) : base() { this.session = machine.session; this.machine = machine; this.id = id; this.isPrivateSlot = isPrivateSlot; this.isReady = isReady; this.DisplayName = displayName; this.Gamertag = gamertag; }
internal void DisconnectMachine(NetworkMachine machine, NetworkSessionEndReason reason) { if (!isHost) { throw new InvalidOperationException(); } if (connectionFromMachine[machine].Status == NetConnectionStatus.Connected) { connectionFromMachine[machine].Disconnect(reason.ToString()); } }
private void SendGamerIdResponse(NetworkMachine recipient, bool success, byte id) { if (!isHost || (success && id == 255)) { throw new InvalidOperationException(); } var msg = CreateMessageWithHeader(MessageType.GamerIdResponse, recipient); msg.Write(success); msg.Write(id); SendMessage(msg, NetDeliveryMethod.ReliableOrdered); }
private bool ReceiveResetReady(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } foreach (var gamer in allGamers) { gamer.isReady = false; } return(true); }
private bool ReceiveEndGame(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } foreach (var gamer in allGamers) { gamer.isReady = false; } state = NetworkSessionState.Lobby; InvokeGameEndedEvent(new GameEndedEventArgs()); return(true); }
private void SendSessionStateChanged(NetworkMachine recipient) { if (!isHost) { throw new InvalidOperationException(); } var msg = CreateMessageWithHeader(MessageType.SessionStateChanged, recipient); msg.Write(allowHostMigration); msg.Write(allowJoinInProgress); msg.Write(maxGamers); msg.Write(privateGamerSlots); msg.Write((byte)state); properties.Pack(msg); SendMessage(msg, NetDeliveryMethod.ReliableOrdered); }
private bool ReceiveGamerIdResponse(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } bool slotAvailable; byte id; try { slotAvailable = msg.ReadBoolean(); id = msg.ReadByte(); } catch { return(false); } if (slotAvailable && id == 255) { return(false); } if (pendingSignedInGamers.Count == 0) { // Don't treat as error but host will have marked slot used by client return(true); } if (!slotAvailable) { pendingSignedInGamers.RemoveAt(0); return(true); } var signedInGamer = pendingSignedInGamers[0]; pendingSignedInGamers.RemoveAt(0); var isPrivateSlot = isHost && GetOpenPrivateGamerSlots() > 0; var localGamer = new LocalNetworkGamer(signedInGamer, localMachine, id, isPrivateSlot); AddGamer(localGamer); SendGamerJoined(localGamer, null); return(true); }
private bool ReceiveSessionStateChanged(NetBuffer msg, NetworkMachine originMachine) { if (!originMachine.isHost) { return(false); } bool allowHostMigration, allowJoinInProgress; int maxGamers, privateGamerSlots; NetworkSessionState state; NetworkSessionProperties properties = new NetworkSessionProperties(); try { allowHostMigration = msg.ReadBoolean(); allowJoinInProgress = msg.ReadBoolean(); maxGamers = msg.ReadInt32(); privateGamerSlots = msg.ReadInt32(); state = (NetworkSessionState)msg.ReadByte(); if (!properties.Unpack(msg)) { return(false); } } catch { return(false); } this.allowHostMigration = allowHostMigration; this.allowJoinInProgress = allowJoinInProgress; this.maxGamers = maxGamers; this.privateGamerSlots = privateGamerSlots; this.state = state; this.properties.CopyValuesFrom(properties); return(true); }
private bool ReceiveGamerStateChanged(NetBuffer msg, NetworkMachine originMachine) { byte id; string displayName, gamertag; bool isReady; try { id = msg.ReadByte(); displayName = msg.ReadString(); gamertag = msg.ReadString(); isReady = msg.ReadBoolean(); } catch { return(false); } if (id == 255) { return(false); } if (!gamerFromId.ContainsKey(id)) { return(false); } var gamer = gamerFromId[id]; if (gamer.machine != originMachine) { return(false); } gamer.DisplayName = displayName; gamer.Gamertag = gamertag; gamer.isReady = isReady; return(true); }
private void ReceiveMessage(NetBuffer msg, NetDeliveryMethod deliveryMethod, NetworkMachine senderMachine) { // Decode header if (msg.LengthBytes < 3) { // TODO: Kick machine? Debug.Write("Received empty message from machine " + senderMachine.id); return; } //ushort header0, header1; byte headerMsgType, headerRecipientId, headerOriginId; try { headerMsgType = msg.ReadByte(); headerRecipientId = msg.ReadByte(); headerOriginId = msg.ReadByte(); } catch { // TODO: Kick machine? Debug.WriteLine("Received message with malformed header from machine " + senderMachine.id); return; } if (headerMsgType >= MessageTypeCount) { // TODO: Kick machine? Debug.WriteLine("Received message with malformed header from machine " + senderMachine.id); return; } MessageType msgType = (MessageType)headerMsgType; bool sendToAll = headerRecipientId == 255; if ((!sendToAll && !machineFromId.ContainsKey(headerRecipientId)) || !machineFromId.ContainsKey(headerOriginId)) { if (isHost) { // TODO: Kick machine? Debug.WriteLine("Received message with malformed header from machine " + senderMachine.id); } return; } var recipientMachine = sendToAll ? null : machineFromId[headerRecipientId]; var originMachine = machineFromId[headerOriginId]; if (isHost && senderMachine != originMachine) { // TODO: Kick machine? Debug.WriteLine("Received message with malformed header from machine " + senderMachine.id); return; } if (msgType != MessageType.User) { Debug.WriteLine("R " + senderMachine.id + "(" + originMachine.id + ")->" + (recipientMachine != null ? recipientMachine.id.ToString() : "[all]") + " " + msgType); } // Handle message bool success = false; switch (msgType) { case MessageType.SessionStateChanged: success = ReceiveSessionStateChanged(msg, originMachine); break; case MessageType.MachineConnected: success = ReceiveMachineConnectedMessage(msg, originMachine); break; case MessageType.MachineDisconnected: success = ReceiveMachineDisconnectedMessage(msg, originMachine); break; case MessageType.GamerIdRequest: success = ReceiveGamerIdRequest(msg, originMachine); break; case MessageType.GamerIdResponse: success = ReceiveGamerIdResponse(msg, originMachine); break; case MessageType.GamerJoined: success = ReceiveGamerJoined(msg, originMachine, recipientMachine); break; case MessageType.GamerLeft: success = ReceiveGamerLeft(msg, originMachine); break; case MessageType.GamerStateChanged: success = ReceiveGamerStateChanged(msg, originMachine); break; case MessageType.ResetReady: success = ReceiveResetReady(msg, originMachine); break; case MessageType.StartGame: success = ReceiveStartGame(msg, originMachine); break; case MessageType.EndGame: success = ReceiveEndGame(msg, originMachine); break; case MessageType.User: success = ReceiveUserMessage(msg, originMachine); break; default: throw new NotImplementedException(); } if (!success) { // TODO: Kick machine if host and disconnect if client? Debug.WriteLine("Failed to parse last message!"); return; } // If host, forward message to peers if (isHost && senderMachine != localMachine && recipientMachine != localMachine) { if (msgType != MessageType.User) { Debug.WriteLine("Forwarding " + msgType + " message to machine " + (recipientMachine != null ? recipientMachine.id.ToString() : "[all]")); } SendMessage(CreateMessageFrom(msg), deliveryMethod, ignoreSelf: true); } }
private bool ReceiveGamerJoined(NetBuffer msg, NetworkMachine originMachine, NetworkMachine recipientMachine) { byte id; string displayName, gamertag; bool isPrivateSlot, isReady; try { id = msg.ReadByte(); displayName = msg.ReadString(); gamertag = msg.ReadString(); isPrivateSlot = msg.ReadBoolean(); isReady = msg.ReadBoolean(); } catch { return(false); } if (id == 255) { return(false); } if (originMachine.isLocal) { // Already added local gamer return(true); } if (isHost) { if (!gamerFromId.ContainsKey(id)) { // Host must know about all gamers return(false); } if (id == 0) { // Someone is impersonating the host gamer return(false); } if (recipientMachine == null || recipientMachine == localMachine) { // Host already added gamer, just update it and add it to the game var gamer = gamerFromId[id]; if (gamer.state != NetworkGamerState.Pending) { return(false); } gamer.DisplayName = displayName; gamer.Gamertag = gamertag; gamer.isPrivateSlot = isPrivateSlot; gamer.isReady = isReady; AddGamer(gamer); } else { // TODO: Make sure client is not spamming recipient } } else { if (id == 0) { // Special case for host gamer if (!originMachine.isHost) { return(false); } // Already added host gamer with id 0, just update it Host.DisplayName = displayName; Host.Gamertag = gamertag; Host.isPrivateSlot = isPrivateSlot; Host.isReady = isReady; } else { AddGamer(new NetworkGamer(originMachine, id, isPrivateSlot, isReady, displayName, gamertag)); } } return(true); }
private bool ReceiveUserMessage(NetBuffer msg, NetworkMachine originMachine) { byte senderId, recipientId; SendDataOptions options; int length; Packet packet; try { senderId = msg.ReadByte(); recipientId = msg.ReadByte(); options = (SendDataOptions)msg.ReadByte(); length = msg.ReadInt32(); packet = packetPool.Get(length); // Critical TODO: Protect memory msg.ReadBytes(packet.data, 0, length); } catch { return(false); } bool sendToAll = recipientId == 255; if (senderId == 255) { return(false); } var sender = gamerFromId.ContainsKey(senderId) ? gamerFromId[senderId] : null; // Sender can be null if gamer joined not yet received if (sender != null && sender.machine != originMachine) { return(false); } if (!IsSendDataOptionsValid(options)) { return(false); } if (sendToAll) { bool firstGamer = true; foreach (var localGamer in localGamers) { var uniquePacket = firstGamer ? packet : packetPool.GetAndFillWith(packet.data); if (!localGamer.AddInboundPacket(uniquePacket, senderId, options)) { // TODO: Kick machine if host? Debug.WriteLine("MaxDelayedInboundPacketsAllowed reached!"); return(false); } firstGamer = false; } return(true); } else { var recipient = FindGamerById(recipientId); if (recipient == null) { // TODO: Check if recipient is on list of previous gamers, for now assume true bool previousGamer = true; // Message is ok if the recipient was a gamer previously return(previousGamer); } var localGamer = recipient as LocalNetworkGamer; if (localGamer == null) { if (isHost) { // The message is meant for someone else (return true so that the message is forwarded) return(true); } else { // The message is meant for us but the local gamer is gone? The host made a mistake or our local gamer left, see above bool previousLocalGamer = true; return(previousLocalGamer); } } if (!localGamer.AddInboundPacket(packet, senderId, options)) { // TODO: Kick machine if host? Debug.WriteLine("MaxDelayedInboundPacketsAllowed reached!"); return(false); } return(true); } }
internal NetworkSession(NetPeer peer, bool isHost, byte machineId, NetworkSessionType type, NetworkSessionProperties properties, int maxGamers, int privateGamerSlots, IEnumerable <SignedInGamer> localGamers, string hostDisplayName, string hostGamertag) { if (peer.Configuration.AutoFlushSendQueue) { throw new InvalidOperationException("Peer must not flush send queue automatically"); } if (isHost && machineId != 0) { throw new InvalidOperationException("Host must have machine id 0"); } if (!isHost && machineId == 0) { throw new InvalidOperationException("Client cannot have machine id 0"); } this.peer = peer; this.isHost = isHost; this.machineId = machineId; this.type = type; this.guid = Guid.NewGuid(); this.properties = properties; this.localMachine = new NetworkMachine(this, true, isHost, machineId); this.hostMachine = isHost ? this.localMachine : new NetworkMachine(this, false, true, 0); AddMachine(this.localMachine, null); if (!isHost) { if (peer.ConnectionsCount != 1 || peer.Connections[0].Status != NetConnectionStatus.Connected) { throw new InvalidOperationException("Client peer must be connected to host before NetworkSession can be instantiated"); } hostConnection = peer.Connections[0]; hostConnection.Tag = this.hostMachine; AddMachine(this.hostMachine, hostConnection); // Add host gamer with id 0, important for NetworkSession.Host property AddGamer(new NetworkGamer(this.hostMachine, 0, privateGamerSlots > 0, false, hostDisplayName, hostGamertag)); } this.maxGamers = maxGamers; this.privateGamerSlots = privateGamerSlots; if (isHost) { // Add local gamers directly to make sure that there is always at least one gamer in the session byte id = 0; foreach (var gamer in localGamers) { AddGamer(new LocalNetworkGamer(gamer, this.localMachine, id++, GetOpenPrivateGamerSlots() > 0)); } if (allGamers.Count == 0) { throw new InvalidOperationException(); } } else { foreach (var gamer in localGamers) { AddLocalGamer(gamer); } } this.AllGamers = new GamerCollection <NetworkGamer>(new List <NetworkGamer>(), this.allGamers); this.PreviousGamers = new GamerCollection <NetworkGamer>(new List <NetworkGamer>(), this.previousGamers); this.RemoteGamers = new GamerCollection <NetworkGamer>(new List <NetworkGamer>(), this.remoteGamers); this.LocalGamers = new GamerCollection <LocalNetworkGamer>(new List <LocalNetworkGamer>(), this.localGamers); this.BytesPerSecondReceived = 0; this.BytesPerSecondSent = 0; this.SimulatedLatency = TimeSpan.Zero; this.SimulatedPacketLoss = 0.0f; SignedInGamer.SignedOut += LocalGamerSignedOut; }
private void ReceiveMessages() { NetIncomingMessage msg; while ((msg = peer.ReadMessage()) != null) { if (msg.MessageType == NetIncomingMessageType.DiscoveryRequest) { Debug.WriteLine("Local discovery request received"); if (isHost) { UpdatePublicInfo(); NetworkMasterServer.SendRequestHostsResponse(peer, msg.SenderEndPoint, true, guid, publicInfo); } } else if (msg.MessageType == NetIncomingMessageType.ConnectionApproval) { if (!isHost) { throw new InvalidOperationException(); } if (allowJoinInProgress || state == NetworkSessionState.Lobby) { byte machineId; if (GetOpenPublicGamerSlots() > 0 && GetUniqueId(machineFromId, out machineId)) { // Approved, create network machine var machine = new NetworkMachine(this, false, false, machineId); msg.SenderConnection.Tag = machine; AddMachine(machine, msg.SenderConnection); // Send approval to client containing unique machine id var hailMsg = peer.CreateMessage(); hailMsg.Write(machineId); msg.SenderConnection.Approve(hailMsg); } else { msg.SenderConnection.Deny(NetworkSessionJoinError.SessionFull.ToString()); } } else { msg.SenderConnection.Deny(NetworkSessionJoinError.SessionNotJoinable.ToString()); } } else if (msg.MessageType == NetIncomingMessageType.StatusChanged) { var status = (NetConnectionStatus)msg.ReadByte(); Debug.WriteLine("Connection status updated: " + status + " (Reason: " + msg.ReadString() + ")"); if (status == NetConnectionStatus.Connected) { if (!isHost) { throw new InvalidOperationException("A client cannot accept new connections"); } if (msg.SenderConnection.Tag == null) { throw new InvalidOperationException(); } var machine = (NetworkMachine)msg.SenderConnection.Tag; SendMachineConnectedMessage(machine, null); } else if (status == NetConnectionStatus.Disconnected) { if (msg.SenderConnection != null) { if (msg.SenderConnection.Tag == null) { throw new InvalidOperationException(); } var machine = (NetworkMachine)msg.SenderConnection.Tag; RemoveMachine(machine); if (isHost) { SendMachineDisconnectedMessage(machine); } else { string reasonString; NetworkSessionEndReason reason; if (msg.ReadString(out reasonString) && Enum.TryParse(reasonString, out reason)) { End(reason); } else { End(NetworkSessionEndReason.Disconnected); } } } } } else if (msg.MessageType == NetIncomingMessageType.Data) { if (msg.SenderConnection.Tag == null) { throw new InvalidOperationException(); } ReceiveMessage(msg, msg.DeliveryMethod, (NetworkMachine)msg.SenderConnection.Tag); } else if (msg.MessageType == NetIncomingMessageType.UnconnectedData) { if (msg.SenderEndPoint.Equals(NetworkMasterServer.ResolveEndPoint())) { MasterServerMessageType responseType; MasterServerMessageResult responseResult; if (NetworkMasterServer.ParseResponseHeader(msg, out responseType, out responseResult)) { if (responseResult == MasterServerMessageResult.Ok) { if (responseType == MasterServerMessageType.RequestGeneralInfo) { string generalInfo; if (NetworkMasterServer.ParseRequestGeneralInfoResponse(msg, out generalInfo)) { masterServerGeneralInfo = generalInfo; } } else if (responseType == MasterServerMessageType.RegisterHost) { isRegisteredAsHostWithMasterServer = true; } else if (responseType == MasterServerMessageType.UnregisterHost) { isRegisteredAsHostWithMasterServer = false; } } else { hasFailedMasterServerValidation = true; } } } else { Debug.WriteLine("Unconnected data not from master server recieved from " + msg.SenderEndPoint + ", ignoring..."); } } else { HandleLidgrenMessage(msg); } peer.Recycle(msg); if (IsDisposed) { break; } } }
internal LocalNetworkGamer(SignedInGamer signedInGamer, NetworkMachine machine, byte id, bool isPrivateSlot) : base(machine, id, isPrivateSlot, false, signedInGamer.DisplayName, signedInGamer.Gamertag) { this.signedInGamer = signedInGamer; }