ovccclient-server.vala 6.17 KB
Newer Older
1
using OVCC;
2
using OVCC.Network;
3

4
5
static const string TILES_FILE   = "tiles.xml";
static const string TILESET_FILE = "tileset.xml";
6

7
8
9
10
namespace OVCCClient
{
  public errordomain ServerError
  {
11
    COMMUNICATION_FAILED,
12
13
14
    INVALID_NICKNAME,
    NICKNAME_IN_USE,
    MISSING_AUTHENTICATION,
15
    NO_OPEN_TABLE,
16
17
18
19
20
21
22
    FAILED
  }
  
  [Flags]
  public enum TablesFilter
  {
    ALL,
23
24
25
    OPEN,
    PEOPLE_WAITING,
    ASKING_FOR_PEOPLE
26
27
28
29
  }
  
  public class Server : Object
  {
30
31
32
33
34
35
    private string            host;
    private uint16            port;
    private SocketClient      socket;
    private SocketConnection  connection = null;
    private DataInputStream   input;
    private DataOutputStream  output;
36
    private SignalHandle      signal_handle = null;
37
    private Cancellable?      listen_loop_cancel = null;
38
39
    
    public Server (string host,
40
                   uint16 port)
41
42
    {
      socket = new SocketClient ();
43
44
      this.host = host;
      this.port = port;
45
46
47

      /* instance unused, just to call the class_init() */
//       Message msg = new DisconnectMessage();
48
49
50
51
52
53
    }
    
    ~Server ()
    {
    }
    
54
    public /*async*/ bool connect_to ()
55
56
      throws ServerError
    {
57
58
      Message msg;

59
      try {
60
61
        SocketConnectable addr = NetworkAddress.parse (host, port);
        connection = socket.connect (addr);
62
63
64
65
66
67
        input  = new DataInputStream  (connection.input_stream);
        output = new DataOutputStream (connection.output_stream);
      } catch (Error e) {
        warning ("Trying to connect: %s", e.message);
        throw new ServerError.FAILED ("Cannot connect to server");
      }
68
69
70
71
72
73
74
75
76
77
78
79
80
81
      try {
        msg = Message.receive (input);
      } catch (Error e) {
        warning ("Failed getting welcome messsage: %s", e.message);
        throw new ServerError.COMMUNICATION_FAILED ("Didn't receive welcome");
      }
      if (msg.message_type != MessageType.WELCOME) {
        throw new ServerError.COMMUNICATION_FAILED ("Received invalid message type");
      }
      debug ("Got welcome from server: %s", (msg as WelcomeMessage).message);

      return true;
    }

Jonathan Michalon's avatar
Jonathan Michalon committed
82
    public /*async*/ bool login_to (string login, string password)
83
84
85
86
      throws ServerError
    {
      Message msg;
      
Jonathan Michalon's avatar
Jonathan Michalon committed
87
      send_message (new LoginMessage (login, password));
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
      try {
        msg = Message.receive (input);
      } catch (Error e) {
        warning ("Failed getting login answer messsage: %s", e.message);
        throw new ServerError.COMMUNICATION_FAILED ("Didn't receive login answer");
      }
      if (msg.message_type != MessageType.LOGIN) {
        throw new ServerError.COMMUNICATION_FAILED ("Received invalid message type");
      }
      if ((msg as LoginMessage).status == LoginMessage.State.ALREADY_EXISTS) {
        throw new ServerError.NICKNAME_IN_USE ("");
      }
      if ((msg as LoginMessage).status == LoginMessage.State.MISSING_AUTHENTICATION) {
        throw new ServerError.MISSING_AUTHENTICATION ("");
      }
      if ((msg as LoginMessage).status !=  LoginMessage.State.OK) {
        throw new ServerError.COMMUNICATION_FAILED ("Invalid login status");
      }

107
108
109
110
111
112
113
      return true;
    }

    public void disconnect_from ()
      throws ServerError
      requires (connection != null)
    {
114
115
116
      if (listen_loop_cancel != null) {
        listen_loop_cancel.cancel ();
      }
117
118
119
      try {
        send_message (new DisconnectMessage ());
        connection.socket.close();
120
      } catch (Error e) {
121
        /* FIXME ? */
122
        warning ("disconnection failed: %s", e.message);
123
      }
Jonathan Michalon's avatar
Jonathan Michalon committed
124
125
126
127
      signal_handle = null;
      connection    = null;
      input         = null;
      output        = null;
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
    }

    public bool is_connected ()
    {
      return connection != null;
    }

    public void send_message (Message msg)
      throws ServerError
      requires (connection != null)
    {
      try {
        msg.send (output);
      } catch (Error e) {
        warning ("Trying to send message %s: %s", msg.get_type().name(), e.message);
        throw new ServerError.COMMUNICATION_FAILED ("Cannot send message");
      }
145
146
    }
    
147
    public List<OVCC.Game>? enumerate_tables (TablesFilter filter)
148
      requires (connection != null)
149
    {
150
      return null;
151
    }
152
153
154

    /* join a table, index -1 means any open table
     * The index is typically within the list returned by enumerate_tables() */
Jonathan Michalon's avatar
Jonathan Michalon committed
155
    public Game join_table (int index, Player player)
156
      throws ServerError
157
      requires (connection != null)
158
    {
159
160
161
162
163
164
165
      /* FIXME load right tileset and do something with stack... */
      TilesDef tiles = new TilesDef ();
      TileSet  tileset = new TileSet ();
      tiles.load (File.new_for_path (TILES_FILE));
      tileset.load (tiles, File.new_for_path (TILESET_FILE));
      
      Game game = new Game.from_tileset(tileset);
Jonathan Michalon's avatar
Jonathan Michalon committed
166
167
168
169
170
171

      /* start communication NOW: we have the game and the player and must
       * send the PLAYER_ADDED triggered by next instruction... */
      start_communication (game, player);

      game.add_player (player);
172
      
Jonathan Michalon's avatar
Jonathan Michalon committed
173
      return game;
174
    }
175
176
177
178

    private void listen_loop ()
    {
      var running = true;
179
      while (running && ! listen_loop_cancel.is_cancelled ()) {
180
        Message? msg;
181
        
182
        try {
183
184
185
          msg = Message.receive (input, listen_loop_cancel);
        } catch (IOError.CANCELLED e) {
          break;
186
187
188
189
190
191
192
193
194
195
196
197
198
        } catch (Error e) {
          warning ("error receiving data: %s", e.message);
          break;
        }
        
        if (msg == null) {
          debug ("Invalid message received");
          continue;
        }
        
        debug ("Message type %s received", msg.get_type ().name ());
        switch (msg.message_type) {
          case MessageType.SIGNAL:
199
200
201
202
203
204
            Idle.add (() => {
              if (! listen_loop_cancel.is_cancelled ()) {
                signal_handle.signal_received (msg as SignalMessage);
              }
              return false;
            });
205
206
207
208
209
210
211
            break;
          case MessageType.DISCONNECT: running = false; break;
        }
      }
      signal_handle = null;
    }

Jonathan Michalon's avatar
Jonathan Michalon committed
212
    public void start_communication (Game game, Player player)
213
    {
214
      listen_loop_cancel = new Cancellable ();
215
216
217
      signal_handle = new SignalHandle (game, player, input, output);
      Thread.create<void> (listen_loop, false);
    }
218
219
220
  }
}