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

Last change on this file since 646 was 646, checked in by Sam Hocevar, 9 years ago

ps3: compilation fixes for non-PS3 platforms.

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