server.vala 3.52 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
/* 
 * 
 * Copyright (C) 2011 Colomban Wendling <ban@herbesfolles.org>
 *                    Jonathan Michalon <studios.chalmion@no-log.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 */

21 22 23 24
using OVCC.Network;


/* TODO: add a Source to be notified of incoming messages asynchronously */
25

26 27
public class Server: ThreadedSocketService
{
28
  private List<Client>  clients = null;
29 30 31 32 33 34
  
  public string name { get; construct; }
  public uint   port { get; construct; default = 0xdead; }
  
  public signal void stopped ();
  
35 36 37
  private bool send_all (Message      message,
                         Client?      exclude = null,
                         Cancellable? cancel = null)
38 39
    throws Error
  {
40 41 42 43 44 45 46 47
    lock (this.clients) {
      foreach (var c in this.clients) {
        if (c != exclude) {
          c.send (message, cancel);
        }
      }
    }
    return true;
48 49 50 51 52 53 54
  }
  
  private bool handle_connection (ThreadedSocketService service,
                                  SocketConnection      connection,
                                  Object?               source)
  {
    debug ("New connection");
55
    var client = new Client ();
56
    try {
57 58 59 60
      client.connect (connection);
    } catch (Error e) {
      warning ("error connecting client: %s", e.message);
      return false;
61 62
    }
    
63 64 65 66 67 68 69 70 71 72 73
    lock (this.clients) {
      this.clients.prepend (client);
    }
    try {
      client.send (new WelcomeMessage ("Welcome to server %s".printf (this.name)));
    } catch {}
    
    var running = true;
    while (running) {
      Message? msg;
      
74
      try {
75
        msg = client.receive ();
76 77 78 79
      } catch (Error e) {
        warning ("error receiving data: %s", e.message);
        break;
      }
80 81 82 83 84 85 86 87 88 89 90
      
      if (msg == null) {
        debug ("Invalid message received");
        continue;
      }
      
      debug ("Message type %s received", msg.get_type ().name ());
      if (msg is StringMessage) {
        debug (" --- %s", (msg as StringMessage).message);
      }
      switch (msg.message_type) {
91 92 93 94
        case MessageType.LOGIN:
          debug ("Login with '%s' and '%s'", (msg as LoginMessage).login,
                                             (msg as LoginMessage).password);
          break;
95 96 97 98 99
        case MessageType.DISCONNECT: running = false; break;
      }
    }
    lock (this.clients) {
      this.clients.remove (client);
100
    }
101
    try { client.disconnect (); } catch { /* we don't care if disconnection failed */ }
102 103 104 105 106 107 108
    debug ("Closed connection");
    return true;
  }
  
  construct
  {
    try {
109
      this.add_inet_port ((uint16) port, null);
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
    } catch (Error e) {
      critical ("failed to add address: %s", e.message);
    }
    this.run.connect (handle_connection);
  }
  
  public Server (string name,
                 uint16 port = 0xdead)
  {
    Object (name: name, port: port);
  }
  
  /* a bit ugly since a direct call to parent method will not trigger the
   * :stopped signal */
  public new void stop ()
  {
    base.stop ();
    this.stopped ();
  }
}