Vegastrike 0.5.1 rc1  1.0
Original sources for Vegastrike Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
netserver.cpp
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 /*
18  * NetServer - Network Server Interface - written by Stephane Vaxelaire <svax@free.fr>
19  */
20 
21 #include <time.h>
22 #include <math.h>
23 #if !defined (_WIN32) || defined (__CYGWIN__)
24  #include <unistd.h>
25 #else
26  #include <io.h>
27 #endif
28 
29 #include "cmd/unit_generic.h"
30 #include "cmd/unit_util.h"
31 #include "cmd/weapon_xml.h"
32 #include "cmd/bolt.h"
33 #include "gfx/cockpit_generic.h"
34 #include "universe_util.h"
35 #include "cmd/unit_factory.h"
36 #include "load_mission.h"
37 #include "save_util.h"
38 #include "networking/client.h"
40 #include "lin_time.h"
41 #include "python/init.h"
42 #include "networking/netserver.h"
43 #include "networking/zonemgr.h"
48 #include "vsfilesystem.h"
49 #include "options.h"
52 #include "cmd/ai/script.h"
53 #include "cmd/ai/order.h"
54 #include "cmd/ai/fire.h"
55 #include "cmd/ai/fireall.h"
56 #include "cmd/ai/flybywire.h"
57 #include "cmd/ai/communication.h"
58 #include "cmd/pilot.h"
59 #include "cmd/role_bitmask.h"
60 #include "gfxlib_struct.h"
61 #include "posh.h"
62 #include "fileutil.h"
63 #include "faction_generic.h"
64 #include "cmd/unit_const_cache.h"
65 
66 #include "python/init.h"
67 #include <Python.h>
68 
69 #include "netversion.h"
71 
72 extern class vs_options game_options;
73 
75 double logintimeout;
77 double DAMAGE_ATOM;
78 double PLANET_ATOM;
79 double SAVE_ATOM;
80 
81 static const char*const MISSION_SCRIPTS_LABEL = "mission_scripts";
82 static const char*const MISSION_NAMES_LABEL = "mission_names";
83 static const char*const MISSION_DESC_LABEL = "mission_descriptions";
84 
85 #define MAXINPUT 1024
87 int nbchars;
88 
91 
92 using namespace VSFileSystem;
93 
94 //What header are these *supposed* to be defined in ???
95 extern const Unit * getUnitFromUpgradeName( const string &upgradeName, int myUnitFaction = 0 );
96 extern int GetModeFromName( const char* ); //1=add, 2=mult, 0=neither.
97 static const string LOAD_FAILED = "LOAD_FAILED";
98 //Takes in a category of an upgrade or cargo and returns true if it is any type of mountable weapon.
99 extern bool isWeapon( std::string name );
100 extern Cargo * GetMasterPartList( const char *input_buffer );
101 extern void ExecuteDirector();
102 
103 void getZoneInfoBuffer( unsigned short zoneid, NetBuffer &netbuf )
104 {
105  VSServer->zonemgr->getZoneBuffer( zoneid, netbuf );
106 }
107 
108 /*
109  *************************************************************
110  **** Constructor / Destructor ***
111  *************************************************************
112  */
113 
115 {
116  udpNetwork = new SOCKETALT();
117  this->nbclients = 0;
118  this->nbaccts = 0;
119  this->keeprun = 1;
120  this->acctserver = 0;
121  this->srvtimeout.tv_sec = 0;
122  this->srvtimeout.tv_usec = 0;
123  this->snapchanged = 0;
124  /***** number of zones should be determined as server loads zones files *****/
125  zonemgr = new ZoneMgr();
126  UpdateTime();
127  srand( (unsigned int) getNewTime() );
128  //Here 500 could be something else between 1 and 0xFFFF
129  serial_seed = (ObjSerial) ( rand()*( 500./( ( (double) (RAND_MAX) )+1 ) ) );
130  globalsave = new SaveGame( "" );
131 
132  _downloadManagerServer.reset( new VsnetDownload::Server::Manager( _sock_set ) );
133  _sock_set.addDownloadManager( _downloadManagerServer );
134 #ifdef CRYPTO
135  FileUtil::use_crypto = true;
136 #endif
137 }
138 
140 {
141  delete zonemgr;
142  delete globalsave;
143  delete udpNetwork;
144 }
145 
146 /*
147  *************************************************************
148  **** Display info on the server at startup ***
149  *************************************************************
150  */
151 
152 void NetServer::startMsg()
153 {
154  cout<<endl<<"Vegastrike Server version "<<GAMESERVER_VERSION<<endl;
155  cout<<"Written by Stephane Vaxelaire"<<endl<<endl<<endl;
156  cout<<POSH_GetArchString()<<endl;
157 }
158 
159 /*
160  *************************************************************
161  **** Start the server loop ***
162  *************************************************************
163  */
164 
165 extern void InitUnitTables(); //universe_generic.cpp
166 
167 void NetServer::start( int argc, char **argv )
168 {
169  const char *serverport = NULL;
170  int i;
171  for (i = 0; i < argc; ++i) {
172  char match = 1;
173  int j;
174  if (strncmp( argv[i], "-p", 2 ) == 0)
175  serverport = argv[i]+2;
176  else
177  match = 0;
178  if (match) {
179  for (j = i+1; j < argc; ++j)
180  argv[j-1] = argv[j];
181  argc--;
182  i--;
183  }
184  }
185  string strperiod, strtimeout, strlogintimeout, strnetatom;
186  int periodrecon;
187  keeprun = 1;
188  double savetime = 0;
189  double reconnect_time = 0;
190  double curtime = 0;
191  double snaptime = 0;
192  double planettime = 0;
193  acct_con = 1;
194  nbchars = 0;
195  memset( input_buffer, 0, MAXINPUT );
196  Packet p2;
197 
198  _sock_set.start();
199 
200  startMsg();
201  if (argc == 2) {
202  CONFIGFILE = argv[1];
203  } else {
204  CONFIGFILE = new char[42];
205  strcpy( CONFIGFILE, "vegastrike.config" );
206  }
207  cout<<"Loading server config...";
209 
210  game_options.init();
211 
212  InitUnitTables(); //universe_generic.cpp
213  //Here we say we want to only handle activity in all starsystems
215  //vs_config = new VegaConfig( SERVERCONFIGFILE);
216  cout<<" config loaded"<<endl;
217  //Save period in seconds
218  strperiod = vs_config->getVariable( "server", "saveperiod", "7200" );
219  SAVE_ATOM = atoi( strperiod.c_str() );
220  string strperiodrecon = vs_config->getVariable( "server", "reconnectperiod", "60" );
221  periodrecon = atoi( strperiodrecon.c_str() );
222  strtimeout = vs_config->getVariable( "server", "clienttimeout", "180" );
223  clienttimeout = atoi( strtimeout.c_str() );
224  strlogintimeout = vs_config->getVariable( "server", "logintimeout", "60" );
225  logintimeout = atoi( strlogintimeout.c_str() );
226 
227  this->server_password = vs_config->getVariable( "server", "server_password", "" );
228 
229  strnetatom = vs_config->getVariable( "network", "network_atom", "0.2" );
230  NETWORK_ATOM = (double) atof( strnetatom.c_str() );
231  strnetatom = vs_config->getVariable( "network", "damage_atom", "1" );
232  DAMAGE_ATOM = (double) atof( strnetatom.c_str() );
233  strnetatom = vs_config->getVariable( "network", "planet_atom", "10" );
234  PLANET_ATOM = (double) atof( strnetatom.c_str() );
235 
236  strnetatom = vs_config->getVariable( "server", "difficulty", "1" );
237  g_game.difficulty = atof( strnetatom.c_str() );
238  InitTime();
239  UpdateTime();
240  savetime = getNewTime();
241  reconnect_time = getNewTime()+periodrecon;
242  std::string configport = vs_config->getVariable( "network", "server_port", "6777" );
243  if ( configport.empty() )
244  configport = vs_config->getVariable( "network", "serverport", "6777" );
245  if (serverport == NULL)
246  serverport = configport.c_str();
247  string tmp;
248  acctserver = XMLSupport::parse_bool( vs_config->getVariable( "server", "useaccountserver",
249  vs_config->getVariable( "network", "use_account_server",
250  "false" ) ) );
251 
252  //Create and bind sockets
253  COUT<<"Initializing TCP server ..."<<endl;
254  tcpNetwork = NetUITCP::createServerSocket( atoi( serverport ), _sock_set );
255  if (tcpNetwork == NULL) {
256  COUT<<"Couldn't create TCP server - quitting"<<endl;
257  exit( -100 );
258  }
259  COUT<<"Initializing UDP server ..."<<endl;
260  *udpNetwork = NetUIUDP::createServerSocket( atoi( serverport ), _sock_set );
261  if (*udpNetwork == NULL) {
262  COUT<<"Couldn't create UDP server - quitting"<<endl;
263  exit( -100 );
264  }
265  COUT<<"done."<<endl;
266  std::string acctsrv = vs_config->getVariable( "network", "account_server_url", "" );
267  if ( acctsrv.empty() )
268  acctsrv = vs_config->getVariable( "network", "accountsrv", "" );
269  if (!acctserver) {
270  /*
271  * // Read data files ;)
272  * cout<<"Loading accounts data... ";
273  * LoadAccounts( "accounts.xml");
274  * // Gets hashtable accounts elements and put them in vector Cltacct
275  * Cltacct = getAllAccounts();
276  * cout<<Cltacct.size()<<" accounts loaded."<<endl;
277  */
278  cout<<"Not connecting to account server."<<endl;
279  } else {
280  cout<<"Initializing connection to account server..."<<endl;
281  if ( acctsrv.empty() ) {
282  cout<<"Account server IP not specified, exiting"<<endl;
283  VSExit( 1 );
284  }
285  if (acctsrv.find( '/' ) == std::string::npos) {
286  int acctport = atoi( vs_config->getVariable( "network", "accountsrvport", "" ).c_str() );
287  if (!acctport)
288  acctport = ACCT_PORT;
289  //acct_sock = NetUITCP::createSocket( acctsrv.c_str(), acctport, _sock_set );
290  } else {
291  acct_sock = new VsnetHTTPSocket( acctsrv, _sock_set );
292  }
293  if (acct_sock == NULL)
294  cerr<<"Invalid Accountserver URL... "<<endl;
295 //VSExit(1);
296  else
297  COUT<<"accountserver on socket "<<acct_sock<<" done."<<endl;
298  }
299  //Create the _Universe telling it we are on server side
300  universe_path = "";
301  universe_file = vs_config->getVariable( "server", "galaxy", "milky_way.xml" );
302  cout<<"LOADING Universe file : "<<universe_file<<endl;
303  _Universe = new Universe( argc, argv, universe_file.c_str(), true );
304  cout<<"Universe LOADED"<<endl;
305  string strmission = vs_config->getVariable( "server", "missionfile", "networking.mission" );
306  Python::init();
307  Python::test();
308  active_missions.push_back( mission = new Mission( strmission.c_str() ) );
309  mission->initMission( true );
310 
311  //Loads dynamic universe
312  string dynpath = "dynaverse.dat";
313  VSFile f;
314  VSError err = f.OpenReadOnly( dynpath, ::VSFileSystem::UnknownFile );
315  if (err > Ok) {
316  cerr<<"!!! ERROR : opening dynamic universe file "<<dynpath.c_str()<<" !!!"<<endl;
317  } else {
318  string dynaverse = f.ReadFull();
319  char *dynchar = strdup( dynaverse.c_str() );
320  globalsave->ReadSavedPackets( dynchar, true );
321  f.Close();
322  }
323  std::vector< std::vector< char > >temp = ROLES::getAllRolePriorities();
324  {
325  char hostName[128];
326  hostName[0] = '\0';
327  gethostname( hostName, 128 );
328  hostent *local = NULL;
329  cout<<endl<<endl<<" ======== SERVER IS NOW RUNNING ========"<<endl;
330  const AddressIP &adr = this->tcpNetwork->get_adr();
331  cout<<" Server Port: "<<ntohs( adr.sin_port )<<endl;
332  cout<<" Server IP Addresses: "<<endl;
333 //cout << " localhost (Local computer only)" << endl;
334  int num = 0;
335  if (hostName[0])
336 //cout << " " << hostName << " (requires DNS lookup) "
337  local = gethostbyname( hostName );
338  if (local) {
339  in_addr **localaddr = (in_addr**) local->h_addr_list;
340  for (int i = 0; i < 5 && localaddr[i]; i++) {
341  string ipaddr = inet_ntoa( *(localaddr[i]) );
342  if (ipaddr.substr( 0, 4 ) == "127.") {
343  continue;
344 //cout << " (Local computer only)";
345  } else {
346  cout<<" "<<ipaddr;
347  num++;
348  if (ipaddr.substr( 0, 8 ) == "169.254.")
349  cout<<" (Ethernet connection)";
350  else if (ipaddr.substr( 0, 8 ) == "192.168."
351  || ipaddr.substr( 0, 3 ) == "10.")
352  cout<<" (Local Area Network)";
353  else
354  cout<<" (Internet Connection)";
355  }
356  cout<<endl;
357  }
358  }
359  if (!num) {
360  cout<<" No network interfaces found associated to your hostname."<<endl;
361 #ifdef _WIN32
362  cout<<" (Consult Start-> Run-> 'cmd /k ipconfig' for your IP.)"<<endl;
363 #else
364  cout<<" (Consult the '/sbin/ifconfig' command-line tool for your IP.)"<<endl;
365 #endif
366  }
367  cout<<" You can also connect locally using 'localhost'"<<endl;
368  if (acctserver) {
369  cout<<" Public Server: "<<endl<<" ";
370  if (acctsrv.length() > 75)
371  cout<<acctsrv.substr( 0, 50 )<<"..."<<acctsrv.substr( acctsrv.length()-20, 20 )<<endl;
372  else
373  cout<<acctsrv<<endl;
374  } else {
375  if ( this->server_password.empty() )
376  cout<<" Private Server"<<endl;
377  else
378  cout<<" Private Server, Password Protected: <"<<this->server_password<<">"<<endl;
379  }
380  cout<<" --------------------------------------- "<<endl;
381  cout<<"To stop this server, hit Ctrl-C, Ctrl-\\, Ctrl-Break, or close this window."<<endl;
382  cout<<endl<<"Have fun!"<<endl<<endl;
383  }
384  //Server loop
385  while (keeprun) {
386  //int nb;
387 
388  UpdateTime();
389  if (_Universe->numPlayers() > 0)
390  ExecuteDirector();
391  //Check a key press
392  //keyset.setReadAlwaysTrue( 0);
393  //this->checkKey( keyset);
394 
395  //Check received communications
396  checkMsg( _sock_set );
397  if (acctserver && acct_con)
398  //Listen for account server answers
399  checkAcctMsg( _sock_set );
400  //And send to it the login request we received
401  //Then send clients confirmations or errors
402  curtime = getNewTime();
403  if (acctserver && !acct_con && (curtime-reconnect_time) > periodrecon) {
404  std::string netbuf;
405  reconnect_time = curtime+periodrecon;
406  //We previously lost connection to account server
407  //We try to reconnect
408  if (acct_sock)
409  delete acct_sock;
410  acct_sock = new VsnetHTTPSocket( acctsrv, _sock_set );
411  if ( acct_sock->valid() ) {
412  LI i;
413  int j = 0;
414  COUT<<">>> Reconnected accountserver on socket "<<*acct_sock<<" done."<<endl;
415  //Send a list of ingame clients
416  //Build a buffer with number of clients and client serials
417  //Put first the number of clients
418  //netbuf.addShort( nbclients);
419  addSimpleChar( netbuf, ACCT_RESYNC );
420  for (j = 0, i = allClients.begin(); i != allClients.end(); i++, j++)
421  //Add the current client's serial to the buffer
422  addSimpleString( netbuf, (*i)->callsign );
423  //Passing NULL to AddressIP arg because between servers -> only TCP
424  //Use the serial packet's field to send the number of clients
425  if ( !acct_sock->sendstr( netbuf ) )
426  COUT<<"Failure to resync, SOCKET was : "<<*acct_sock<<endl;
427  } else {
428  cerr<<">>> Reconnection to account server failed."<<endl;
429  }
430  }
431  //See if we have some timed out clients and disconnect them
432  this->checkTimedoutClients_udp();
433 
434  //Remove all clients to be disconnected
435  LI j;
436  for (j = discList.begin(); j != discList.end(); j++)
437  disconnect( (*j), __FILE__, PSEUDO__LINE__( 328 ) );
438  discList.clear();
439  //Remove all clients that logged out
440  for (j = logoutList.begin(); j != logoutList.end(); j++)
441  this->logout( (*j) );
442  logoutList.clear();
443 
444  /****************************** VS STUFF TO DO ************************************/
445  //UPDATE STAR SYSTEM -> TO INTEGRATE WITH NETWORKING
446  //PROCESS JUMPS -> MAKE UNITS CHANGE THEIR STAR SYSTEM
447 
448 //NETFIXME: Why was StarSystem->Update() commented out?
449 
450  unsigned int i;
451  /*
452  * static float nonactivesystemtime = XMLSupport::parse_float (vs_config->getVariable ("physics","InactiveSystemTime",".3"));
453  * static unsigned int numrunningsystems = XMLSupport::parse_int (vs_config->getVariable ("physics","NumRunningSystems","4"));
454  * float systime=nonactivesystemtime;
455  */
456  for (i = 0; i < _Universe->star_system.size(); i++)
457 //NETFIXME: No Director for you!
458  _Universe->star_system[i]->Update( 1, true /*need to run python serverside*/ );
460  /****************************** VS STUFF TO DO ************************************/
461  if (snapchanged && (curtime-snaptime) > NETWORK_ATOM) {
462  //COUT<<"SENDING SNAPSHOT ----------"<<end;
463  //If planet time we send planet and nebula info
464  if ( (curtime-planettime) > PLANET_ATOM ) {
465  zonemgr->broadcastSnapshots( true );
466  planettime = curtime;
467  }
468  //Otherwise we just send ships/bases... info
469  else {
470  zonemgr->broadcastSnapshots( false );
471  }
472  snapchanged = 0;
473  snaptime = curtime;
474  }
475  sendNewUnitQueue();
476  //Check for automatic server status save time (in seconds)
477  //curtime = getNewTime();
478  if ( (curtime-savetime) > SAVE_ATOM ) {
479  //Not implemented
480  cout<<">>> Saving server status... Time="<<curtime<<endl;
481  this->save();
482  savetime = curtime;
483  cout<<"<<< Finished saving."<<endl;
484  }
485  _sock_set.waste_time( 0, 10000 );
486  }
487  delete CONFIGFILE;
488  delete vs_config;
489  vs_config = NULL;
490  this->closeAllSockets();
491 }
492 
493 /*
494  *************************************************************
495  **** Check keyboard interaction ***
496  *************************************************************
497  */
498 
499 void NetServer::checkKey( SocketSet &sets )
500 {
501 #if 0
502  int memory_use = 0;
503  char c;
504  if ( sets.select( 0, 0 ) ) {
505  if (read( 0, &c, 1 ) == -1)
506  cerr<<"Error reading char on std input "<<endl;
507  if (c != 0x0a) {
509  nbchars++;
510  } else {
511  if ( !strncmp( input_buffer, "quit", 4 ) || !strncmp( input_buffer, "QUIT", 4 ) ) {
512  VSExit( 0 );
513  } else if ( !strncmp( input_buffer, "stats", 4 ) || !strncmp( input_buffer, "STATS", 4 ) ) {
514  //Display server stats
515  cout<<endl;
516  cout<<"-----------------------------------------------"<<endl;
517  cout<<"| Server stats |"<<endl;
518  cout<<"-----------------------------------------------"<<endl<<endl;
519  cout<<"\tNumber of loaded and active star systems :\t"<<_Universe->star_system.size()<<endl;
520  cout<<"\tNumber of players in all star systems :\t\t"<<allClients.size()<<endl;
521  cout<<"\t\tClients : "<<allClients.size()<<endl;
522  cout<<"\tNumber of clients waiting for authorization :\t"<<waitList.size()<<endl<<endl;
523  zonemgr->displayStats();
524  cout<<"-----------------------------------------------"<<endl;
525  cout<<"| End stats |"<<endl;
526  cout<<"-----------------------------------------------"<<endl<<endl;
527  } else if (!strncmp( input_buffer, "mem",
528  3 ) || !strncmp( input_buffer, "MEM", 3 ) || input_buffer[0] == 'm' && nbchars == 1) {
529  //Display memory usage
530  cout<<endl;
531  cout<<"-----------------------------------------------"<<endl;
532  cout<<"| Server memory usage |"<<endl;
533  cout<<"-----------------------------------------------"<<endl<<endl;
534  memory_use = sizeof (ServerSocket)*2+sizeof (class Packet)*2+sizeof (class SaveGame)+sizeof (class ZoneMgr);
535  memory_use += sizeof (int)*5+sizeof (SOCKETALT)+sizeof (struct timeval);
536  //List of clients
537  memory_use += sizeof (Client*)*allClients.size()+discList.size()*sizeof (Client*)+waitList.size()
538  *sizeof (struct WaitListEntry);
539  cout<<"\tSize of NetServer variables :\t"<<(memory_use/1024)<<" KB ("<<memory_use<<" bytes)"<<endl;
540  memory_use += zonemgr->displayMemory();
541  cout<<"\t========== TOTAL MEMORY USAGE = "
542  <<(memory_use/1024)<<" KB ("<<memory_use<<" bytes) ==========="<<endl<<endl;
543  }
544  nbchars = 0;
545  memset( input_buffer, 0, MAXINPUT );
546  }
547  }
548 #endif
549 }
550 
551 /*
552  *************************************************************
553  **** Check which clients are sending data to the server ***
554  *************************************************************
555  */
556 
557 //NETFIXME: Completely separate code logic in debug and non-debug. put #ifdef's only around print statements.
558 
559 void NetServer::checkMsg( SocketSet &sets )
560 #ifdef VSNET_DEBUG
561 {
562  ostringstream ostr;
563  bool printit = false;
564  ostr<<"Checking activity on sockets, TCP=";
565  for (LI i = allClients.begin(); i != allClients.end(); i++) {
566  ClientPtr cl = *i;
567  if ( cl->sock.isActive() ) {
568  ostr<<cl->sock.get_fd()<<"+ ";
569  printit = true;
570  this->recvMsg_tcp( cl );
571  }
572  }
573  ostr<<" ";
574  if ( udpNetwork->isActive() ) {
575  ostr<<"UDP="<<udpNetwork->get_fd()<<"+"<<ends;
576  recvMsg_udp();
577  printit = true;
578  }
579  if ( tcpNetwork->isActive() )
580  newConnection_tcp();
581  ostr<<ends;
582  if (printit) COUT<<ostr.str()<<endl;
583 }
584 #else
585 {
586  for (LI i = allClients.begin(); i != allClients.end(); i++) {
587  ClientPtr cl = *i;
588  if ( cl->tcp_sock.isActive() )
589  this->recvMsg_tcp( cl );
590  }
591  if ( udpNetwork->isActive() )
592  recvMsg_udp();
593  if ( tcpNetwork->isActive() )
594  newConnection_tcp();
595 }
596 #endif
597 
598 //Return true if ok, false if we received a late packet
599 bool NetServer::updateTimestamps( ClientPtr cltp, Packet &p )
600 {
601  assert( cltp );
602  Client *clt = cltp.get();
603 
604  bool ret = true;
605  //A packet's timestamp is in ms whereas getNewTime is in seconds
606  unsigned int int_ts = p.getTimestamp();
607 
608 //cerr<<"GOT TIMESTAMP="<<int_ts<<" latest is="<<clt->getLatestTimestamp() << " in " << p.getCommand() << endl;
609  double curtime = getNewTime();
610  //Check for late packet : compare received timestamp to the latest we have
611 //assert( int_ts >= clt->getLatestTimestamp());
612  if ( int_ts < clt->getLatestTimestamp() ) {
613  //If ts > 0xFFFFFFF0 (15 seconds before the maxin an u_int)
614  //This is not really a reliable test -> we may still have late packet in that range of timestamps
615  //Only check for late packets when sent non reliable because we need others
616 //if(p.getFlags() & SENDANDFORGET)
617 //{
618  ret = !(p.getCommand() == CMD_SNAPSHOT || p.getCommand() == CMD_POSUPDATE || p.getCommand() == CMD_PING); //only invalidates updates if its a snapshot or posupdate--same reason it updates the timestamps to begin with
619  }
620  //}
621  /*
622  * else if( clt->isTcp())
623  * {
624  * COUT << "!!!ERROR : Late packet in TCP mode : this should not happen !!!" << endl
625  * << " Previous client timestamp: " << clt->getLatestTimestamp() << "ms" << endl
626  * << " Current client timtstamp: " << int_ts << "ms" << endl;
627  * // VSExit(1);
628  * }
629  */
630  //If packet is late we don't update time vars but we process it if we have to
631  else {
632  //Update the timeout vals anytime we receive a packet
633  //Set old_timeout to the old_latest one and latest_timeout to current time in seconds
634  clt->old_timeout = clt->latest_timeout;
635  clt->latest_timeout = curtime;
636  //Packet is not late so we update timestamps only when receving a CMD_SNAPSHOT
637  //because we predict and interpolate based on the elapsed time between 2 SNAPSHOTS or PING
638  if (p.getCommand() == CMD_SNAPSHOT || p.getCommand() == CMD_POSUPDATE || p.getCommand() == CMD_PING)
639  //Set old_timestamp to the old latest_timestamp and the latest_timestamp to the received one
640  clt->setLatestTimestamp( int_ts );
641  }
642  return ret;
643 }
644 
645 /*
646  *************************************************************
647  **** Add a client in the game ***
648  *************************************************************
649  */
650 
651 void NetServer::processPacket( ClientPtr clt, unsigned char cmd, const AddressIP &ipadr, Packet &p )
652 {
653  packet = p;
654 
655  Packet p2;
656  NetBuffer netbuf( packet.getData(), packet.getDataLength() );
657  if (clt)
658  clt->versionBuf( netbuf );
659  unsigned int mount_num;
660  unsigned short zone;
661  char mis;
662  //Find the unit
663  Unit *un = NULL;
664  Unit *unclt = NULL;
665  ObjSerial target_serial;
666  ObjSerial packet_serial = p.getSerial();
667  switch (cmd)
668  {
669  case CMD_CONNECT:
670  {
671  if (!clt) break;
672  clt->netversion = packet_serial;
673  if (clt->netversion > SERVER_NETVERSION)
674  clt->netversion = SERVER_NETVERSION;
675  Packet psend;
676  NetBuffer netnewbuf;
677  netnewbuf.addSerial( SERVER_NETVERSION );
678  netnewbuf.addString( clt->cltadr.ipadr() );
679  psend.send( CMD_CONNECT, 0, netnewbuf.getData(), netnewbuf.getDataLength(), SENDRELIABLE,
680  &ipadr, clt->tcp_sock, __FILE__, PSEUDO__LINE__( 656 ) );
681  break;
682  }
683  case CMD_LOGIN:
684  if (!clt) break;
685  COUT<<">>> LOGIN REQUEST --------------------------------------"<<endl;
686  //Authenticate client
687  //Need to give the IP address of incoming message in UDP mode to store it
688  //in the Client struct
689  if (!acctserver) {
690  this->localLogin( clt, packet ); //NETFIXME--right now assume acctserver
691  } else if (!acct_con) {
692  this->sendLoginUnavailable( clt );
693  } else {
694  SOCKETALT tmpsock;
695  const AddressIP *iptmp;
696  WaitListEntry entry;
697  NetBuffer netbuf( packet.getData(), packet.getDataLength() );
698  std::string user = netbuf.getString();
699  std::string passwd = netbuf.getString();
700  //This must be a TCP client
701  entry.tcp = true;
702  entry.type = WaitListEntry::CONNECTING;
703  entry.t = clt;
704  if ( user.empty() ) {
705  sendLoginError( clt );
706  break;
707  }
708  if (clt->loginstate != Client::CONNECTED)
709  break;
710  if ( waitList.find( user ) != waitList.end() ) {
711  sendLoginAlready( clt );
712  break;
713  }
714  iptmp = &clt->cltadr;
715  tmpsock = clt->tcp_sock;
716 
717  //Redirect the login request packet to account server
718  COUT<<"Redirecting login request to account server on socket "<<*acct_sock<<endl
719  <<"*** Packet to copy length : "<<packet.getDataLength()<<endl;
720  char redirectcommand[2] = {ACCT_LOGIN, '\0'};
721  std::string redirect( redirectcommand );
722  for (unsigned int i = 0; i < _Universe->numPlayers(); i++) {
723  Cockpit *cp = _Universe->AccessCockpit( i );
724  if (cp->savegame && cp->savegame->GetCallsign() == user) {
725  COUT<<"Cannot login player "<<user<<": already exists on this server!";
726  sendLoginAlready( clt );
727  user = "";
728  }
729  }
730  if ( !user.empty() ) {
731  addSimpleString( redirect, user );
732  addSimpleString( redirect, passwd );
733  if ( !acct_sock->sendstr( redirect ) ) {
734  //NETFIXME is this in http format or binary format
735  perror( "FATAL ERROR sending redirected login request to ACCOUNT SERVER : " );
736  COUT<<"SOCKET was : "<<acct_sock<<endl;
737  this->sendLoginUnavailable( clt );
738  break;
739  }
740  this->waitList[user] = (entry);
741  clt->loginstate = Client::WAITLISTED;
742 
743  getSimpleChar( redirect );
744  clt->callsign = getSimpleString( redirect );
745  clt->passwd = getSimpleString( redirect );
746  }
747  }
748  COUT<<"<<< LOGIN REQUEST --------------------------------------"<<endl;
749  break;
750  case CMD_CHOOSESHIP:
751  if (!acctserver)
752  this->chooseShip( clt, packet );
753  //No logic since accountserver only supports one ship per player.
754  else
755  this->sendLoginError( clt ); //Client is in a confused state if it sends this here.
756  //chooseShip is currently intended to be a temporary, one-time selection.
757  //In the future it can be expanded to pick a player ship from an account if there is more than one.
758  break;
759  case CMD_ADDCLIENT:
760  //Add the client to the game
761  COUT<<">>> ADD REQUEST =( serial #"<<packet.getSerial()<<" )= --------------------------------------"<<endl;
762  //COUT<<"Received ADDCLIENT request"<<endl;
763  this->addClient( clt );
764  COUT<<"<<< ADD REQUEST --------------------------------------------------------------"<<endl;
765  break;
766  case CMD_POSUPDATE:
767  //Received a position update from a client
768 //cerr<<">>> POSITION UPDATE =( serial #"<<packet.getSerial()<<" )= --------------------------------------"<<endl;
769  this->posUpdate( clt );
770 //cerr<<"<<< POSITION UPDATE ---------------------------------------------------------------"<<endl;
771  break;
772  case CMD_PING:
773  //Nothing to do here, just receiving the packet is enough
774  //COUT<<"Got PING from serial "<<packet.getSerial()<<endl;
775  break;
776  case CMD_SERVERTIME:
777 
778  serverTimeInitUDP( clt, netbuf );
779  {}
780  break;
781  case CMD_TXTMESSAGE:
782  {
783  if (!clt) break;
784  un = clt->game_unit.GetUnit();
785  string message = netbuf.getString();
786  netbuf.Reset();
787  if ( message.empty() ) break;
788  if (message[0] == '/') {
789  string cmd, args;
790  bool local = (clt->cltadr.inaddr() == 0x0100007f);
791  if (!acctserver)
792  //NETFIXME: Trusted always true in deathmatch!
793  local = true;
794  //std::replace(message.begin(),message.end(),'#','$');
795  int cp = _Universe->whichPlayerStarship( un );
796  if (cp < 0) {
797  if (local)
798  cp = 0;
799  else
800  break;
801  }
802  std::replace( message.begin(), message.end(), '\n', ' ' );
803  std::replace( message.begin(), message.end(), '\r', ' ' );
804  string::size_type first_space = message.find( ' ' );
805  if (first_space == string::npos) {
806  cmd = message.substr( 1 );
807  } else {
808  cmd = message.substr( 1, first_space-1 );
809  args = message.substr( first_space+1 );
810  }
811  UniverseUtil::receivedCustom( cp, local, cmd, args, string() );
812  break;
813  }
814  if (!un) break;
815  message = message.substr( 0, 160 );
816  std::replace( message.begin(), message.end(), '#', '$' );
817  std::replace( message.begin(), message.end(), '\n', ' ' );
818  std::replace( message.begin(), message.end(), '\r', ' ' );
819  netbuf.addString( clt->callsign );
820  netbuf.addString( message );
822  netbuf.getData(), netbuf.getDataLength(), SENDRELIABLE,
823  __FILE__, PSEUDO__LINE__( 1293 ) );
824  //Send to concerned clients
825  zonemgr->broadcast( un->getStarSystem()->GetZone(), un->GetSerial(), &p2, true );
826  COUT<<"Received text message from client "<<clt->callsign<<endl;
827  break;
828  }
829  case CMD_LOGOUT:
830  if (clt->loginstate >= Client::LOGGEDIN) {
831  COUT<<">>> LOGOUT REQUEST =( serial #"<<packet.getSerial()<<" )= --------------------------------------"<<endl;
832  //Client wants to quit the game
833  logoutList.push_back( clt );
834  COUT<<"<<< LOGOUT REQUEST -----------------------------------------------------------------"<<endl;
835  }
836  break;
837  case CMD_CUSTOM:
838  {
839  if (!clt) break;
840  un = clt->game_unit.GetUnit();
841  int cp = _Universe->whichPlayerStarship( un );
842  //NETFIXME: CMD_CUSTOM should work with a dead unit.
843  bool trusted = (clt->cltadr.inaddr() == 0x0100007f);
844  if (!acctserver)
845  //NETFIXME: Trusted always true in deathmatch!
846  trusted = true;
847  if (cp < 0) {
848  if (trusted)
849  cp = 0;
850  else
851  break; //You died or something... too bad.
852  }
853  string cmd = netbuf.getString();
854  string args = netbuf.getString();
855  string id = netbuf.getString();
856  UniverseUtil::receivedCustom( cp, trusted, cmd, args, id );
857  break;
858  }
859  //SHOULD NOT BE USED ANYMORE
860  case CMD_ASKFILE:
861  break;
862  case CMD_SAVEACCOUNTS:
863  COUT<<"Received a save request for "
864  <<clt->callsign<<" ("<<packet_serial<<")..."<<endl;
865  un = clt->game_unit.GetUnit();
866  if (un) {
867  int cpnum = _Universe->whichPlayerStarship( un );
868  if (cpnum != -1)
869  saveAccount( cpnum );
870  }
871  break;
872  case CMD_KILL:
873  un = clt->game_unit.GetUnit();
874  if (un) {
875  un->hull = 0;
876  un->Destroy();
877  }
878  break;
879  case CMD_RESPAWN:
880  COUT<<"Received a respawning request for "
881  <<clt->callsign<<"..."<<endl;
882  {
883  //Remove the client from its current starsystem
884  Unit *oldun = clt->game_unit.GetUnit();
885  if (oldun == NULL || oldun->GetHull() <= 0) {
886  zonemgr->removeClient( clt );
887  if (oldun) oldun->Kill( true, true );
888  Cockpit *cp = loadCockpit( clt ); //Should find existing cp.
889  loadFromSavegame( clt, cp );
890  //actually cp not used
891  this->addClient( clt );
892  } else {
893  COUT<<clt->callsign<<"'s not quite dead yet laddie. Disallowing respawn\n";
894  }
895  }
896  break;
897  case CMD_SHIPDEALER:
898  {
899  std::string cargoName = netbuf.getString();
900  int type = netbuf.getChar();
901 
902  Unit *docked = NULL;
903  Unit *player = clt->game_unit.GetUnit();
904  if (!player) break;
905  int cpnum = _Universe->whichPlayerStarship( player );
906  if (cpnum == -1) break;
907  Cockpit *cp = _Universe->AccessCockpit( cpnum );
908  {
909  const Unit *un;
910  for (un_kiter ui = player->getStarSystem()->getUnitList().constIterator(); (un = *ui); ++ui)
911  if ( un->isDocked( player ) ) {
912  docked = const_cast< Unit* > (un); //Stupid STL.
913  break;
914  }
915  }
916  if (!docked) break;
917  if (type == Subcmd::BuyShip) {
918  unsigned int cargIndex = UINT_MAX;
919  Cargo *cargptr = docked->GetCargo( cargoName, cargIndex );
920  if (cargIndex == UINT_MAX || !cargptr) break;
921  if (cargptr->price > cp->credits) break;
922  /* // Do the transaction.
923  * cp->credits -= cargptr->price;
924  * sendCredits(player->GetSerial(), cp->credits);
925  * ...
926  */
927  saveAccount( cpnum );
928  player->hull = 0;
929  player->Destroy();
930  }
931  break;
932  }
933  case CMD_DOWNLOAD:
934  COUT<<">>> CMD DOWNLOAD =( serial #"<<packet.getSerial()<<" )= --------------------------------------"<<endl;
935  if (_downloadManagerServer)
936  _downloadManagerServer->addCmdDownload( clt->tcp_sock, netbuf );
937  COUT<<"<<< CMD DOWNLOAD --------------------------------------------------------------"<<endl;
938  break;
939  case CMD_FIREREQUEST:
940  //Here should put a flag on the concerned mount of the concerned Unit to say we want to fire
941  //target_serial is in fact the serial of the firing unit (client itself or turret)
942  target_serial = netbuf.getSerial();
943  un = clt->game_unit.GetUnit();
944  if (!un) {
945  COUT<<"ERROR --> Received a fire order for dead UNIT"<<endl;
946  break; //Don't fire from a dead unit...
947  }
948  zone = un->getStarSystem()->GetZone();
949  mis = netbuf.getChar();
950  mount_num = (unsigned int)netbuf.getInt32();
951  //Find the unit
952  //Set the concerned mount as ACTIVE and others as INACTIVE
953  un = zonemgr->getUnit( target_serial, zone );
954  if (un == NULL) {
955  COUT<<"ERROR --> Received a fire order for non-existing UNIT"<<endl;
956  } else {
957  if ( mount_num > un->mounts.size() ) {
958  COUT<<"ERROR recvd information about "<<mount_num<<" mounts, only "<<un->mounts.size()<<" on ship"<<std::endl;
959  mount_num = un->mounts.size();
960  }
961  printf( "[x " );
962 
963  vector< Mount >
964  ::iterator i = un->mounts.begin(); //note to self: if vector<Mount *> is ever changed to vector<Mount> remove the const_ from the const_iterator
965  for (; i != un->mounts.end(); ++i) {
966  printf( "%.1f, ", (*i).time_to_lock );
967  (*i).status = Mount::INACTIVE;
968  }
969  for (unsigned int j = 0; j < mount_num; ++j) {
970  unsigned int mnt = (unsigned int)netbuf.getInt32();
971  if (mnt < un->mounts.size() && mnt >= 0)
972  un->mounts[mnt].status = Mount::ACTIVE;
973  else
974  COUT<<"ERROR --> Received a fire order on an invalid MOUNT: "<<mount_num<<" > "<<( un->mounts.size() )
975  <<endl;
976  }
977  //Ask for fire
978  if (mis != 0)
980  else
982  printf( "]\n" );
983  }
984  break;
985  case CMD_UNFIREREQUEST:
986  target_serial = netbuf.getSerial();
987  mount_num = (unsigned int)netbuf.getInt32();
988  un = clt->game_unit.GetUnit();
989  if (!un) {
990  COUT<<"ERROR --> Received an unfire order for dead UNIT"<<endl;
991  break; //Don't fire from a dead unit...
992  }
993  zone = un->getStarSystem()->GetZone();
994  //Find the unit
995  //Set the concerned mount as ACTIVE and others as INACTIVE
996  un = zonemgr->getUnit( target_serial, zone );
997  if (un == NULL) {
998  COUT<<"ERROR --> Received an unfire order for non-existing UNIT"<<endl;
999  } else {
1000  vector< Mount >
1001  ::iterator i = un->mounts.begin(); //note to self: if vector<Mount *> is ever changed to vector<Mount> remove the const_ from the const_iterator
1002  if ( mount_num > un->mounts.size() ) {
1003  COUT<<"ERROR recvd information about "<<mount_num<<" mounts, only "<<un->mounts.size()<<" on ship"<<std::endl;
1004  mount_num = un->mounts.size();
1005  }
1006  for (; i != un->mounts.end(); ++i)
1007  (*i).status = Mount::INACTIVE;
1008  for (unsigned int j = 0; j < mount_num; j++) {
1009  unsigned int mnt = (unsigned int)netbuf.getInt32();
1010  if (mnt < un->mounts.size() && mnt >= 0)
1011  un->mounts[mnt].status = Mount::ACTIVE;
1012  else
1013  COUT<<"ERROR --> Received an unfire order on an invalid MOUNT: "<<mount_num<<" > "<<( un->mounts.size() )
1014  <<endl;
1015  }
1016  //Ask for fire
1017  un->UnFire();
1018  }
1019  break;
1020  case CMD_JUMP:
1021  {
1022  if (clt && clt->loginstate > Client::LOGGEDIN) {
1023  un = clt->game_unit.GetUnit();
1024  if (un)
1025  //Do Magic.
1026  un->ActivateJumpDrive();
1027  }
1028  break;
1029  //Everything handled by Magic. We don't need this any more.
1030  string newsystem = netbuf.getString();
1031 #ifdef CRYPTO
1032  unsigned char *server_hash = new unsigned char[FileUtil::Hash.DigestSize()];
1033  unsigned char *client_hash = netbuf.getBuffer( FileUtil::Hash.DigestSize() );
1034 #endif
1035  cerr<<"ATTEMPTING TO JUMP, BUT JUMP UNIMPLEMENTED"<<endl;
1036  bool found = false;
1037  NetBuffer netbuf2;
1038  Cockpit *cp;
1039  un = clt->game_unit.GetUnit();
1040  if (un == NULL) {
1041  COUT<<"ERROR --> Received a jump request from non-existing UNIT"<<endl;
1042  } else {
1043  cp = _Universe->isPlayerStarship( un );
1044  //Verify if there really is a jump point to the new starsystem
1045  const vector< string > &adjacent = _Universe->getAdjacentStarSystems( cp->savegame->GetStarSystem()+".system" );
1046  for (unsigned int i = 0; !found && i < adjacent.size(); i++)
1047  if (adjacent[i] == newsystem)
1048  found = true;
1049  if (found) {
1050  //Then activate jump drive to say we want to jump
1051  un->ActivateJumpDrive();
1052  //The jump reply is sent in Unit::jumpReactToCollision()
1053  //In the meantime we create the star system if it isn't loaded yet
1054  //The starsystem maybe loaded for nothing if the client has not enough warp energy to jump
1055  //but that's no big deal since they all will be loaded finally
1056  }
1057  }
1058 #ifdef CRYPTO
1059  delete[] server_hash;
1060 #endif
1061  break;
1062  }
1063  case CMD_MISSION:
1064  {
1065  Unit *sender = clt->game_unit.GetUnit();
1066  if (!sender) break;
1067  int playernum = _Universe->whichPlayerStarship( sender );
1068  if (playernum < 0) break;
1069  unsigned short type = netbuf.getShort();
1070  string qualname = netbuf.getString();
1071  int pos = netbuf.getInt32();
1072  if (type == Subcmd::TerminateMission) {
1073  //Abstracting number of actual missions running on server.
1074  //(added 1 for player's main mission).
1075  Mission *mis = Mission::getNthPlayerMission( playernum, (pos+1) );
1076  if (mis)
1077  //Found it!
1078  mis->terminateMission();
1079  } else if (type == Subcmd::AcceptMission) {
1080  string finalScript;
1081  unsigned int stringCount = getSaveStringLength( playernum, MISSION_NAMES_LABEL );
1082  unsigned int temp = getSaveStringLength( playernum, MISSION_DESC_LABEL );
1083  if (temp < stringCount) stringCount = temp;
1084  temp = getSaveStringLength( playernum, MISSION_SCRIPTS_LABEL );
1085  if (temp < stringCount) stringCount = temp;
1086  for (unsigned int i = 0; i < stringCount; i++) {
1087  if (getSaveString( playernum, MISSION_NAMES_LABEL, i ) == qualname) {
1088  finalScript = getSaveString( playernum, MISSION_SCRIPTS_LABEL, i );
1089  eraseSaveString( playernum, MISSION_SCRIPTS_LABEL, i );
1090  eraseSaveString( playernum, MISSION_NAMES_LABEL, i );
1091  eraseSaveString( playernum, MISSION_DESC_LABEL, i );
1092  break;
1093  }
1094  }
1095  if ( finalScript.empty() ) break;
1096  unsigned int oldcp = _Universe->CurrentCockpit();
1097  _Universe->SetActiveCockpit( playernum );
1099  string nission = string( "#" )+qualname;
1100  LoadMission( nission.c_str(), finalScript, false );
1102  _Universe->SetActiveCockpit( oldcp );
1103  }
1104  break;
1105  }
1106  case CMD_COMM:
1107  {
1108  ObjSerial send_to = netbuf.getSerial();
1109  Unit *targ = UniverseUtil::GetUnitFromSerial( send_to );
1110  if (!targ)
1111  break;
1112  char newEdge = netbuf.getChar();
1113  int node = netbuf.getInt32();
1114  Unit *parent = clt->game_unit.GetUnit();
1115  if (!parent)
1116  break;
1117  FSM *fsm = FactionUtil::GetConversation( parent->faction, targ->faction );
1118  int oldNode;
1119  int newNode;
1120  if (newEdge < 0) {
1121  oldNode = fsm->getDefaultState( parent->getRelation( targ ) );
1122  if ( node < 0 || (unsigned int) node >= fsm->nodes.size() )
1123  break;
1124  newNode = node; //fixme make sure it's a default node, or go to a special one.
1125  } else {
1126  oldNode = node; //fixme validate
1127  if ( oldNode < 0 || (unsigned int) node >= fsm->nodes.size() )
1128  break;
1129  if ( (unsigned int) newEdge >= fsm->nodes[oldNode].edges.size() )
1130  break;
1131  newNode = fsm->nodes[oldNode].edges[newEdge];
1132  }
1133  unsigned char sex = 0;
1134  if (parent->pilot)
1135  sex = parent->pilot->getGender();
1136  CommunicationMessage c( parent, targ, oldNode, newNode, NULL, sex );
1137  Order *oo = targ->getAIState();
1138  if (oo)
1139  oo->Communicate( c );
1140  break;
1141  }
1142  case CMD_CARGOUPGRADE:
1143  {
1144  ObjSerial buyer_ser = netbuf.getSerial();
1145  ObjSerial seller_ser = netbuf.getSerial();
1146  int quantity = netbuf.getInt32();
1147  std::string cargoName = netbuf.getString();
1148  int mountOffset = ( (int) netbuf.getInt32() );
1149  int subunitOffset = ( (int) netbuf.getInt32() );
1150  Unit *sender = clt->game_unit.GetUnit();
1151  Cockpit *sender_cpt = _Universe->isPlayerStarship( sender );
1152  if (!sender || !sender->getStarSystem() || !sender_cpt) break;
1153  zone = sender->getStarSystem()->GetZone();
1154  unsigned int cargIndex = UINT_MAX;
1155  Unit *seller = zonemgr->getUnit( seller_ser, zone );
1156  Unit *buyer = zonemgr->getUnit( buyer_ser, zone );
1157  Unit *docked = NULL;
1158  {
1159  const Unit *un;
1160  for (un_kiter ui = sender->getStarSystem()->getUnitList().constIterator(); (un = *ui); ++ui) {
1161  if ( un->isDocked( sender ) ) {
1162  docked = const_cast< Unit* > (un); //Stupid STL.
1163  break;
1164  }
1165  }
1166  }
1167  if (docked) {
1168  if (seller == sender) {
1169  buyer = docked;
1170  } else {
1171  seller = docked;
1172  buyer = sender;
1173  }
1174  } else {
1175  if (seller == sender)
1176  buyer = NULL;
1177  else
1178  seller = NULL;
1179  }
1180  Cockpit *buyer_cpt = _Universe->isPlayerStarship( buyer );
1181  Cockpit *seller_cpt = _Universe->isPlayerStarship( seller );
1182 
1183  bool sellerEmpty = false;
1184  Cargo *cargptr = NULL;
1185  if (seller)
1186  cargptr = seller->GetCargo( cargoName, cargIndex );
1187  if (!cargptr) {
1188  cargIndex = UINT_MAX;
1189  cargptr = GetMasterPartList( cargoName.c_str() );
1190  sellerEmpty = true;
1191  if (!cargptr) {
1192  fprintf( stderr, "Player id %d attempted transaction with NULL cargo %s, %d->%d\n",
1193  sender ? sender->GetSerial() : -1, cargoName.c_str(),
1194  buyer ? buyer->GetSerial() : -1, seller ? seller->GetSerial() : -1 );
1195  //Return the credits.
1196  sendCredits( sender->GetSerial(), sender_cpt->credits );
1197  break;
1198  }
1199  }
1200  Cargo carg = *cargptr;
1201  bool upgrade = false;
1202  bool weapon = false;
1203  bool didMoney = false;
1204  bool didUpgrade = false;
1205  bool repair = false;
1206  if (carg.GetCategory().find( "upgrades" ) == 0) {
1207  upgrade = true;
1208  if ( isWeapon( carg.GetCategory() ) )
1209  weapon = true;
1210  else if (!quantity && buyer == sender)
1211  repair = true;
1212  }
1213  if (weapon && quantity) {
1214  //Return the credits.
1215  sendCredits( sender->GetSerial(), sender_cpt->credits );
1216  break;
1217  }
1218  if (!weapon && sellerEmpty) {
1219  //Cargo does not exist... allowed only for mounted cargo.
1220  sendCredits( sender->GetSerial(), sender_cpt->credits );
1221  break;
1222  }
1223  if (!weapon && !quantity && !repair) {
1224  sendCredits( sender->GetSerial(), sender_cpt->credits );
1225  break;
1226  }
1227  if (seller == NULL) {
1228  sendCredits( sender->GetSerial(), sender_cpt->credits );
1229  break;
1230  }
1231  //Guaranteed: seller, sender, sender_cpt are not NULL.
1232  if (buyer == NULL) {
1233  if (cargIndex != UINT_MAX)
1234  seller->EjectCargo( cargIndex );
1235  quantity = 0; //So that the cargo won't be bought/sold again.
1236  }
1237  if (quantity && cargIndex != UINT_MAX) {
1238  //Guaranteed: buyer, sender, seller, and one cockpit are not null.
1239  //Guaranteed: Not a weapon: (weapon && quantity) is disallowed.
1240  _Universe->netLock( true );
1241  if (buyer == sender && buyer_cpt) {
1242  float creds_before = buyer_cpt->credits;
1243  float &creds = buyer_cpt->credits;
1244  if ( buyer->BuyCargo( cargIndex, quantity, seller, creds ) ) {
1245  didMoney = true;
1246  if (seller_cpt)
1247  seller_cpt->credits += (creds_before-creds);
1248  }
1249  } else if (seller == sender && seller_cpt) {
1250  float creds_before = seller_cpt->credits;
1251  float &creds = seller_cpt->credits;
1252  bool success = false;
1253  if ( !carg.GetMissionFlag() )
1254  success = seller->SellCargo( cargIndex, quantity, creds, carg, buyer );
1255  else
1256  success = seller->RemoveCargo( cargIndex, quantity, true );
1257  didMoney = success;
1258  if (success)
1259  if (buyer_cpt)
1260  buyer_cpt->credits += (creds_before-creds);
1261  }
1262  _Universe->netLock( false );
1263  }
1264  if ( (didMoney || weapon) && upgrade && (seller == sender || buyer == sender) ) {
1265  double percent; //not used.
1266  const Unit *unitCarg = getUnitFromUpgradeName( carg.GetContent(), seller->faction );
1267  if (!unitCarg) {
1268  //Return the credits.
1269  sendCredits( sender->GetSerial(), sender_cpt->credits );
1270  break; //not an upgrade, and already did cargo transactions.
1271  }
1272  int multAddMode = GetModeFromName( carg.GetContent().c_str() );
1273 
1274  //Now we're sure it's an authentic upgrade...
1275  //Wow! So much code just to perform an upgrade!
1276 
1277  string templateName;
1278  int faction; //FIXME If neither the seller nor the buyer is the sender, faction is uninitialized!!!
1279  faction = 0; //FIXME This line temporarily added by chuck_starchaser
1280  const string unitDir = GetUnitDir( sender->name.get().c_str() );
1281  if (seller == sender) {
1282  templateName = unitDir+".blank";
1283  faction = seller->faction;
1284  } else if (buyer == sender) {
1285  faction = buyer->faction;
1286  templateName = unitDir+".template";
1287  }
1288  //Get the "limiter" for the upgrade. Stats can't increase more than this.
1289  const Unit *templateUnit = UnitConstCache::getCachedConst( StringIntKey( templateName, faction ) ); //FIXME faction uninitialized!!!
1290  if (!templateUnit)
1291  templateUnit = UnitConstCache::setCachedConst( StringIntKey( templateName, faction ), //FIXME faction uninitialized!!!
1292  UnitFactory::createUnit( templateName.c_str(), true, faction ) );
1293  if (templateUnit->name == LOAD_FAILED)
1294  templateUnit = NULL;
1295  if (unitCarg->name == LOAD_FAILED) {
1296  //Return money.
1297  sendCredits( sender->GetSerial(), sender_cpt->credits );
1298  break;
1299  }
1300  if (seller == sender) {
1301  //Selling it... Downgrade time!
1302  if ( seller->canDowngrade( unitCarg, mountOffset, subunitOffset, percent, templateUnit ) ) {
1303  if (weapon) {
1304  if (seller_cpt) {
1305  didMoney = true;
1306  seller_cpt->credits += carg.GetPrice();
1307  }
1308  if (buyer && didMoney)
1309  buyer->AddCargo( carg, true );
1310  }
1311  if (didMoney) {
1312  _Universe->netLock( true );
1313  seller->Downgrade( unitCarg, mountOffset, subunitOffset, percent, templateUnit );
1314  _Universe->netLock( false );
1315  didUpgrade = true;
1316  }
1317  }
1318  } else if (buyer == sender) {
1319  //Buying it... Upgrade time!
1320  if ( buyer->canUpgrade( unitCarg, mountOffset, subunitOffset, multAddMode, true, percent,
1321  templateUnit ) ) {
1322  if (weapon) {
1323  if ( buyer_cpt && buyer_cpt->credits > carg.GetPrice() ) {
1324  buyer_cpt->credits -= carg.GetPrice();
1325  didMoney = true;
1326  }
1327  if (seller && didMoney && cargIndex != UINT_MAX)
1328  seller->RemoveCargo( cargIndex, 1, true );
1329  }
1330  if (didMoney) {
1331  _Universe->netLock( true );
1332  buyer->Upgrade( unitCarg, mountOffset, subunitOffset, multAddMode, true, percent, templateUnit );
1333  _Universe->netLock( false );
1334  didUpgrade = true;
1335  }
1336  }
1337  }
1338  }
1339  if (repair && !didMoney)
1340  didMoney = sender->RepairUpgradeCargo( &carg, seller, sender_cpt ? &sender_cpt->credits : NULL );
1341  if (sender_cpt)
1342  //The client always needs to get credits back, no matter what.
1343  sendCredits( sender->GetSerial(), sender_cpt->credits );
1344  //Otherwise, it will get stuck with 0 credits.
1345  if (didMoney) {
1346  ObjSerial buyer_ser = buyer ? buyer->GetSerial() : 0;
1347  if (!upgrade) {
1348  BroadcastCargoUpgrade( sender->GetSerial(), buyer_ser, seller->GetSerial(), cargoName,
1349  carg.GetPrice(), carg.GetMass(), carg.GetVolume(), carg.GetMissionFlag(),
1350  quantity, 0, 0, zone );
1351  } else if (didUpgrade) {
1352  BroadcastCargoUpgrade( sender->GetSerial(), buyer_ser, seller->GetSerial(), cargoName,
1353  carg.GetPrice(), carg.GetMass(), carg.GetVolume(), false,
1354  weapon || repair ? 0 : 1, mountOffset, subunitOffset, zone );
1355  }
1356  }
1357  //Completed transaction.
1358  //Send player new amount of credits.
1359  //Broadcast out cargo request.
1360  break;
1361  }
1362  case CMD_TARGET:
1363  //Received a computer targetting request
1364  target_serial = netbuf.getSerial();
1365  unclt = clt->game_unit.GetUnit();
1366  if (!unclt)
1367  break;
1368  {
1369  StarSystem *ss = unclt->getStarSystem();
1370  if (!ss) {
1371  COUT<<"StarSystem for client "<<clt->callsign
1372  <<", "<<unclt->GetSerial()<<" not found!"<<endl;
1373  break;
1374  }
1376  zone = ss->GetZone();
1377  }
1378  //NETFIXME: Make sure that serials have 0 allocated for NULL
1379  un = zonemgr->getUnit( target_serial, zone );
1380  if (unclt)
1381  //It's fine if un is null...
1382  unclt->Target( un );
1384  break;
1385  case CMD_CLOAK:
1386  {
1387  //Received a computer targetting request
1388  //target_serial = netbuf.getSerial();
1389  char engage = netbuf.getChar();
1390  unclt = clt->game_unit.GetUnit();
1391  if (!unclt)
1392  break;
1393  unclt->Cloak( engage );
1394  break;
1395  }
1396  case CMD_SCAN:
1397  //Received a target scan request
1398  //NETFIXME: WE SHOULD FIND A WAY TO CHECK THAT THE CLIENT HAS THE RIGHT SCAN SYSTEM FOR THAT
1399  target_serial = netbuf.getSerial();
1400  unclt = clt->game_unit.GetUnit();
1401  zone = unclt->activeStarSystem->GetZone(); //netbuf.getShort();
1402  un = zonemgr->getUnit( target_serial, zone );
1403  //Get the un Unit data and send it in a packet
1404  //Here we should get what a scanner could get on the target ship
1405  //Get the unit that asked for target info
1406  netbuf.Reset();
1407  unclt = zonemgr->getUnit( packet.getSerial(), zone );
1408  //float distance = UnitUtil::getDistance( unclt, un);
1409  //Add armor data
1410  /*
1411  * netbuf.addShort();
1412  * netbuf.addShort();
1413  * netbuf.addShort();
1414  * netbuf.addShort();
1415  */
1416  //Add shield data
1417  //netbuf.addFloat();
1418  //??
1419  //Add hull
1420  //netbuf.addFloat( un->hull);
1421  //Add distance
1422  //netbuf.addFloat( distance);
1423  break;
1424  case CMD_STARTNETCOMM:
1425  {
1426  un = clt->game_unit.GetUnit();
1427  if (!un) break;
1428  float freq = netbuf.getFloat();
1429  clt->comm_freq = freq;
1430  clt->secured = netbuf.getChar();
1431  clt->webcam = netbuf.getChar();
1432  clt->portaudio = netbuf.getChar();
1433  //Broadcast players with same frequency that there is a new one listening to it
1434  p2.bc_create( packet.getCommand(), packet_serial,
1435  packet.getData(), packet.getDataLength(), SENDRELIABLE,
1436  __FILE__, PSEUDO__LINE__( 1293 ) );
1437  //Send to concerned clients
1438  zonemgr->broadcast( un->getStarSystem()->GetZone(), packet_serial, &p2, true );
1439  break;
1440  }
1441  case CMD_STOPNETCOMM:
1442  un = clt->game_unit.GetUnit();
1443  if (!un) break;
1444  clt->comm_freq = -1;
1445  //float freq = netbuf.getFloat();
1446  //Broadcast players with same frequency that this client is leaving the comm session
1447  p2.bc_create( packet.getCommand(), packet_serial,
1448  packet.getData(), packet.getDataLength(), SENDRELIABLE,
1449  __FILE__, PSEUDO__LINE__( 1302 ) );
1450  //Send to concerned clients
1451  zonemgr->broadcast( un->getStarSystem()->GetZone(), packet_serial, &p2, true );
1452  break;
1453  case CMD_SOUNDSAMPLE:
1454  un = clt->game_unit.GetUnit();
1455  if (!un) break;
1456  //Broadcast sound sample to the clients in the same zone and the have PortAudio support
1457  p2.bc_create( packet.getCommand(), packet_serial,
1458  packet.getData(), packet.getDataLength(), SENDRELIABLE,
1459  __FILE__, PSEUDO__LINE__( 1341 ) );
1460  zonemgr->broadcastSample( un->getStarSystem()->GetZone(), packet_serial, &p2, clt->comm_freq );
1461 #if 0
1462  //NETFIXME maybe this code just works fine
1463  case CMD_TXTMESSAGE:
1464  un = clt->game_unit.GetUnit();
1465  if (!un) break;
1466  //Broadcast sound sample to the clients in the same zone and the have PortAudio support
1467  p2.bc_create( packet.getCommand(), packet_serial, ,
1468  SENDRELIABLE,
1469  __FILE__, PSEUDO__LINE__( 1341 ) );
1470  zonemgr->broadcastText( un->getStarSystem()->GetZone(), packet_serial, &p2, clt->comm_freq );
1471 #endif
1472  case CMD_DOCK:
1473  {
1474  Unit *docking_unit;
1475  un = clt->game_unit.GetUnit();
1476  if (!un) break;
1477  ObjSerial utdwserial = netbuf.getSerial();
1478  unsigned short zonenum = un->getStarSystem()->GetZone();
1479  cerr<<"RECEIVED a DockRequest from unit "<<un->GetSerial()<<" to unit "<<utdwserial<<" in zone "<<zonenum<<endl;
1480  docking_unit = zonemgr->getUnit( utdwserial, zonenum );
1481  if (docking_unit) {
1482  docking_unit->RequestClearance( un );
1483  int dockport = un->Dock( docking_unit )-1; //For some reason Unit::Dock adds 1.
1484  if (dockport >= 0) {
1485  this->sendDockAuthorize( un->GetSerial(), utdwserial, dockport, zonenum );
1486  int cpt = UnitUtil::isPlayerStarship( un );
1487  if (cpt >= 0) {
1488  vector< string >vec;
1489  vec.push_back( docking_unit->name );
1490  saveStringList( cpt, mission_key, vec );
1491  }
1492  } else {
1493  this->sendDockDeny( un->GetSerial(), zonenum );
1494  }
1495  } else {
1496  cerr<<"!!! ERROR : cannot dock with unit serial="<<utdwserial<<endl;
1497  }
1498  break;
1499  }
1500  case CMD_UNDOCK:
1501  {
1502  Unit *docking_unit;
1503  un = clt->game_unit.GetUnit();
1504  if (!un) break;
1505  ObjSerial utdwserial = netbuf.getSerial();
1506  unsigned short zonenum = un->getStarSystem()->GetZone();
1507  cerr<<"RECEIVED an UnDockRequest from unit "<<un->GetSerial()<<" to unit "<<utdwserial<<" in zone "<<zonenum<<endl;
1508  docking_unit = zonemgr->getUnit( utdwserial, zonenum );
1509  if (docking_unit) {
1510  bool undocked = un->UnDock( docking_unit );
1511  if (undocked) {
1512  int cpt = UnitUtil::isPlayerStarship( un );
1513  if (un && cpt >= 0) {
1514  vector< string >vec;
1515  vec.push_back( string() );
1516  saveStringList( cpt, mission_key, vec );
1517  }
1518  this->sendUnDock( un->GetSerial(), utdwserial, zonenum );
1519  }
1520  } else {
1521  cerr<<"!!! ERROR : cannot dock with unit serial="<<utdwserial<<endl;
1522  }
1523  break;
1524  }
1525  default:
1526  un = clt->game_unit.GetUnit();
1527  COUT<<"Unknown command "<<Cmd( cmd )<<" ! "<<"from client ";
1528  if (un)
1529  COUT<<un->GetSerial();
1530  else
1531  COUT<<"(Dead)";
1532  COUT<<" ("<<clt->callsign<<")";
1533  COUT<<endl;
1534  }
1535 }
1536 
1537 /*
1538  *************************************************************
1539  **** Broadcast a netbuffer to a given zone ***
1540  *************************************************************
1541  */
1542 
1543 void NetServer::broadcast( NetBuffer &netbuf, ObjSerial serial, unsigned short zone, Cmd command, bool isTcp )
1544 {
1545  Packet p;
1546  p.bc_create( command, serial,
1547  netbuf.getData(), netbuf.getDataLength(), SENDRELIABLE,
1548  __FILE__, PSEUDO__LINE__( 902 ) );
1549  zonemgr->broadcast( zone, 0, &p, isTcp );
1550 }
1551 
1552 /*
1553  *************************************************************
1554  **** Close all sockets for shutdown ***
1555  *************************************************************
1556  */
1557 
1559 {
1560  tcpNetwork->disconnect( "Closing sockettcp" );
1561  udpNetwork->disconnect( "Closing socketudp" );
1562  for (LI i = allClients.begin(); i != allClients.end(); i++) {
1563  ClientPtr cl = *i;
1564  cl->tcp_sock.disconnect( cl->callsign.c_str() );
1565  }
1566 }
1567 
1568 void NetServer::addSystem( string &sysname, string &system )
1569 {
1570  zonemgr->addSystem( sysname, system );
1571 }
1572