source: abuse/trunk/src/net/gserver.cpp @ 651

Last change on this file since 651 was 651, checked in by Sam Hocevar, 12 years ago

build: add a --disable-network compilation flag and get rid of most of
the CELLOS_LV2 ifdefs.

File size: 11.7 KB
Line 
1/*
2 *  Abuse - dark 2D side-scrolling platform game
3 *  Copyright (c) 1995 Crack dot Com
4 *  Copyright (c) 2005-2011 Sam Hocevar <sam@hocevar.net>
5 *
6 *  This software was released into the Public Domain. As with most public
7 *  domain software, no warranty is made or implied by Crack dot Com, by
8 *  Jonathan Clark, or by Sam Hocevar.
9 */
10
11#if defined HAVE_CONFIG_H
12#   include "config.h"
13#endif
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <fcntl.h>
18#include <unistd.h>
19#include <sys/stat.h>
20#include <sys/types.h>
21#include <string.h>
22
23#include "common.h"
24
25#include "gserver.h"
26#include "netface.h"
27#include "timing.h"
28#include "netcfg.h"
29#include "id.h"
30#include "jwindow.h"
31#include "input.h"
32#include "dev.h"
33#include "game.h"
34
35extern base_memory_struct *base;
36extern net_socket *comm_sock,*game_sock;
37
38extern net_protocol *prot;
39extern join_struct *join_array;
40extern void service_net_request();
41
42game_server::game_server()
43{
44    player_list = NULL;
45    waiting_server_input = 1;
46    reload_state = 0;
47}
48
49int game_server::total_players()
50{
51    player_client *fl = player_list;
52    int total = 1;
53    for( ; fl; fl = fl->next)
54    {
55        total++;
56    }
57    return total;
58}
59
60void game_server::game_start_wait()
61{
62  int last_count=0;
63  Jwindow *stat=NULL;
64  Event ev;
65  int abort=0;
66  while (!abort && total_players()<main_net_cfg->min_players)
67  {
68    if (last_count!=total_players())
69    {
70      if (stat) wm->close_window(stat);
71      char msg[100];
72      sprintf(msg,symbol_str("min_wait"),main_net_cfg->min_players-total_players());
73      stat=wm->new_window(100,50,-1,-1,new info_field(0, 0, ID_NULL,msg,
74                       new button(0, wm->font()->height()*2, ID_CANCEL,symbol_str("cancel_button"),NULL)  ));
75      wm->flush_screen();
76      last_count=total_players();
77    }
78
79    if (wm->IsPending())
80    {
81      do { wm->get_event(ev); }  while (ev.type==EV_MOUSE_MOVE && wm->IsPending());
82      wm->flush_screen();
83      if (ev.type==EV_MESSAGE && ev.message.id==ID_CANCEL)
84        abort=1;
85    }
86
87    service_net_request();
88  }
89  if (stat)
90  {
91    wm->close_window(stat);
92    wm->flush_screen();
93  }
94}
95
96game_server::player_client::~player_client()
97{
98  delete comm;
99  delete data_address;
100}
101
102void game_server::check_collection_complete()
103{
104  player_client *c;
105  int got_all=waiting_server_input==0;
106  int add_deletes=0;
107  for (c=player_list; c && got_all; c=c->next)
108  {
109    if (c->delete_me())
110      add_deletes=1;
111    else if (c->has_joined() && c->wait_input())
112      got_all=0;
113  }
114
115  if (add_deletes)
116  {
117    player_client *last=NULL;
118    for (c=player_list; c; )
119    {
120      if (c->delete_me())
121      {
122    base->packet.write_uint8(SCMD_DELETE_CLIENT);
123    base->packet.write_uint8(c->client_id);
124    if (c->wait_reload())
125    {
126      c->set_wait_reload(0);
127      check_reload_wait();
128    }
129
130    if (last) last->next=c->next;
131    else player_list=c->next;
132    player_client *d=c;
133    c=c->next;
134    delete d;
135      } else
136      {
137    last=c;
138    c=c->next;
139      }
140    }
141  }
142
143  if (got_all)    // see if we have input from everyone, if so send it out
144  {
145    base->packet.calc_checksum();
146
147    for (c=player_list; c; c=c->next)      // setup for next time, wait for all the input
148    {
149      if (c->has_joined())
150      {
151    c->set_wait_input(1);
152    game_sock->write(base->packet.data,base->packet.packet_size()+base->packet.packet_prefix_size(),c->data_address);
153
154      }
155    }
156
157    base->input_state=INPUT_PROCESSING; // tell engine to start processing
158    game_sock->read_unselectable();    // don't listen to this socket until we are prepared to read next tick's game data
159    waiting_server_input=1;
160  }
161}
162
163void game_server::add_engine_input()
164{
165  waiting_server_input=0;
166  base->input_state=INPUT_COLLECTING;
167  base->packet.set_tick_received(base->current_tick);
168  game_sock->read_selectable();    // we can listen for game data now that we have server input
169  check_collection_complete();
170}
171
172void game_server::add_client_input(char *buf, int size, player_client *c)
173{
174  if (c->wait_input())  // don't add if we already have it
175  {
176    base->packet.add_to_packet(buf,size);
177    c->set_wait_input(0);
178    check_collection_complete();
179  }
180}
181
182void game_server::check_reload_wait()
183{
184  player_client *d=player_list;
185  for (; d; d=d->next)
186   if (d->wait_reload()) return ;    // we are still waiting for someone to reload the game
187  base->wait_reload=0;
188}
189
190int game_server::process_client_command(player_client *c)
191{
192  uint8_t cmd;
193  if (c->comm->read(&cmd,1)!=1) return 0;
194  switch (cmd)
195  {
196    case CLCMD_REQUEST_RESEND :
197    {
198      uint8_t tick;
199      if (c->comm->read(&tick,1)!=1) return 0;
200
201      fprintf(stderr,"request for resend tick %d (game cur=%d, pack=%d, last=%d)\n",
202          tick,base->current_tick,base->packet.tick_received(),base->last_packet.tick_received());
203
204      if (tick==base->last_packet.tick_received())
205      {
206    net_packet *pack=&base->last_packet;
207    game_sock->write(pack->data,pack->packet_size()+pack->packet_prefix_size(),c->data_address);
208      }
209      return 1;
210    } break;
211    case CLCMD_RELOAD_START :
212    {
213      if (reload_state)   // already in reload state, notify client ok to start reloading
214      {
215        if (c->comm->write(&cmd,1)!=1)
216      c->set_delete_me(1);
217      } else c->set_need_reload_start_ok(1);
218      return 1;
219    } break;
220
221    case CLCMD_RELOAD_END :
222    {
223      c->set_wait_reload(0);
224      return 1;
225    } break;
226    case CLCMD_UNJOIN :
227    {
228      c->comm->write(&cmd,1);        // don't care weither this works or not
229      c->set_delete_me(1);
230      if (base->input_state==INPUT_COLLECTING)
231        check_collection_complete();
232    } break;
233  }
234  return 0;
235}
236
237
238int game_server::process_net()
239{
240  int ret=0;
241  /**************************       Any game data waiting?       **************************/
242  if ((base->input_state==INPUT_COLLECTING ||
243       base->input_state==INPUT_RELOAD)
244       && game_sock->ready_to_read())
245  {
246    net_packet tmp;
247    net_packet *use=&tmp;
248    net_address *from;
249    int bytes_received=game_sock->read(use->data,PACKET_MAX_SIZE,&from);
250
251    if (from && bytes_received)
252    {
253      // make sure we got a complete packet and the packet was not a previous game tick packet
254      if (bytes_received==use->packet_size()+use->packet_prefix_size())
255      {
256    uint16_t rec_crc=use->get_checksum();
257    if (rec_crc==use->calc_checksum())
258    {
259      player_client *f=player_list,*found=NULL;
260      for (; !found &&f; f=f->next)
261      if (f->has_joined() && from->equal(f->data_address))
262        found=f;
263      if (found)
264      {
265        if (base->current_tick==use->tick_received())
266        {
267          if (prot->debug_level(net_protocol::DB_MINOR_EVENT))
268            fprintf(stderr,"(got data from %d)",found->client_id);
269
270//          fprintf(stderr,"(got packet %d)\n",use->tick_received());
271//          { time_marker now,start; while (now.diff_time(&start)<5.0) now.get_time(); }
272
273          if (base->input_state!=INPUT_RELOAD)
274            add_client_input((char *)use->packet_data(),use->packet_size(),found);
275
276        }
277        else if (use->tick_received()==base->last_packet.tick_received())
278        {
279          if (prot->debug_level(net_protocol::DB_IMPORTANT_EVENT))
280            fprintf(stderr,"(sending old %d)\n",use->tick_received());
281
282          // if they are sending stale data we need to send them the last packet so they can catchup
283          net_packet *pack=&base->last_packet;
284          game_sock->write(pack->data,pack->packet_size()+pack->packet_prefix_size(),found->data_address);
285
286        } else if (prot->debug_level(net_protocol::DB_MAJOR_EVENT))
287          fprintf(stderr,"received stale packet (got %d, expected %d)\n",use->tick_received(),base->current_tick);
288
289      } else
290      {
291        if (prot->debug_level(net_protocol::DB_MAJOR_EVENT))
292        {
293          fprintf(stderr,"received data from unknown client\n");
294          printf("from address "); from->print();
295          printf(" first addr "); player_list->data_address->print(); printf("\n");
296        }
297      }
298
299    } else fprintf(stderr,"received packet with bad checksum\n");
300      } else fprintf(stderr,"received incomplete packet\n");
301    } else if (!from)
302      fprintf(stderr,"received data and no from\n");
303    else if (!bytes_received)
304      fprintf(stderr,"received 0 byte data\n");
305    ret=1;
306    if (from) delete from;
307
308  }
309
310
311  /**************************       Any client with commands?       **************************/
312  player_client *c;
313  for (c=player_list; c; c=c->next)
314    if (c->comm->error() || (c->comm->ready_to_read() && !process_client_command(c)))
315    {
316      c->set_delete_me(1);
317      check_collection_complete();
318    }
319    else ret=1;
320
321  return 1;
322}
323
324
325int game_server::input_missing()
326{
327
328  return 1;
329}
330
331
332
333int game_server::end_reload(int disconnect)  // notify evryone you've reloaded the level (at server request)
334{
335  player_client *c=player_list;
336  prot->select(0);
337
338  for (; c; c=c->next)
339    if (!c->delete_me() && c->wait_reload())
340    {
341      if (disconnect)
342        c->set_delete_me(1);
343      else return 0;
344    }
345
346  for (c=player_list; c; c=c->next)
347    c->set_has_joined(1);
348  reload_state=0;
349
350  return 1;
351}
352
353int game_server::start_reload()
354{
355  player_client *c=player_list;
356  reload_state=1;
357  prot->select(0);
358
359  for (; c; c=c->next)
360  {
361    if (!c->delete_me() && c->need_reload_start_ok())    // if the client is already waiting for reload state to start, send ok
362    {
363      uint8_t cmd=CLCMD_RELOAD_START;
364      if (c->comm->write(&cmd,1)!=1) { c->set_delete_me(1); }
365      c->set_need_reload_start_ok(0);
366    }
367    c->set_wait_reload(1);
368  }
369  return 1;
370}
371
372
373int game_server::isa_client(int client_id)
374{
375  player_client *c=player_list;
376  if (!client_id) return 1;
377  for (; c; c=c->next) if (c->client_id==client_id) return 1;
378  return 0;
379}
380
381int game_server::add_client(int type, net_socket *sock, net_address *from)
382{
383    if( type == CLIENT_ABUSE )
384    {
385        if( total_players() >= main_net_cfg->max_players )
386        {
387            uint8_t too_many = 2;
388            sock->write( &too_many, 1 );
389            return 0;
390        }
391
392        uint8_t reg = 1; // Of course the game is registered
393        if( sock->write( &reg, 1 ) != 1 )
394            return 0;
395
396        uint16_t our_port = lstl( main_net_cfg->port + 1 ), cport;
397        char name[256];
398        uint8_t len;
399        int16_t nkills=lstl(main_net_cfg->kills);
400
401        if( sock->read(&len,1)!=1 ||
402            sock->read(name,len)!=len ||
403            sock->read(&cport,2)!=2 ||
404            sock->write(&our_port,2)!=2 ||
405            sock->write(&nkills,2)!=2)
406        {
407            return 0;
408        }
409
410        cport=lstl(cport);
411
412        int f = -1, i;
413        for( i = 0; f == -1 && i < MAX_JOINERS; i++ )
414        {
415            if( !isa_client(i) )
416            {
417                f = i;
418                join_struct *j=base->join_list;
419                for( ; j; j = j->next )
420                {
421                    if( j->client_id == i )
422                        f = -1;
423                }
424            }
425        }
426
427        if( f == -1 )
428            return 0;
429
430        from->set_port( cport );
431
432        uint16_t client_id = lstl( f );
433        if( sock->write( &client_id, 2 ) != 2 )
434        {
435            return 0;
436        }
437        client_id=f;
438
439        join_array[client_id].next = base->join_list;
440        base->join_list = &join_array[client_id];
441        join_array[client_id].client_id = client_id;
442        strcpy( join_array[client_id].name, name );
443        player_list = new player_client( f, sock, from, player_list );
444
445        return 1;
446    }
447    else
448    {
449        return 0;
450    }
451}
452
453int game_server::kill_slackers()
454{
455  player_client *c=player_list;
456  for (; c; c=c->next)
457    if (c->wait_input())
458      c->set_delete_me(1);
459  check_collection_complete();
460  return 1;
461}
462
463int game_server::quit()
464{
465  player_client *c=player_list;
466  while (c)
467  {
468    player_client *d=c;
469    c=c->next;
470    delete d;
471  }
472  player_list=NULL;
473  return 1;
474}
475
476
477game_server::~game_server()
478{
479    quit();
480}
481
Note: See TracBrowser for help on using the repository browser.