ovcc-network-signals.vala 9.54 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/* 
 * 
 * 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/>.
 * 
 */

/**
 * Communicate signals over network
 */

namespace OVCC.Network
{
  public errordomain SignalError
  {
    UNMAPPED_SIGNAL,
    FAILED
  }

  public enum SignalType
  {
    PLAYER_ADDED,
    PLAYER_REMOVED,
    GAME_STATE,
Jonathan Michalon's avatar
Jonathan Michalon committed
38
    TILE_PLACED,
39
    PAWN_PLACED,
40
    TURN_FINISHED,
41 42 43 44 45 46 47 48 49 50 51
    TYPES_N;
  }



  public class SignalHandle: Object
  {
    /* objects on which the signals take effect */
    public Game   game_object     { get; construct; }
    public Player player_object   { get; construct; }

52
    private ulong[] handle_ids = new ulong[SignalType.TYPES_N];
53

54
    [Signal (detailed=true)]
55
    public signal void signal_received (SignalMessage msg);
56
    public signal void send_signal (SignalMessage msg);
57

58 59 60 61 62
    public void emit_received (SignalMessage msg)
    {
      signal_received[msg.stype.to_string ()] (msg);
    }

63 64 65 66 67 68 69 70 71
    private void stop_forward (Object o, SignalType t)
    {
      SignalHandler.block (o, handle_ids[t]);
    }
    private void resume_forward (Object o, SignalType t)
    {
      SignalHandler.unblock (o, handle_ids[t]);
    }

72
    public SignalHandle (Game game, Player player)
73 74 75
    {
      Object (game_object: game, player_object: player);

76 77 78 79 80 81 82 83 84 85 86 87
      handle_ids[SignalType.PLAYER_ADDED] =
        game_object.player_added.connect ((p) => {
            send_to_peer (SignalType.PLAYER_ADDED, "s", p.nick);
          });
      handle_ids[SignalType.PLAYER_REMOVED] =
        game_object.player_removed.connect ((p) => {
            send_to_peer (SignalType.PLAYER_REMOVED, "s", p.nick);
          });
      handle_ids[SignalType.GAME_STATE] =
        game_object.notify["state"].connect ((s, p) => {
            OVCC.GameState state = ((OVCC.Game)s).state;
            /* other states should be automatic (useful until full communication) */
88 89
            if (state == OVCC.GameState.STARTED ||
                state == OVCC.GameState.ABORTED ) {
90
              send_to_peer (SignalType.GAME_STATE, "i", state);
91
            }
92
          });
Jonathan Michalon's avatar
Jonathan Michalon committed
93 94 95 96
      handle_ids[SignalType.TILE_PLACED] =
        game_object.board.tile_added.connect ((t, p) => {
            send_to_peer (SignalType.TILE_PLACED, "(iiu)", p.x, p.y, t.rotation);
          });
97
      handle_ids[SignalType.PAWN_PLACED] =
98
        game_object.board.pawn_added.connect ((board, pawn, obj, pos) => {
99 100
            uint i = 0;
            
101
            var tile = board.get_tile (pos);
102 103 104 105 106 107 108 109 110 111 112 113 114
            var found = false;
            assert (tile != null);
            foreach (var o in tile.objects) {
              if (o == obj) {
                found = true;
                break;
              }
              i++;
            }
            assert (found);
            
            send_to_peer (SignalType.PAWN_PLACED, "(uuii)", pawn.kind, i, pos.x, pos.y);
          });
115 116 117 118 119
      handle_ids[SignalType.TURN_FINISHED] =
        game_object.turn_finished.connect (() => {
            /* actually we don't want to send anything, but currently we can't */
            send_to_peer (SignalType.TURN_FINISHED, "i", 42);
          });
120 121 122 123 124 125
      
      /* incoming signal messages handling */
      signal_received.connect ((msg) => {
          debug ("Received signal %s", msg.stype.to_string());
        });
      signal_received[SignalType.PLAYER_ADDED.to_string ()].connect ((msg) => {
126
          string nick;
127
          msg.parse ("s", out nick);
128
          debug ("Adding new player %s", nick);
129
          stop_forward (game_object, SignalType.PLAYER_ADDED);
130 131 132 133 134 135 136 137
          try {
            if (nick != player_object.nick) {
              game_object.add_player (new Player(nick));
            } else {
              game_object.add_player (player_object);
            }
          } catch (OVCC.GameError e) {
            critical ("Could not add a player (%s) as requested, you may be out of sync", e.message);
138
          }
139
          resume_forward (game_object, SignalType.PLAYER_ADDED);
140 141
        });
      signal_received[SignalType.PLAYER_REMOVED.to_string ()].connect ((msg) => {
142
          string nick;
143
          msg.parse ("s", out nick);
144 145 146
          debug ("Removing player %s", nick);
          foreach (var p in game_object.players) {
            if (p.nick == nick) {
147
              stop_forward (game_object, SignalType.PLAYER_REMOVED);
148
              game_object.remove_player (p);
149
              resume_forward (game_object, SignalType.PLAYER_REMOVED);
150 151 152
              break;
            }
          }
153 154
        });
      signal_received[SignalType.GAME_STATE.to_string ()].connect ((msg) => {
155
          OVCC.GameState s;
156
          msg.parse ("i", out s);
157 158
          debug ("Game state changed from %s to %s",
                 game_object.state.to_string(), s.to_string());
159
          stop_forward (game_object, SignalType.GAME_STATE);
160 161
          switch (s) {
            case OVCC.GameState.STARTED:
162 163 164 165 166
              try {
                game_object.start();
              } catch (GameError e) {
                critical ("Got signal to start game but couldn't: %s", e.message);
              }
167
              break;
168 169
            case OVCC.GameState.NEW:
            case OVCC.GameState.PLAYER_WAITING:
170 171 172 173 174 175
            case OVCC.GameState.FINISHED:
              break; /* should be automatic */
            case OVCC.GameState.ABORTED:
              game_object.abort();
              break;
          }
176
          resume_forward (game_object, SignalType.GAME_STATE);
177
        });
Jonathan Michalon's avatar
Jonathan Michalon committed
178 179 180 181 182 183
      signal_received[SignalType.TILE_PLACED.to_string ()].connect ((msg) => {
          Position p = Position();
          uint r;
          msg.parse ("(iiu)", out p.x, out p.y, out r);
          debug ("Current player '%s' placed tile with ID %u at %d %d, angle %u",
                 game_object.current_player.nick, game_object.current_tile.id, p.x, p.y, r);
184
          game_object.current_tile.rotate ((int) r - (int) game_object.current_tile.rotation);
Jonathan Michalon's avatar
Jonathan Michalon committed
185 186 187 188
          stop_forward (game_object.board, SignalType.TILE_PLACED);
          game_object.current_player.place_tile (p);
          resume_forward (game_object.board, SignalType.TILE_PLACED);
        });
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
      signal_received[SignalType.PAWN_PLACED.to_string ()].connect ((msg) => {
          uint        k;
          uint        i;
          Position    p = Position ();
          TileObject? o = null;
          
          msg.parse ("(uuii)", out k, out i, out p.x, out p.y);
          var tile = game_object.board.get_tile (p);
          assert (tile != null);
          foreach (var o_ in tile.objects) {
            if (i < 1) {
              o = o_;
              break;
            }
            i--;
          }
          assert (o != null);
          
          debug ("Current player '%s' placed pawn kind %u at %d %d, object %p",
                 game_object.current_player.nick, k, p.x, p.y, o);
209
          stop_forward (game_object.board, SignalType.PAWN_PLACED);
210
          game_object.current_player.place_pawn ((PawnKind) k, o, p);
211
          resume_forward (game_object.board, SignalType.PAWN_PLACED);
212
        });
213 214 215 216 217 218 219
      signal_received[SignalType.TURN_FINISHED.to_string ()].connect ((msg) => {
          debug ("Current player '%s' finished its turn",
                 game_object.current_player.nick);
          stop_forward (game_object, SignalType.TURN_FINISHED);
          game_object.turn_finished ();
          resume_forward (game_object, SignalType.TURN_FINISHED);
        });
220 221
    }

222
    private void send_to_peer (SignalType s, string format, ...)
223 224 225 226 227
    {
      var ap    = va_list();
      Variant v = new Variant.va (format, null, &ap);
      SignalMessage msg = new SignalMessage (s, v);
      debug ("Transmitting signal %s", s.to_string());
228
      send_signal (msg);
229
    }
230 231
  }

232
  public class SignalMessage: Message
233 234 235 236
  {
    public override MessageType message_type { get { return MessageType.SIGNAL; } }
    
    public SignalType stype    { get; set; default = 0; }
237 238 239
    protected uint8[] data    = null;
    protected Variant variant = null;

240 241 242 243

    public SignalMessage (SignalType s, Variant v)
    {
      stype = s;
244 245 246
      data = new uint8[v.get_size()];
      v.store (data);
      variant = v;
247
    }
248 249 250 251 252 253
    
    public override bool serialize (DataOutputStream stream,
                                    Cancellable?     cancel = null)
      throws Error
    {
      stream.put_uint16 ((uint16)stype, cancel);
254
      return write_buffer (stream, data, cancel);
255 256 257 258 259 260 261
    }

    public override bool deserialize (DataInputStream stream,
                                      Cancellable?    cancel = null)
      throws Error
    {
      stype = (SignalType)stream.read_uint16 (cancel);
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
      data = read_buffer (stream, cancel);
      return true;
    }

    public Variant get_variant (string format)
    {
      if (variant == null) {
        variant = Variant.new_from_data<Object> (new VariantType(format), data, false, this);
      }

      return variant;
    }

    public void parse (string format,
                       ...)
    {
      var ap = va_list ();
      var v  = get_variant (format);
280

281
      v.get_va (format, null, &ap);
282
    }
283 284
  }
}