# dedicated_v.cpp

    00001 /* $Id: dedicated_v.cpp 14266 2008-09-07 21:26:26Z rubidium $ */
    00002
    00003 #include "../stdafx.h"
    00004
    00005 #ifdef ENABLE_NETWORK
    00006
    00007 #include "../openttd.h"
    00008 #include "../debug.h"
    00009 #include "../gfx_func.h"
    00010 #include "../network/network.h"
    00011 #include "../network/network_internal.h"
    00012 #include "../console.h"
    00013 #include "../variables.h"
    00014 #include "../genworld.h"
    00015 #include "../fileio.h"
    00016 #include "../fios.h"
    00017 #include "../blitter/factory.hpp"
    00018 #include "../core/alloc_func.hpp"
    00019 #include "../player_func.h"
    00020 #include "../core/random_func.hpp"
    00021 #include "dedicated_v.h"
    00022
    00023 #ifdef BEOS_NET_SERVER
    00024 #include <net/socket.h>
    00025 #endif
    00026
    00027 #ifdef __OS2__
    00028 # include <sys/time.h> /* gettimeofday */
    00029 # include <sys/types.h>
    00030 # include <unistd.h>
    00031 # include <conio.h>
    00032
    00033 # define INCL_DOS
    00034 # include <os2.h>
    00035
    00036 # define STDIN 0  /* file descriptor for standard input */
    00037
    00041 static void OS2_SwitchToConsoleMode()
    00042 {
    00043   PPIB pib;
    00044   PTIB tib;
    00045
    00046   DosGetInfoBlocks(&tib, &pib);
    00047
    00048   // Change flag from PM to VIO
    00049   pib->pib_ultype = 3;
    00050 }
    00051 #endif
    00052
    00053 #if defined(UNIX) || defined(PSP)
    00054 # include <sys/time.h> /* gettimeofday */
    00055 # include <sys/types.h>
    00056 # include <unistd.h>
    00057 # include <signal.h>
    00058 # define STDIN 0  /* file descriptor for standard input */
    00059 # if defined(PSP)
    00060 #   include <sys/fd_set.h>
    00061 #   include <sys/select.h>
    00062 # endif /* PSP */
    00063
    00064 /* Signal handlers */
    00065 static void DedicatedSignalHandler(int sig)
    00066 {
    00067   _exit_game = true;
    00068   signal(sig, DedicatedSignalHandler);
    00069 }
    00070 #endif
    00071
    00072 #if defined(WIN32)
    00073 # include <windows.h> /* GetTickCount */
    00074 # if !defined(WINCE)
    00075 #  include <conio.h>
    00076 # endif
    00077 # include <time.h>
    00078 # include <tchar.h>
    00079 static HANDLE _hInputReady, _hWaitForInputHandling;
    00080 static HANDLE _hThread; // Thread to close
    00081 static char _win_console_thread_buffer[200];
    00082
    00083 /* Windows Console thread. Just loop and signal when input has been received */
    00084 static void WINAPI CheckForConsoleInput()
    00085 {
    00086 #if defined(WINCE)
    00087   /* WinCE doesn't support console stuff */
    00088   return;
    00089 #else
    00090   DWORD nb;
    00091   HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
    00092   while (true) {
    00093     ReadFile(hStdin, _win_console_thread_buffer, lengthof(_win_console_thread_buffer), &nb, NULL);
    00094     /* Signal input waiting that input is read and wait for it being handled
    00095      * SignalObjectAndWait() should be used here, but it's unsupported in Win98< */
    00096     SetEvent(_hInputReady);
    00097     WaitForSingleObject(_hWaitForInputHandling, INFINITE);
    00098   }
    00099 #endif
    00100 }
    00101
    00102 static void CreateWindowsConsoleThread()
    00103 {
    00104   DWORD dwThreadId;
    00105   /* Create event to signal when console input is ready */
    00106   _hInputReady = CreateEvent(NULL, false, false, NULL);
    00107   _hWaitForInputHandling = CreateEvent(NULL, false, false, NULL);
    00108   if (_hInputReady == NULL || _hWaitForInputHandling == NULL) error("Cannot create console event!");
    00109
    00110   _hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)CheckForConsoleInput, NULL, 0, &dwThreadId);
    00111   if (_hThread == NULL) error("Cannot create console thread!");
    00112
    00113   DEBUG(driver, 2, "Windows console thread started");
    00114 }
    00115
    00116 static void CloseWindowsConsoleThread()
    00117 {
    00118   CloseHandle(_hThread);
    00119   CloseHandle(_hInputReady);
    00120   CloseHandle(_hWaitForInputHandling);
    00121   DEBUG(driver, 2, "Windows console thread shut down");
    00122 }
    00123
    00124 #endif
    00125
    00126
    00127 static void *_dedicated_video_mem;
    00128
    00129 extern bool SafeSaveOrLoad(const char *filename, int mode, int newgm, Subdirectory subdir);
    00130 extern void SwitchMode(int new_mode);
    00131
    00132 static FVideoDriver_Dedicated iFVideoDriver_Dedicated;
    00133
    00134
    00135 const char *VideoDriver_Dedicated::Start(const char * const *parm)
    00136 {
    00137   int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
    00138   if (bpp == 0) _dedicated_video_mem = NULL;
    00139   else          _dedicated_video_mem = MallocT<byte>(_cur_resolution[0] * _cur_resolution[1] * (bpp / 8));
    00140
    00141   _screen.width = _screen.pitch = _cur_resolution[0];
    00142   _screen.height = _cur_resolution[1];
    00143
    00144   SetDebugString("net=6");
    00145
    00146 #if defined(WINCE)
    00147   /* WinCE doesn't support console stuff */
    00148 #elif defined(WIN32)
    00149   // For win32 we need to allocate a console (debug mode does the same)
    00150   CreateConsole();
    00151   CreateWindowsConsoleThread();
    00152   SetConsoleTitle(_T("OpenTTD Dedicated Server"));
    00153 #endif
    00154
    00155 #ifdef __OS2__
    00156   // For OS/2 we also need to switch to console mode instead of PM mode
    00157   OS2_SwitchToConsoleMode();
    00158 #endif
    00159
    00160   DEBUG(driver, 1, "Loading dedicated server");
    00161   return NULL;
    00162 }
    00163
    00164 void VideoDriver_Dedicated::Stop()
    00165 {
    00166 #ifdef WIN32
    00167   CloseWindowsConsoleThread();
    00168 #endif
    00169   free(_dedicated_video_mem);
    00170 }
    00171
    00172 void VideoDriver_Dedicated::MakeDirty(int left, int top, int width, int height) {}
    00173 bool VideoDriver_Dedicated::ChangeResolution(int w, int h) { return false; }
    00174 bool VideoDriver_Dedicated::ToggleFullscreen(bool fs) { return false; }
    00175
    00176 #if defined(UNIX) || defined(__OS2__) || defined(PSP)
    00177 static bool InputWaiting()
    00178 {
    00179   struct timeval tv;
    00180   fd_set readfds;
    00181
    00182   tv.tv_sec = 0;
    00183   tv.tv_usec = 1;
    00184
    00185   FD_ZERO(&readfds);
    00186   FD_SET(STDIN, &readfds);
    00187
    00188   /* don't care about writefds and exceptfds: */
    00189   return select(STDIN + 1, &readfds, NULL, NULL, &tv) > 0;
    00190 }
    00191
    00192 static uint32 GetTime()
    00193 {
    00194   struct timeval tim;
    00195
    00196   gettimeofday(&tim, NULL);
    00197   return tim.tv_usec / 1000 + tim.tv_sec * 1000;
    00198 }
    00199
    00200 #else
    00201
    00202 static bool InputWaiting()
    00203 {
    00204   return WaitForSingleObject(_hInputReady, 1) == WAIT_OBJECT_0;
    00205 }
    00206
    00207 static uint32 GetTime()
    00208 {
    00209   return GetTickCount();
    00210 }
    00211
    00212 #endif
    00213
    00214 static void DedicatedHandleKeyInput()
    00215 {
    00216   static char input_line[200] = "";
    00217
    00218   if (!InputWaiting()) return;
    00219
    00220   if (_exit_game) return;
    00221
    00222 #if defined(UNIX) || defined(__OS2__) || defined(PSP)
    00223   if (fgets(input_line, lengthof(input_line), stdin) == NULL) return;
    00224 #else
    00225   /* Handle console input, and singal console thread, it can accept input again */
    00226   strncpy(input_line, _win_console_thread_buffer, lengthof(input_line));
    00227   SetEvent(_hWaitForInputHandling);
    00228 #endif
    00229
    00230   /* strtok() does not 'forget' \r\n if the string starts with it,
    00231    * so we have to manually remove that! */
    00232   strtok(input_line, "\r\n");
    00233   for (char *c = input_line; *c != '\0'; c++) {
    00234     if (*c == '\n' || *c == '\r' || c == lastof(input_line)) {
    00235       *c = '\0';
    00236       break;
    00237     }
    00238   }
    00239   str_validate(input_line);
    00240
    00241   IConsoleCmdExec(input_line); // execute command
    00242 }
    00243
    00244 void VideoDriver_Dedicated::MainLoop()
    00245 {
    00246   uint32 cur_ticks = GetTime();
    00247   uint32 next_tick = cur_ticks + 30;
    00248
    00249   /* Signal handlers */
    00250 #if defined(UNIX) || defined(PSP)
    00251   signal(SIGTERM, DedicatedSignalHandler);
    00252   signal(SIGINT, DedicatedSignalHandler);
    00253   signal(SIGQUIT, DedicatedSignalHandler);
    00254 #endif
    00255
    00256   // Load the dedicated server stuff
    00257   _is_network_server = true;
    00258   _network_dedicated = true;
    00259   _network_playas = PLAYER_SPECTATOR;
    00260   _local_player = PLAYER_SPECTATOR;
    00261
    00262   /* If SwitchMode is SM_LOAD, it means that the user used the '-g' options */
    00263   if (_switch_mode != SM_LOAD) {
    00264     StartNewGameWithoutGUI(GENERATE_NEW_SEED);
    00265     SwitchMode(_switch_mode);
    00266     _switch_mode = SM_NONE;
    00267   } else {
    00268     _switch_mode = SM_NONE;
    00269     /* First we need to test if the savegame can be loaded, else we will end up playing the
    00270      *  intro game... */
    00271     if (!SafeSaveOrLoad(_file_to_saveload.name, _file_to_saveload.mode, GM_NORMAL, BASE_DIR)) {
    00272       /* Loading failed, pop out.. */
    00273       DEBUG(net, 0, "Loading requested map failed, aborting");
    00274       _networking = false;
    00275     } else {
    00276       /* We can load this game, so go ahead */
    00277       SwitchMode(SM_LOAD);
    00278     }
    00279   }
    00280
    00281   // Done loading, start game!
    00282
    00283   if (!_networking) {
    00284     DEBUG(net, 0, "Dedicated server could not be started, aborting");
    00285     return;
    00286   }
    00287
    00288   while (!_exit_game) {
    00289     uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
    00290     InteractiveRandom(); // randomness
    00291
    00292     if (!_dedicated_forks)
    00293       DedicatedHandleKeyInput();
    00294
    00295     cur_ticks = GetTime();
    00296     if (cur_ticks >= next_tick || cur_ticks < prev_cur_ticks) {
    00297       next_tick = cur_ticks + 30;
    00298
    00299       GameLoop();
    00300       _screen.dst_ptr = _dedicated_video_mem;
    00301       UpdateWindows();
    00302     }
    00303     CSleep(1);
    00304   }
    00305 }
    00306
    00307 #endif /* ENABLE_NETWORK */