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

Last change on this file since 494 was 494, checked in by Sam Hocevar, 11 years ago

style: remove trailing spaces, fix copyright statements.

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