ReceiveEvent HandleNetworkEvent() { ReceiveEvent e; using( BitReader reader = GetPooledReader() ) { DataStream stream = reader.GetStream(); int connectionId; ChannelIndex channel; e = PollReceive( stream, out connectionId, out channel ); if( e == ReceiveEvent.Error || e == ReceiveEvent.NoMoreEvents ) { return e; } // on the server, the connectionId is one to one with the client index NodeIndex client = new NodeIndex( (uint)connectionId ); switch( e ) { case ReceiveEvent.Connect: //_profiler.RecordEvent(TickType.Receive, (uint)receivedSize, MessageManager.reverseChannels[channelId], "TRANSPORT_CONNECT"); Log.Info( $"Client {client.GetClientIndex()} sent initial connection packet" ); ClientConnection c; if( _commonSettings.enableEncryption ) { // This client is required to complete the crypto-hail exchange. EllipticDiffieHellman keyExchange = SendCryptoHail(); c = new ClientConnection( client, ClientConnection.Status.PendingConnectionChallengeResponse, keyExchange ); } else { c = new ClientConnection( client, ClientConnection.Status.PendingConnectionRequest, null ); } _connection.Add( c.GetIndex(), c ); // TODO: cozeroff NO, NO, NO, check for timeouts of clients manually //StartCoroutine(ApprovalTimeout(clientId)); break; case ReceiveEvent.Message: HandleMessage( reader, client, channel ); break; case ReceiveEvent.Disconnect: //_profiler.RecordEvent(TickType.Receive, 0, "NONE", "TRANSPORT_DISCONNECT"); Log.Info( "Disconnect Event From " + client.GetClientIndex() ); // TODO: cozeroff //OnClientDisconnectServer( client ); if( _onClientDisconnect != null ) { _onClientDisconnect.Invoke( client ); } break; } } return e; }
bool Send( NodeIndex client, InternalMessage message, ChannelIndex channel, BitWriter writer, bool sendImmediately, out string error ) { int clientIndex = client.GetClientIndex(); byte[] key = _connection.GetAt(clientIndex-1).GetSharedSecretKey(); // for the server, connectionId == clientIndex return base.Send( clientIndex, key, message, channel, writer, sendImmediately, out error ); }
void HandleMessage( BitReader reader, NodeIndex client, ChannelIndex channel ) { InternalMessage messageType = UnwrapMessage( reader, client ); if( messageType == InternalMessage.INVALID ) { return; } //_profiler.StartEvent(TickType.Receive, size, channelId, messageType); Log.Info( $"Handling message {AlpacaConstant.GetName(messageType)} from client {client.GetClientIndex()}" ); ClientConnection connection = _connection.GetAt( client.GetClientIndex() - 1 ); ClientConnection.Status state = connection.GetState(); if( (state == ClientConnection.Status.PendingConnectionChallengeResponse) && (messageType != InternalMessage.ConnectionResponse ) ) { Log.Error( $"Client {client.GetClientIndex()} is pending connection response, but client sent message {AlpacaConstant.GetName(messageType)} instead." ); return; } if( (state == ClientConnection.Status.PendingConnectionRequest) && (messageType != InternalMessage.ConnectionRequest) ) { Log.Error( $"Client {client.GetClientIndex()} is pending connection request, but client sent message {AlpacaConstant.GetName(messageType)} instead." ); return; } switch( messageType ) { case InternalMessage.ConnectionRequest: OnMessageConnectionRequest( client, reader ); break; case InternalMessage.ConnectionResponse: // TODO: cozeroff crypto implementation Log.Error( "Crypto not implemented yet!" ); break; case InternalMessage.CustomServer: OnMessageCustomServer( client, reader ); break; default: Log.Error( $"Read unrecognized messageType{AlpacaConstant.GetName(messageType)}" ); break; } //_profiler.EndEvent(); }
void OnMessageCustomServer( NodeIndex clientNode, BitReader reader ) { if( _onMessageCustomServer != null ) { _onMessageCustomServer.Invoke( clientNode, reader ); } else { Log.Error( $"Received custom message from client {clientNode.GetClientIndex()} but no custom handler is set" ); } }
void OnMessageConnectionRequest( NodeIndex clientNode, BitReader reader ) { // update ClientConnection state int clientIndex = clientNode.GetClientIndex(); ClientConnection connection = _connection.GetAt( clientIndex - 1 ); connection.SetConnected(); // send the new client the data it needs, plus spawn instructions for all current entities using( BitWriter writer = GetPooledWriter() ) { writer.Packed<Int32>( clientIndex ); writer.Packed<float>( _networkTime ); writer.Packed<Int32>( NetworkTransport.GetNetworkTimestamp() ); int entityCount = _entity.GetCount(); writer.Packed<Int32>( entityCount ); Entity e; for( int i = 0; i < entityCount; ++i ) { e = _entity.GetAt(i); e.MakeSpawn().Write( writer ); } string error; if( !SendInternal( clientNode, InternalMessage.ConnectionApproved, InternalChannel.Reliable, writer, true, out error ) ) { Log.Error( $"OnMessageConnectionRequest failed to send connection approval data to new client {clientIndex}.\n{error}" ); } } // Inform old clients of the new client using( BitWriter writer = GetPooledWriter() ) { writer.Packed<Int32>( clientIndex ); for( int i = 0; i < _connection.GetCount(); ++i ) { if( i == (clientIndex - 1) ) { continue; } // skip the new client ClientConnection sibling = _connection.GetAt(i); string error; if( !SendInternal( sibling.GetId(), InternalMessage.SiblingConnected, InternalChannel.Reliable, writer, false, out error ) ) { Log.Error( $"OnMessageConnectionRequest failed to send SiblingConnected message to all other clients.\n{error}" ); } } } // callback //if( _onClientConnect != null ) { _onClientConnect.Invoke( clientNode ); } }
public Entity SpawnEntityServer( NodeIndex owner, EntityPrefabIndex prefabIndex, Vector3 position, Quaternion rotation, out string error ) { if( owner != NodeIndex.SERVER_NODE_INDEX ) { ClientConnection client = _connection.Get( owner ); if( client == null ) { error = $"Cannot spawn entity with ownerClientId {owner.GetClientIndex()}, client not yet connected!"; return null; } } // Generate unique network id uint unique_id = _entityCounter; ++_entityCounter; EntityIndex entityIndex = new EntityIndex( unique_id ); // spawn Entity.Data data = new Entity.Data() { id = entityIndex, owner = owner, prefabIndex = prefabIndex }; Entity.Spawn spawn = new Entity.Spawn( data, position, rotation ); Entity entity = Entity.SpawnEntity( _commonSettings.entityPrefab, spawn, _localIndex ); _entity.Add( entityIndex, entity ); // notify all clients that an entity was created for( int i = 0; i < _connection.GetCount(); ++i ) { string clientSendError; ClientConnection client = _connection.GetAt(i); using( BitWriter writer = GetPooledWriter() ) { spawn.Write( writer ); if( !SendInternal( client.GetId(), InternalMessage.EntityCreate, InternalChannel.Reliable, writer, false, out clientSendError ) ) { error = $"Failed to send spawn to client {i}, error is:\n{clientSendError}\n"; return null; } } } if( _onEntitySpawn != null ) { _onEntitySpawn.Invoke( entity ); } error = null; return entity; }