server.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 2000-2002 The Exult Team
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #  include <config.h> // All the ifdefs aren't useful if we don't do this
00027 #endif
00028 
00029 // only if compiled with "exult studio support"
00030 #ifdef USE_EXULTSTUDIO
00031 
00032 #include <unistd.h>
00033 #include <fcntl.h>
00034 
00035 #if HAVE_SYS_TYPES_H
00036 #include <sys/types.h>
00037 #endif
00038 
00039 #if HAVE_SYS_TIME_H
00040 #include <sys/time.h>
00041 #endif
00042 
00043 #if HAVE_SYS_SOCKET_H
00044 #include <sys/socket.h>
00045 #endif
00046 
00047 #include <cstdio>
00048 
00049 #ifdef _AIX
00050 #include <strings.h>
00051 #endif
00052 
00053 #if HAVE_NETDB_H
00054 #include <netdb.h>
00055 #endif
00056 
00057 #ifndef WIN32
00058 #include <sys/un.h>
00059 #endif
00060 
00061 #include "server.h"
00062 #include "servemsg.h"
00063 #include "utils.h"
00064 #include "egg.h"
00065 #include "actors.h"
00066 #include "gamewin.h"
00067 #include "gamemap.h"
00068 #include "chunkter.h"
00069 #include "cheat.h"
00070 #include "objserial.h"
00071 #include "effects.h"
00072 
00073 #ifdef USECODE_DEBUGGER
00074 #include "debugserver.h"
00075 #endif
00076 
00077 #ifdef WIN32
00078 #include "servewin32.h"
00079 #include "cheat.h"
00080 #endif
00081 
00082 using std::cout;
00083 using std::cerr;
00084 using std::endl;
00085 /*
00086  *  Sockets, etc.
00087  */
00088 extern int xfd;       // X-windows fd.
00089 int listen_socket = -1;     // Listen here for map-editor.
00090 int client_socket = -1;     // Socket to the map-editor.
00091 int highest_fd = -1;      // Largest fd + 1.
00092 
00093 #ifdef __sun__
00094 // Solaris doesn't know PF_LOCAL
00095 #define PF_LOCAL PF_UNIX
00096 #endif
00097 
00098 /*
00099  *  Set the 'highest_fd' value to 1 + <largest fd>.
00100  */
00101 inline void Set_highest_fd
00102   (
00103   )
00104   {
00105   highest_fd = xfd;   // Figure highest to listen to.
00106   if (listen_socket > highest_fd)
00107     highest_fd = listen_socket;
00108   if (client_socket > highest_fd)
00109     highest_fd = client_socket;
00110   highest_fd++;     // Select wants 1+highest.
00111   }
00112 
00113 /*
00114  *  Initialize server for map-editing.  Call this AFTER xfd has been set.
00115  */
00116 
00117 void Server_init
00118   (
00119   )
00120   {
00121 #ifndef WIN32
00122   // Get location of socket file.
00123   std::string servename("");
00124   char *home = getenv("HOME");
00125   if (home)     // Use $HOME/.exult/exultserver
00126     {     //   if possible.
00127     servename = home;
00128     servename += "/.exult";
00129     if (U7exists(servename.c_str()))
00130       servename += "/exultserver";
00131     else
00132       servename = "";
00133     }
00134   if (servename == "")
00135     servename = get_system_path("<GAMEDAT>/exultserver");
00136   // Make sure it isn't there.
00137   unlink(servename.c_str());
00138 #if HAVE_GETADDRINFOX
00139   // Don't use the old deprecated network API
00140   int r;
00141   struct addrinfo hints,*ai;
00142 
00143   memset(&hints,0,sizeof(hints));
00144   hints.ai_flags=AI_PASSIVE;
00145   r=getaddrinfo(  0,
00146       servename.c_str(),
00147       &hints,
00148       &ai);
00149   if(r!=0)
00150     {
00151     cerr << "getaddrinfo(): "<<gai_strerror(r) << endl;
00152     return;
00153     }
00154 
00155   listen_socket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
00156 #else
00157   // Deprecated
00158   listen_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
00159 #endif
00160   if (listen_socket < 0)
00161     perror("Failed to open map-editor socket");
00162   else 
00163     {
00164 #if HAVE_GETADDRINFOX
00165     if(bind(listen_socket,ai->ai_addr,ai->ai_addrlen) == -1 ||
00166       listen(listen_socket,1) == -1)
00167 #else
00168     struct sockaddr_un addr;
00169     addr.sun_family = AF_UNIX;
00170     strcpy(addr.sun_path, servename.c_str());
00171     if (bind(listen_socket, (struct sockaddr *) &addr, 
00172           sizeof(addr.sun_family) + strlen(addr.sun_path)) == -1 ||
00173         listen(listen_socket, 1) == -1)
00174 #endif
00175       {
00176       perror("Bind or listen on socket failed");
00177       close(listen_socket);
00178       listen_socket = -1;
00179       }
00180     else      // Set to be non-blocking.
00181       {
00182       cout << "Listening on socket " << listen_socket
00183                 << endl;
00184       fcntl(listen_socket, F_SETFL, 
00185         fcntl(listen_socket, F_GETFL) | O_NONBLOCK);
00186       }
00187 #if HAVE_GETADDRINFOX
00188     freeaddrinfo(ai);
00189 #endif
00190     }
00191   Set_highest_fd();
00192 #else
00193 
00194   listen_socket = client_socket = -1;
00195 
00196   std::string servename = get_system_path("<STATIC>");
00197 
00198   if (Exult_server::create_pipe(servename.c_str())) listen_socket = 1;
00199 
00200 #endif
00201   }
00202 
00203 /*
00204  *  Close the server.
00205  */
00206 
00207 void Server_close
00208   (
00209   )
00210   {
00211 #ifdef WIN32
00212   Exult_server::close_pipe();
00213   listen_socket = client_socket = -1;
00214 #else
00215   // unlink socket file+++++++
00216 #endif
00217   }
00218 
00219 
00220 /*
00221  *  A message from a client is available, so handle it.
00222  */
00223 
00224 static void Handle_client_message
00225   (
00226   int& fd       // Socket to client.  May be closed.
00227   )
00228   {
00229   unsigned char data[Exult_server::maxlength];
00230   Exult_server::Msg_type id;
00231   int datalen = Exult_server::Receive_data(fd, id, data, sizeof(data));
00232   if (datalen < 0)
00233     return;
00234   unsigned char *ptr = &data[0];
00235   Game_window *gwin = Game_window::get_instance();
00236   switch (id)
00237     {
00238   case Exult_server::obj:
00239     Game_object::update_from_studio(&data[0], datalen);
00240     break;
00241   case Exult_server::egg:
00242     Egg_object::update_from_studio(&data[0], datalen);
00243     break;
00244   case Exult_server::npc:
00245     Actor::update_from_studio(&data[0], datalen);
00246     break;
00247   case Exult_server::info:
00248     {
00249     unsigned char data[Exult_server::maxlength];
00250     unsigned char *ptr = &data[0];
00251     Game_info_out(client_socket, Exult_server::version, 
00252       cheat.get_edit_lift(),
00253       gwin->skip_lift,
00254       cheat.in_map_editor(),
00255       cheat.show_tile_grid(),
00256       gwin->get_map()->was_map_modified(),
00257       (int) cheat.get_edit_mode());
00258     break;
00259     }
00260   case Exult_server::write_map:
00261     gwin->write_map();  // Send feedback?+++++
00262     break;
00263   case Exult_server::read_map:
00264     gwin->read_map();
00265     break;
00266   case Exult_server::map_editing_mode:
00267     {
00268     int onoff = Read2(ptr);
00269     if ((onoff != 0) != cheat.in_map_editor())
00270       cheat.toggle_map_editor();
00271     break;
00272     }
00273   case Exult_server::tile_grid:
00274     {
00275     int onoff = Read2(ptr);
00276     if ((onoff != 0) != cheat.show_tile_grid())
00277       cheat.toggle_tile_grid();
00278     break;
00279     }
00280   case Exult_server::edit_lift:
00281     {
00282     int lift = Read2(ptr);
00283     cheat.set_edit_lift(lift);
00284     break;
00285     }
00286   case Exult_server::reload_usecode:
00287     gwin->reload_usecode();
00288     break;
00289   case Exult_server::locate_terrain:
00290     {
00291     int tnum = Read2(ptr);
00292     int cx = (short) Read2(ptr);
00293     int cy = (short) Read2(ptr);
00294     bool up = *ptr++ ? true : false;
00295     bool okay = gwin->get_map()->locate_terrain(tnum, cx, cy, up);
00296     ptr = &data[2];   // Set back reply.
00297     Write2(ptr, cx);
00298     Write2(ptr, cy);
00299     ptr++;      // Skip 'up' flag.
00300     *ptr++ = okay ? 1 : 0;
00301     Exult_server::Send_data(client_socket, 
00302       Exult_server::locate_terrain, data, ptr - data);
00303     break;
00304     }
00305   case Exult_server::swap_terrain:
00306     {
00307     int tnum = Read2(ptr);
00308     bool okay = gwin->get_map()->swap_terrains(tnum);
00309     *ptr++ = okay ? 1 : 0;
00310     Exult_server::Send_data(client_socket,
00311       Exult_server::swap_terrain, data, ptr - data);
00312     break;
00313     }
00314   case Exult_server::insert_terrain:
00315     {
00316     int tnum = (short) Read2(ptr);
00317     bool dup = *ptr++ ? true : false;
00318     bool okay = gwin->get_map()->insert_terrain(tnum, dup);
00319     *ptr++ = okay ? 1 : 0;
00320     Exult_server::Send_data(client_socket, 
00321       Exult_server::insert_terrain, data, ptr - data);
00322     break;
00323     }
00324   case Exult_server::delete_terrain:
00325     {
00326     int tnum = (short) Read2(ptr);
00327     bool okay = gwin->get_map()->delete_terrain(tnum);
00328     *ptr++ = okay ? 1 : 0;
00329     Exult_server::Send_data(client_socket,
00330       Exult_server::delete_terrain, data, ptr - data);
00331     break;
00332     }
00333   case Exult_server::send_terrain:
00334     {     // Send back #, total, 512-bytes data.
00335     int tnum = (short) Read2(ptr);
00336     Write2(ptr, gwin->get_map()->get_num_chunk_terrains());
00337     Chunk_terrain *ter = gwin->get_map()->get_terrain(tnum);
00338     ter->write_flats(ptr);  // Serialize it.
00339     ptr += 512;   // I just happen to know the length...
00340     Exult_server::Send_data(client_socket, 
00341       Exult_server::send_terrain, data, ptr - data);
00342     break;
00343     }
00344   case Exult_server::terrain_editing_mode:
00345     {     // 1=on, 0=off, -1=undo.
00346     int onoff = (short) Read2(ptr);
00347           // skip_lift==0 <==> terrain-editing.
00348     gwin->skip_lift = onoff == 1 ? 0 : 16;
00349     static char *msgs[3] = {"Terrain-Editing Aborted",
00350           "Terrain-Editing Done",
00351           "Terrain-Editing Enabled"};
00352     if (onoff == 0)   // End/commit.
00353       gwin->get_map()->commit_terrain_edits();
00354     else if (onoff == -1)
00355       gwin->get_map()->abort_terrain_edits();
00356     if (onoff >= -1 && onoff <= 1)
00357       gwin->get_effects()->center_text(msgs[onoff + 1]);
00358     gwin->set_all_dirty();
00359     break;
00360     }
00361   case Exult_server::set_edit_shape:
00362     {
00363     int shnum = (short) Read2(ptr);
00364     int frnum = (short) Read2(ptr);
00365     cheat.set_edit_shape(shnum, frnum);
00366     break;
00367     }
00368   case Exult_server::view_pos:
00369     {
00370     int tx = Read4(ptr);
00371     if (tx == -1)   // This is a query?
00372       {
00373       gwin->send_location();
00374       break;
00375       }
00376     int ty = Read4(ptr);
00377     // +++Later int txs = Read4(ptr);
00378     // int tys = Read4(ptr);
00379     // int scale = Read4(ptr);
00380           // Only set if chunk changed.
00381     if (tx/c_tiles_per_chunk != 
00382       gwin->get_scrolltx()/c_tiles_per_chunk || 
00383         ty/c_tiles_per_chunk != 
00384       gwin->get_scrollty()/c_tiles_per_chunk)
00385       {
00386       gwin->set_scrolls(tx, ty);
00387       gwin->set_all_dirty();
00388       }
00389     break;
00390     }
00391   case Exult_server::set_edit_mode:
00392     {
00393     int md = Read2(ptr);
00394     if (md >= 0 && md <= 3)
00395       cheat.set_edit_mode((Cheat::Map_editor_mode) md);
00396     break;
00397     }
00398   case Exult_server::hide_lift:
00399     {
00400     int lift = Read2(ptr);
00401     gwin->skip_lift = lift;
00402     gwin->set_all_dirty();
00403     break;
00404     }
00405   case Exult_server::reload_shapes:
00406     Shape_manager::get_instance()->reload_shapes(Read2(ptr));
00407     break;
00408   case Exult_server::unused_shapes:
00409     {     // Send back shapes not used in game.
00410     unsigned char data[Exult_server::maxlength];
00411     int sz = 1024/8;  // Gets bits for unused shapes.
00412     sz = sz > sizeof(data) ? sizeof(data) : sz;
00413     gwin->get_map()->find_unused_shapes(data, sz);
00414     Exult_server::Send_data(client_socket, 
00415         Exult_server::unused_shapes, data, sz);
00416     break;
00417     }
00418   case Exult_server::locate_shape:
00419     {
00420     int shnum = Read2(ptr);
00421     bool up = *ptr++ ? true : false;
00422     bool okay = gwin->locate_shape(shnum, up);
00423     ptr = &data[2];   // Send back reply.
00424     ptr++;      // Skip 'up' flag.
00425     *ptr++ = okay ? 1 : 0;
00426     Exult_server::Send_data(client_socket, 
00427       Exult_server::locate_shape, data, ptr - data);
00428     break;
00429     }
00430   case Exult_server::cut:   // Cut/copy.
00431     cheat.cut(*ptr != 0);
00432     break;
00433   case Exult_server::paste:
00434     cheat.paste();
00435     break;
00436   case Exult_server::npc_info:
00437     Write2(ptr, gwin->get_num_npcs());
00438     Write2(ptr, gwin->get_unused_npc());
00439     Exult_server::Send_data(client_socket, Exult_server::npc_info,
00440               data, ptr - data);
00441     break;
00442   case Exult_server::edit_selected:
00443     {
00444     unsigned char basic = *ptr;
00445     const Game_object_vector& sel = cheat.get_selected();
00446     if (!sel.empty())
00447       if (basic)    // Basic obj. props?
00448         sel.back()->Game_object::edit();
00449       else
00450         sel.back()->edit();
00451     break;
00452     }
00453   case Exult_server::set_edit_chunknum:
00454     cheat.set_edit_chunknum((short) Read2(ptr));
00455     break;
00456 
00457   case Exult_server::game_pos:
00458     {
00459     Tile_coord pos = gwin->get_main_actor()->get_tile();
00460     Write2(ptr, pos.tx);
00461     Write2(ptr, pos.ty);
00462     Write2(ptr, pos.tz);
00463     Exult_server::Send_data(client_socket, 
00464       Exult_server::game_pos, data, ptr - data);
00465     break;
00466     }
00467 
00468 #ifdef USECODE_DEBUGGER
00469   case Exult_server::usecode_debugging:
00470     Handle_debug_message(&data[0], datalen);
00471     break;
00472 #endif
00473     }
00474   }
00475 
00476 /*
00477  *  Delay for a fraction of a second, or until there's data available.
00478  *  If a server request comes, it's handled here.
00479  */
00480 
00481 void Server_delay
00482   (
00483    Message_handler handle_message
00484   )
00485   {
00486 #ifndef WIN32
00487   extern int xfd;
00488   fd_set rfds;
00489   struct timeval timer;
00490   timer.tv_sec = 0;
00491   timer.tv_usec = 50000;    // Try 1/50 second.
00492   FD_ZERO(&rfds);
00493   FD_SET(xfd, &rfds);
00494   if (listen_socket >= 0)
00495     FD_SET(listen_socket, &rfds);
00496   if (client_socket >= 0)
00497     FD_SET(client_socket, &rfds);
00498           // Wait for timeout or event.
00499   if (select(highest_fd, &rfds, 0, 0, &timer) > 0)
00500     {     // Something's come in.
00501     if (listen_socket >= 0 && FD_ISSET(listen_socket, &rfds))
00502       {   // New client connection.
00503           // For now, just one at a time.
00504       if (client_socket >= 0)
00505         close(client_socket);
00506       client_socket = accept(listen_socket, 0, 0);
00507       cout << "Accept returned client_socket = " <<
00508               client_socket << endl;
00509           // Non-blocking.
00510       fcntl(client_socket, F_SETFL, 
00511         fcntl(client_socket, F_GETFL) | O_NONBLOCK);
00512       Set_highest_fd();
00513       }
00514     if (client_socket >= 0 && FD_ISSET(client_socket, &rfds))
00515       {
00516       handle_message(client_socket);
00517           // Client gone?
00518       if (client_socket == -1)
00519         Set_highest_fd();
00520       }
00521     }
00522 #else
00523   if (listen_socket == -1) return;
00524 
00525   if (Exult_server::is_broken()) {
00526     std::cout << "Client disconnected." << endl;
00527     Exult_server::disconnect_from_client();
00528     Exult_server::setup_connect();
00529     client_socket = -1;
00530   }
00531 
00532   SleepEx(20, TRUE);
00533 
00534   if (client_socket == -1) {
00535     // Only do this in map edit mode
00536     if (!cheat.in_map_editor()) return;
00537 
00538     std::string servename = get_system_path("<STATIC>");
00539     if (!Exult_server::try_connect_to_client(servename.c_str())) return;
00540     else client_socket = 1;
00541     std::cout << "Connected to client" << endl;
00542   }
00543 
00544   if (Exult_server::peek_pipe() > 0)
00545     handle_message(client_socket);
00546     
00547   if (Exult_server::is_broken()) {
00548     if (Exult_server::notify_connection_lost()) std::cout << "Client disconnected." << endl;
00549     Exult_server::disconnect_from_client();
00550     Exult_server::setup_connect();
00551     client_socket = -1;
00552   }
00553 #endif
00554 
00555   }
00556 
00557 void Server_delay()
00558 {
00559   Server_delay(Handle_client_message);
00560 }
00561 
00562 #endif

Generated on Mon Jul 9 14:42:50 2007 for ExultEngine by  doxygen 1.5.1