/// <summary> Attempts to read data from a client once </summary> /// <param name="client"> Client information to read data for </param> public void RecieveData(Client client) { // Helper method to handle reading Client.ReadState read(Client.ReadState state, int kind) { if (state.bytesRead > 0) { state.message = state.buffer.Chop(state.bytesRead); state.message = client.dec(state.message); string str = state.message.GetStringUTF8(); state.held += str; int index = state.held.IndexOf(RPCMessage.EOT); while (index >= 0) { string pulled = state.held.Substring(0, index); state.held = state.held.Remove(0, index + 1); index = state.held.IndexOf(RPCMessage.EOT); if (pulled.Length > 0) { RPCMessage msg = kind == UDP?RPCMessage.UDP(client, pulled) : RPCMessage.TCP(client, pulled); incoming.Enqueue(msg); } } } return(state); } if (client.udp != null) { try { bool canReadUDP = !client.closed && client.udp.Available > 0; EndPoint ep = client.remoteUdpHost; client.udpReadState.bytesRead = canReadUDP ? client.udp.ReceiveFrom(client.udpReadState.buffer, ref ep) : -1; client.udpReadState = read(client.udpReadState, UDP); if (canReadUDP && client.udpReadState.bytesRead > 0 && ep is IPEndPoint) { client.remoteUdpHost = (IPEndPoint)ep; Log.Info($"{client.identity} recieved from {client.remoteUdpHost}"); } } catch (Exception e) { Log.Warning($"Server.RecieveData(Client): {client.identity} Error during UDP read. {e.GetType()}. Will defer to TCP closure to disconnect.", e); } } try { client.tcpReadState.bytesRead = !client.closed && client.tcpStream.CanRead && client.tcpStream.DataAvailable ? client.tcpStream.Read(client.tcpReadState.buffer, 0, client.tcpReadState.buffer.Length) : -1; client.tcpReadState = read(client.tcpReadState, TCP); } catch (ObjectDisposedException e) { Log.Verbose($"Server.RecieveData(Client): {client.identity} Probably Disconnected. {e.GetType()}", e); Close(client); } catch (SocketException e) { Log.Verbose($"Server.RecieveData(Client): {client.identity} Probably Disconnected. {e.GetType()}", e); Close(client); } catch (IOException e) { Log.Verbose($"Server.RecieveData(Client): {client.identity} Probably timed out. {e.GetType()}", e); Close(client); } catch (InvalidOperationException e) { Log.Verbose($"Server.RecieveData(Client): {client.identity} Probably timed out. {e.GetType()}", e); Close(client); } catch (Exception e) { Log.Warning($"Server.RecieveData(Client): ", e); } }
/// <summary> Testing Method at the end of a small response chain </summary> /// <param name="msg"> RPCMessage info </param> public void Ack(RPCMessage msg) { Log.Verbose($"Ack from {msg.sender.identity}: {msg[0]}"); }
public void Pong(RPCMessage msg) { Log.Info($"Pong'd by {msg.sender.identity}"); }
/// <summary> Testing Method in the middle of a small response chain </summary> /// <param name="msg"> RPCMessage info </param> public void SynAck(RPCMessage msg) { Log.Verbose($"SynAck from {msg.sender.identity}: {msg[0]}"); msg.sender.Call(Ack, msg[0]); }
/// <summary> Connected database service </summary> /// <summary> Client -> Server RPC. Checks user and credentials to validate login, responds with <see cref="LoginResponse(RPCMessage)"/></summary> /// <param name="msg"> RPC Info. </param> public void Login(RPCMessage msg) { #if !UNITY string user = msg[0]; string hash = msg[1]; string version = msg.numArgs >= 3 ? msg[2] : "[[Version Not Set]]"; // Login flow. Credentials creds = null; LoginResult result = LoginResult.Failed_Unspecified; string reason = "none"; UserLoginInfo userInfo = null; #region Submethod CheckLogin() { // SubMethod: CheckLogin() if (version != versionCode) { Log.Debug($"{nameof(LoginService)}: Version mismatch {version}, expected {versionCode}"); reason = VERSION_MISMATCH; result = LoginResult.Failed_VersionMismatch; } else if (!usernameValidator(user)) { Log.Debug($"{nameof(LoginService)}: Bad username {user}"); reason = "Invalid Username"; result = LoginResult.Failed_BadUsername; } else if (GetLogin(msg.sender) != null) { Log.Debug($"{nameof(LoginService)}: Client {msg.sender.identity} already logged in"); reason = "Already Logged In"; result = LoginResult.Failed_ClientAlreadyLoggedIn; } else { userInfo = dbService.Get <UserLoginInfo>(nameof(userInfo.userName), user); if (userInfo == null) { Log.Debug($"{nameof(LoginService)}: User {user} not found, creating them now. "); Guid userId = Guid.NewGuid(); userInfo = CreateNewUser(msg); if (userInfo != null) { result = LoginResult.Success_Created; creds = new Credentials(user, hash, userInfo.guid); } else { result = LoginResult.Failed_CreationCooldown; reason = "Too many account creations"; } } else { // Check credentials against existing credentials. if (loginsByUserId.ContainsKey(userInfo.guid)) { reason = "Already logged in"; result = LoginResult.Failed_UserAlreadyLoggedIn; } else if (hash != userInfo.hash) { reason = "Bad credentials"; result = LoginResult.Failed_BadCredentials; } else { creds = new Credentials(user, hash, userInfo.guid); result = LoginResult.Success; } } } } // Submethod: CheckLogin() #endregion #region Submethod RecordLoginAttempt { // SubMethod: RecordLoginAttempt() LoginAttempt attempt = new LoginAttempt(); attempt.result = result; attempt.result_desc = result.ToString(); attempt.timestamp = DateTime.UtcNow; attempt.hash = hash; attempt.userName = user; attempt.ip = msg.sender.remoteIP; if (userInfo != null) { attempt.success = true; attempt.creation = result == LoginResult.Success_Created; attempt.guid = userInfo.guid; } else { attempt.success = attempt.creation = false; attempt.guid = Guid.Empty; } dbService.Save(attempt); } // Submethod: RecordLoginAttempt() #endregion #region Submethod Respond { // Submethod: Respond() if (creds == null) { msg.sender.Call(LoginResponse, "fail", reason); Log.Info($"Client {msg.sender.identity} Failed to login."); server.On(new LoginFailure_Server() { ip = msg.sender.remoteIP }); } else { var session = new Session(msg.sender, creds); loginsByClient[msg.sender] = session; loginsByUserId[creds.userId] = session; userInfo.lastLogin = DateTime.UtcNow; dbService.Save(userInfo); msg.sender.Call(LoginResponse, "succ", creds.userId); Log.Info($"Client {msg.sender.identity} logged in as user {creds.username} / {creds.userId}. "); server.On(new LoginSuccess_Server(msg.sender)); } } // Submethod: Respond() #endregion #endif }