source: abuse/branches/lol/src/net/gserver.cpp @ 732

Last change on this file since 732 was 732, checked in by Sam Hocevar, 8 years ago

build: SDL2 compilation fixes.

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