This tutorial was created by Royce Pipkins and demonstrates a Beta 4 minimum netplay application. Download a copy of the source code associated with this file by clicking here.

//Make sure you create your project as a "Win32 Application" NOT a
//"Win32 Console Application"
//don't forget to set the working directory for the project to the base project
//directory. It's also very help to set the output directories to the base project
//directory.
//don't forget to copy all the dll's, the lib, include, and levels directoies
//from the SDK base code into your proj directory
//add lib\genesis.lib and lib\genesisd.lib to the release and debug link lists
//respectivly.
//add winmm.lib to both link lists
//change /ML to /MT in the C/C++ project options for Release.
//change /MLd to /MTd in the C/C++ project options for Debug.
//run the exe from the project base directory.

//NOTE: the /MT and /MTd bits are diffrent from what I said in the Beta 4 MinApp

//code but some comments I got from Dan of the ROC crew and a post by an Eclipse

//person leads me to belive code generation should really be set to multi-threaded.
//As a test: copy your exe to the SDK base directory. (The same directory GTest.exe

//is in) Run it from there. If it works in the SDK directory but not in your project

//directory you have not copied all the nessecary files to your project directory.

#include <windows.h> 

#include "include\Genesis.h" 

#include <malloc.h> 

#include <string.h> 

#include <Time.h> 

#include <mmsystem.h> 

  
//PROTOTYPES 

static HWND CreateMainWindow(HANDLE hInstance, char *AppName, int32 Width, int32 Height); 

LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); 

void MoveCamera(void); 

void TextPump(void); 

void AddDisplayString(const char* string); 

void TextEntry(void); 

void StartServer(void); 

void StopNetPlay(void); 

void ClientConnect(const char * IP); 

void ClientCycle(void); 

void ServerCycle(void); 

void SendChatToServer(const char* chat_str); 

void SendChatToClient(geCSNetMgr_NetClient *client, const char *chat_str); 

void RestorwIfMined(void); 
//USER MESSAGE SUBTYPES 

#define SUBTYPE_CHAT 0 

  
//GLOBAL VARIABLES 

HWND              mainwindowhandle; 

int               focus = 1; 

geEngine          *Engine = NULL; 

geDriver_System   *DrvSys = NULL; 

geDriver          *Driver = NULL; 

geDriver_Mode     *Mode = NULL; 

geWorld           *World = NULL; 

geCamera          *Camera = NULL; 

geRect            Rect; 

char              *modename = NULL, *drvname = NULL; 

MSG               Msg; 

geXForm3d         Xform; 

int               run, Width, Height; 
geBoolean         LeftMouse = GE_FALSE, RightMouse = GE_FALSE, 

                  MoveForward = GE_FALSE, MoveBackward = GE_FALSE, 

                  WantToQuit = GE_FALSE, TextEntryMode = GE_FALSE, 

                  ChatMode = GE_FALSE, IPEntryMode = GE_FALSE, 

                  NameEntryMode = GE_FALSE; 

int               WWidth = 640, WHeight = 480; 

geVec3d           CamAngle = {0, 0, 0}, CamLocation = {-1156, 400, 1215}; 

geVec3d           Mins = {-12.5, -12.5, -12.5}, 

                  Maxs = {12.5, 12.5, 12.5}; 

char*             display[15] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL, NULL, NULL, NULL, NULL}; 

  
char              textbuffer[1024]; 

int               textbufpos = 0; 

geCSNetMgr        *NetMgr = NULL; 

geCSNetMgr_NetSession *NetSess = NULL; 

geBoolean         ServerMode = GE_FALSE, ClientMode = GE_FALSE; 

geCSNetMgr_NetClient *clients[64]; 

char              thisClientName[32]; 

char              TempName[1024];

geVFile           *MainFS, *map;

  
  

  

//////////////////////////////////////////////////////////////////////// 

//        WinMain                                                     // 

//////////////////////////////////////////////////////////////////////// 

  

int WINAPI WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdParam, int nCmdShow) 

{ 
  //start main window 

  mainwindowhandle = CreateMainWindow(hInstance, "Small Netplay Application", WWidth, WHeight); 
  //start genesis 

  Engine = geEngine_Create(mainwindowhandle, "MinApp", ".");
  if (!Engine) { 

    MessageBox(NULL,"No Engine", "Error",MB_OK); 

    _exit(-1); 

  } 
  geEngine_EnableFrameRateCounter(Engine, GE_FALSE); 
  //get driver container 

 DrvSys = geEngine_GetDriverSystem(Engine); 

  if (!DrvSys) { 

    MessageBox(NULL,"No DrvSys", "Error",MB_OK); 

    _exit(-1); 

  } 

  
  //pick driver 

  Driver = geDriver_SystemGetNextDriver(DrvSys, NULL); 

  if (!Driver) MessageBox(NULL,"No Driver","Error",MB_OK); 

  while(1) { 

    geDriver_GetName(Driver, &drvname); 

    if (drvname[0] == 'S') break; 

    Driver = geDriver_SystemGetNextDriver(DrvSys, Driver); 

    if (!Driver) _exit(-1); 

  } 

  

  //pick screen resolution 

  Mode =   geDriver_GetNextMode(Driver, NULL); 

  while(1) { 

    if (!Mode) MessageBox(NULL,"No Mode","Error",MB_OK); 

    geDriver_ModeGetWidthHeight(Mode, &Width, &Height); 

    if (Width ==  -1/*WWidth*/ && Height == -1/*WHeight*/) break; 

    Mode =   geDriver_GetNextMode(Driver, Mode); 

  } 

  

  //tell Genesis the use our selected video Driver in our selected video mode 

  if (!geEngine_SetDriverAndMode(Engine, Driver, Mode)) { 

    MessageBox(NULL,"Set Driver/Mode failed","Error",MB_OK); 

    _exit(-1); 

  } 
  ///*********NEW FOR BETA 4!! IMPORTANT************

  //Call this here and BeginFrame will fail.

  //geEngine_SetGamma(Engine, 2.0f); 

  



  GetCurrentDirectory(sizeof(TempName), TempName);

        MainFS = geVFile_OpenNewSystem(NULL,

                                       GE_VFILE_TYPE_DOS,

                                       TempName,

                                       NULL,

                                       GE_VFILE_OPEN_READONLY | GE_VFILE_OPEN_DIRECTORY);
  map = geVFile_Open(MainFS, "levels\\genvs.bsp", GE_VFILE_OPEN_READONLY); 

  //create a world object from a BSP file. 

  World = geWorld_Create(map);
  if (!World) { 

    MessageBox(NULL,"No World","Error",MB_OK); 

    _exit(-1); 

  } 

  ///*********NEW FOR BETA 4!! IMPORTANT************

  //geEngine now has a member that is an array of World pointers!!! 

  //somebody should try a multiple worlds demo :)

  if (!geEngine_AddWorld(Engine, World)) {

    MessageBox(NULL,"Failed to add World","Error",MB_OK);

    _exit(-1);

  }
  //Camera rect 

 Rect.Left = 0; 

 Rect.Right = WWidth - 1; 

 Rect.Top = 0; 

 Rect.Bottom = (WHeight - 1) - 60; 
  //create a camera object with an FOV of 2 (clueless), 

  //and Rect window rendering area 

  Camera = geCamera_Create(2.0f, &Rect); 
  if (!Camera) { 

    MessageBox(NULL,"No Camera","Error",MB_OK); 

    _exit(-1); 

  } 

  

  //initialize camera 

  geXForm3d_SetIdentity(&Xform); 

  geXForm3d_RotateZ(&Xform, (geFloat)0.0); 

  geXForm3d_RotateX(&Xform, (geFloat)0.0); 

  geXForm3d_RotateY(&Xform, (geFloat)0.0); 

  geXForm3d_Translate(&Xform, -1156, 400, 1215); 

  geCamera_SetXForm(Camera, &Xform); 

  

  //init the text buffer 

  textbuffer[0] = 0; 

  textbuffer[1] = 0; 

  

  //Reposition the window once more 

  SetWindowPos(mainwindowhandle, HWND_TOP,100,100,WWidth+10,WHeight+10,SWP_NOCOPYBITS | SWP_NOZORDER); 

  ShowCursor(FALSE); 
  //main application loop 

  run = 1; 

  while (run) { 

  
    if (WantToQuit) { 

      PostMessage(mainwindowhandle, WM_QUIT, 0, 0); 

      ShowCursor(TRUE); 

    } 
    if (focus) MoveCamera(); 

    TextEntry(); 

    TextPump(); 

    if (ServerMode) ServerCycle(); 

    if (ClientMode || ServerMode) ClientCycle(); 

  
    //Begin the engine frame, Engine, and Camera better be valid pointers

    //or its gonna hang.

    ///*********NEW FOR BETA 4!! IMPORTANT************

    //World was dropped from the arg list. 

    if (GE_FALSE == geEngine_BeginFrame(Engine, Camera, GE_TRUE)) {

      MessageBox(mainwindowhandle, "BeginFrame failed", "Error", MB_OK);

      break;

    }   

    
    //render to offscreen buffer

    if (GE_FALSE == geEngine_RenderWorld(Engine, World, Camera, 0.0f)) {

      MessageBox(mainwindowhandle, "RenderWorld failed", "Error", MB_OK);

      break;

    }
    //end the frame, copy offscreen buffer to onscreen window

    if (GE_FALSE == geEngine_EndFrame(Engine)) {

      MessageBox(mainwindowhandle, "Endframe failed", "Error", MB_OK);

      break;

    }

   while (PeekMessage( &Msg, NULL, 0, 0, PM_NOREMOVE)) 

    { 

     if (!GetMessage(&Msg, NULL, 0, 0 )) 

     { 

      run = 0; 

      break; 

     } 
     TranslateMessage(&Msg); 

     DispatchMessage(&Msg); 

    } 

  
  

  } 
  //shut it all down and quit 

  geCamera_Destroy(&Camera); 
  geWorld_Free(World); 
  geEngine_ShutdownDriver(Engine); 

  

  geEngine_Free(Engine); 
  return (0); 

} 
//===================================================================================== 

// CreateMainWindow 

//===================================================================================== 

static HWND CreateMainWindow(HANDLE hInstance, char *AppName, int32 Width, int32 Height) 

{ 

 WNDCLASS  wc; 

 HWND   hWnd; 
 RECT   WindowRect; 

 // 

 // Set up and register application window class 

 // 
 wc.style         = CS_HREDRAW | CS_VREDRAW; 

 wc.lpfnWndProc   = WndProc; 

 wc.cbClsExtra    = 0; 

 wc.cbWndExtra    = 0; 

 wc.hInstance     = hInstance; 

 wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION); 

 wc.hCursor       = LoadCursor(NULL, IDC_ARROW); 

 wc.hbrBackground = GetStockObject(BLACK_BRUSH); 

 wc.lpszMenuName  = (const char*)NULL; 

 wc.lpszClassName = AppName; 
 RegisterClass(&wc); 
 // 

 // Create application's main window 

 // 
 hWnd = CreateWindowEx( 

  0, 

  AppName, 

  AppName, 

  0, 

  CW_USEDEFAULT, 

  CW_USEDEFAULT, 

  CW_USEDEFAULT, 

  CW_USEDEFAULT, 

  NULL, 

  NULL, 

  hInstance, 

  NULL); 
 if (!hWnd) 

 { 

  MessageBox(0, "Could not create window.", "** ERROR **", MB_OK); 

  _exit(1); 

 } 
 UpdateWindow(hWnd); 

 SetFocus(hWnd); 
 SetWindowLong(hWnd, 

                 GWL_STYLE, 

                 GetWindowLong(hWnd, GWL_STYLE) & ~WS_POPUP); 
 SetWindowLong(hWnd, 

                 GWL_STYLE, 

                 GetWindowLong(hWnd, GWL_STYLE) | (WS_OVERLAPPED  | 

                                                   WS_CAPTION     | 

                                                   WS_SYSMENU     | 

                                                   WS_MINIMIZEBOX)); 
 SetWindowLong(hWnd, 

                  GWL_STYLE, 

                  GetWindowLong(hWnd, GWL_STYLE) | WS_THICKFRAME | 

                                                     WS_MAXIMIZEBOX); 
 SetWindowLong(hWnd, 

                  GWL_EXSTYLE, 

                  GetWindowLong(hWnd, GWL_EXSTYLE) ); 
 WindowRect.left = 10; 

 WindowRect.top = 10; 

 WindowRect.right = 10+Width; 

 WindowRect.bottom = 10+Height; 
  SetWindowPos(hWnd, 

                HWND_TOP, 

                100, 

                100, 

    Width+10, 

    Height+10, 

                SWP_NOCOPYBITS); 
 // 

 // Make window visible 

 // 

 ShowWindow(hWnd, SW_SHOWNORMAL); 
 return hWnd; 
} 

  
  
//////////////////////////////////////////////////////////////////////// 

//        WndProc - processes all WM messages                     // 

//////////////////////////////////////////////////////////////////////// 
LRESULT CALLBACK WndProc(HWND hWnd, UINT iMessage, 

       WPARAM wParam, LPARAM lParam) 

{ 
  char key; 
 switch(iMessage) 

 { 

  

  case WM_RBUTTONDOWN: 

   RightMouse = GE_TRUE; 

   break; 
  case WM_LBUTTONUP: 

   LeftMouse = GE_FALSE; 

   break; 

  case WM_RBUTTONUP: 

   RightMouse = GE_FALSE; 

   break; 

  case WM_LBUTTONDOWN: 

   LeftMouse = GE_TRUE; 

   break; 
    case WM_KEYDOWN:  //alpha VK's are same as ascii capitals 

      if (!TextEntryMode) { 

        key = (char)wParam; 

        switch (key)  { 

          case 'L': 

            WantToQuit = GE_TRUE; 

            break; 

          case 'A': 

            MoveForward = GE_TRUE; 

            MoveBackward = GE_FALSE; 

            break; 

          case 'Z': 

            MoveBackward = GE_TRUE; 

            MoveForward = GE_FALSE; 

            break; 

          case 'T': 

            TextEntryMode = GE_TRUE; 

            ChatMode = GE_TRUE; 

            AddDisplayString(">>> Type a chat message."); 

            break; 

          case 'S': 

            if (!ClientMode) { 

              if (!ServerMode) { 

                StartServer(); 

              } 

              else AddDisplayString (">>> You are already a server."); 

            } 

            else AddDisplayString(">>> You are a client. You can't start a server now."); 

            break; 

          case 'D': 

            StopNetPlay(); 

            break; 

          case 'C': 

            if (!ServerMode) { 

              if (!ClientMode) { 

                TextEntryMode = GE_TRUE; 

                IPEntryMode = GE_TRUE; 

                AddDisplayString(">>> Enter IP Address of Server"); 

              } 

              else AddDisplayString(">>> You are already a client."); 

            } 

            else AddDisplayString(">>> You are a server. You can't connect to another server."); 

            break; 

          case 'N': 

            if (!ServerMode && !ClientMode) { 

              NameEntryMode = GE_TRUE; 

              TextEntryMode = GE_TRUE; 

              AddDisplayString(">>> Enter your Name. (31 chars max)"); 

            } 

            else { 

              AddDisplayString(">>> You can only set your name BEFORE you activate NetPlay."); 

              AddDisplayString(">>> Use the 'D' key to shut down NetPlay"); 

            } 

            break; 

          case 'H': 

            AddDisplayString(">>> HELP MENU"); 

            AddDisplayString(">>> A - Move Forward"); 

            AddDisplayString(">>> Z - Move Backward"); 

            AddDisplayString(">>> C - Connect to Server"); 

            AddDisplayString(">>> S - Become a Server"); 

            AddDisplayString(">>> D - Shutdown Netplay"); 

            AddDisplayString(">>> N - Name Yourself"); 

            AddDisplayString(">>> T - Send a Chat Message"); 

            AddDisplayString(">>> NOTE: You won't find each other in the world."); 

            AddDisplayString(">>>       Chat only for now."); 

            break; 

          case 'V': 

            AddDisplayString("MinApp Ver. 0.06"); 

            break; 

        } 

      } 

      break; 
    case WM_KEYUP: 

      if (!TextEntryMode) { 

        key = (char)wParam; 

        switch (key)  { 

          case 'A': 

            MoveForward = GE_FALSE; 

            break; 

          case 'Z': 

            MoveBackward = GE_FALSE; 

            break; 

        } 

      } 

      break; 
    case WM_CHAR: 

      if (TextEntryMode) { 

        //gather strings here 

        if (wParam == VK_RETURN) { 

          TextEntryMode = GE_FALSE; 

  
          textbuffer[textbufpos] = 0; 

          textbufpos++; 

        } 

        else if (wParam == VK_BACK) { 

          if (textbufpos) textbufpos--; 

          textbuffer[textbufpos] = 0; 

        } 

        else { 

          textbuffer[textbufpos] = (char)wParam; 

          if (textbufpos < 1023) textbufpos++; 

          textbuffer[textbufpos] = 0; 

        } 

      } 

      break; 

    case WM_KILLFOCUS: 

      ShowCursor(TRUE); 

      focus = 0; 

      break; 

    case WM_SETFOCUS: 

      ShowCursor(FALSE); 

      focus = 1; 

      break; 
 default: 

  return DefWindowProc(hWnd, iMessage, wParam, lParam); 

 } 

 return 0; 

} 
void RestoreIfMined(void) { 

  
  

      ShowWindow(mainwindowhandle, SW_RESTORE); 

  
} 

  
  
//////////////////////////////////////////////////////////////////////////////////////////////////////////// 

//  MoveCamera - processes mouse and movement data that was gathered by WndProc      // 

/////////////////////////////////////////////////////////////////////////////////////////////////////////// 

void MoveCamera(void) 

{ 

  POINT pos; 

  geXForm3d   delta; //Camera space transform 

  geFloat     z_trans = 0.0; //Camera space 

  geVec3d     zerovector = {0,0,0}; 

  geVec3d     CamAngle_Bak, CamLoc_Bak; 

  GE_Collision Collision_data; 

  
  CamAngle_Bak.X = CamAngle.X; 

  CamAngle_Bak.Y = CamAngle.Y; 

  CamAngle_Bak.Z = CamAngle.Z; 
  CamLoc_Bak.X = CamLocation.X; 

  CamLoc_Bak.Y = CamLocation.Y; 

  CamLoc_Bak.Z = CamLocation.Z; 
  GetCursorPos(&pos); 

  ScreenToClient(mainwindowhandle, &pos); 

  
  geXForm3d_SetIdentity(&Xform); 

  geXForm3d_SetIdentity(&delta); 
  CamAngle.Y += (float)((pos.x - WWidth/2) * (-0.001745)); 

  CamAngle.X += (float)((pos.y - WHeight/2) * (-0.001745)); 
  geXForm3d_RotateX(&Xform, CamAngle.X); 

  geXForm3d_RotateY(&Xform, CamAngle.Y); 

  geXForm3d_Translate(&Xform, CamLocation.X, CamLocation.Y, CamLocation.Z); 
  if (MoveForward) z_trans = -25.0; 

  if (MoveBackward) z_trans = 25.0; 

  geXForm3d_Translate(&delta, 0, 0, z_trans); 
  geXForm3d_Multiply(&Xform, &delta, &Xform); 
  geXForm3d_Transform(&Xform, &zerovector, &CamLocation); 
  if (geWorld_Collision(World, &Mins, &Maxs, &CamLocation, &CamLoc_Bak, GE_COLLIDE_ALL, 0, NULL, NULL, &Collision_data)) { 

    CamLocation.X = CamLoc_Bak.X; 

    CamLocation.Y = CamLoc_Bak.Y; 

    CamLocation.Z = CamLoc_Bak.Z; 

    CamAngle.X = CamAngle_Bak.X; 

    CamAngle.Y = CamAngle_Bak.Y; 

    CamAngle.Z = CamAngle_Bak.Z; 

  } 

  else geCamera_SetXForm(Camera, &Xform); 
  pos.x = WWidth/2; 

  pos.y = WHeight/2; 

  ClientToScreen(mainwindowhandle, &pos); 

  SetCursorPos(pos.x, pos.y); 

} 

  
  
/////////////////////////////////////////////////////////////////////////////////////// 

//  Text Pump                                                                                       // 

/////////////////////////////////////////////////////////////////////////////////////// 

void TextPump(void) { 

  static  int IsEmpty = 1; 

  static  DWORD d_start_t; 

  DWORD   current_t; 

  int i; 
  if (IsEmpty) { 

    if (display[0] == NULL) return; 

    else { 

      IsEmpty = 0; 

      d_start_t = GetTickCount(); 

    } 

  } 
  if (!IsEmpty) { 
    //check the time first and maybe roll the display 

    current_t = GetTickCount(); 

    if ((current_t - d_start_t) % 15000 < 1000 && 

        (current_t - d_start_t) > 1000) { 

      d_start_t = current_t; 

      free(display[0]); 

      for (i = 0; i < 14; i++) { 

        display[i] = display[i+1]; 

      } 

      display[14] = NULL; 

    } 
    //if we just emptied out the display, reset IsEmpty and return 

    if (display[0] == NULL) { 

      IsEmpty = 1; 

      return; 

    } 
    //output the display 

    for (i = 0; i < 15; i++) { 

      if (display[i] == NULL) break; 

      geEngine_Printf(Engine, 10, 14 * i, display[i]); 

    } 

  } 

} 

  

//will make a copy of string 

void AddDisplayString(const char* string) { 

  
  int i; 

  char *internalcopy = NULL; 
  internalcopy = malloc(strlen(string) + 2); 

  strcpy(internalcopy, string); 
  if (display[14] == NULL) { 

    for (i = 0; i < 15; i++) { 

      if (display[i] == NULL) { 

        display[i] = internalcopy; 

        break; 

      } 

    } 

  } 

  else { 

    free(display[0]); 

    for (i = 0; i < 14; i++) { 

      display[i] = display[i+1]; 

    } 

    display[14] = internalcopy; 

  } 

} 

  

void TextEntry(void) { 

  static geBoolean LastCall = GE_FALSE; 

  char sys_msgbuf[1024]; 

  
  if (TextEntryMode) { 

    geEngine_Printf(Engine, 100, 15 * 25, textbuffer + 1); 

  } 

  else { 

    if (LastCall) { 

      if (ChatMode) { 

        if (ServerMode) { 

          SendChatToServer(textbuffer + 1); 

        } 

        if (ClientMode) { 

          SendChatToServer(textbuffer + 1); 

          //AddDisplayString(textbuffer + 1); clients will get it echoed back anyway 

        } 

        ChatMode = GE_FALSE; 

      } 

      if (IPEntryMode) { 

        ClientConnect(textbuffer + 1); 

        IPEntryMode = GE_FALSE; 

        sprintf(sys_msgbuf,"Trying to connect to %s.", textbuffer + 1); 

        AddDisplayString(sys_msgbuf); 

      } 

      if (NameEntryMode) { 

        strncpy(thisClientName, textbuffer + 1, 32); 

        thisClientName[31] - 0; 

        NameEntryMode = GE_FALSE; 

        sprintf(sys_msgbuf,"Your name is now %s.", textbuffer + 1); 

        AddDisplayString(sys_msgbuf); 

      } 

      textbufpos = 0; 

      textbuffer[0] = 0; 

      textbuffer[1] = 0; 

    } 

  } 

  LastCall = TextEntryMode; 

} 

  
  
/////////////////////////////////////////////////////////////////////////////////////// 

//  NetPlay Code                                                                                   // 

/////////////////////////////////////////////////////////////////////////////////////// 

  

void StartServer(void) { 
  if (!ServerMode && !ClientMode) { 

    NetMgr = geCSNetMgr_Create(); 

    if(!geCSNetMgr_StartSession(NetMgr, "Small NetPlay Server", thisClientName)) { 

      AddDisplayString("Start Server command failed"); 

      return; 

    } 

    ServerMode = GE_TRUE; 

    AddDisplayString("Server Started"); 

  } 

} 
void StopNetPlay(void) { 

  if (ServerMode) geCSNetMgr_StopSession(NetMgr); 

  geCSNetMgr_Destroy(&NetMgr); 

  AddDisplayString("NetPlay Shutdown."); 

  ServerMode = GE_FALSE; 

  ClientMode = GE_FALSE; 

} 

  

void ClientConnect(const char * IP) { 

  
  geCSNetMgr_NetSession *Session = NULL; 

  int32 Session_count = 0; 
  NetMgr = geCSNetMgr_Create(); 

  if (!geCSNetMgr_FindSession(NetMgr, IP, &Session, &Session_count)) { 

    AddDisplayString("Did not find a NetPlay Session on the server."); 

    StopNetPlay(); 

    return; 

  } 

  if (!geCSNetMgr_JoinSession(NetMgr, thisClientName, &Session[0])) { 

    AddDisplayString("Found a session on the server, but cannot join it."); 

    StopNetPlay(); 

    return; 

  } 

  AddDisplayString("Client is connected to server!!"); 

  ClientMode = GE_TRUE; 

} 
void ClientCycle(void) { 

  geCSNetMgr_NetMsgType MsgType; 

  int32 Size = 0; 

  void *msg_data = NULL; 

  char chat_str[1024]; 

  
  

  while (geCSNetMgr_ReceiveFromServer(NetMgr, &MsgType, &Size, (uint8 **)(&msg_data))) { 

    if (MsgType == NET_MSG_USER && Size > 1) { 

      switch(*((uint8 *)msg_data)) { 

        case SUBTYPE_CHAT: 

          strncpy(chat_str, (char *)(msg_data) + 1, Size - 1); 

          AddDisplayString(chat_str); 

          break; 

      } 

    } 

  } 

} 
void SendChatToServer(const char* chat_str) { 

  static char send_buf[2048]; 

  int bufferlen = 0; 

  
  send_buf[0] = SUBTYPE_CHAT; 
  strcpy(send_buf + 1, chat_str); 

  bufferlen = strlen(chat_str) + 2; 
  if (!geCSNetMgr_SendToServer(NetMgr, GE_FALSE, send_buf, bufferlen)) 

   AddDisplayString("SendToServer failed."); 

} 
void ServerCycle(void) { 

  uint32 MsgType; 

  geCSNetMgr_NetID cId; 

  int32 Size = 0; 

  uint8 *msg_data = NULL; 

  char chat_str[1024]; 

  char *msg_copy = NULL; 

  int i; 
  

  
  

  while (geCSNetMgr_ReceiveFromClient(NetMgr, &MsgType, &cId, &Size, &msg_data)) { 
    //make a copy of the buffer immeadiatly. It may become invalid later. 

    msg_copy = malloc(Size + 1); 

    memcpy(msg_copy, msg_data, Size); 
    if (MsgType == NET_MSG_USER && Size > 1) { 

      switch(*((uint8 *)msg_copy)) { 

        case SUBTYPE_CHAT: 

          for (i = 0; i < 64; i++) { 

            if (clients[i] != NULL) { 

              if (clients[i]->Id == cId) break; 

            } 

          } 

          // for the moment the To parameter is ignored 

          // any xmit to any client, goes to all clients 

          // this will almost certainly change. 

         if (clients[i] != NULL) { 

            sprintf(chat_str, "%s> %s", clients[i]->Name, (char *)msg_copy + 1); 

            SendChatToClient(clients[i], chat_str); 

         } 
  

  
         //AddDisplayString(chat_str); 

         //The server now runs ClientCycle 

         //and gets back it own chat messages thru there. 
         break; 

      } 

    } 
    if (MsgType == NET_MSG_CREATE_CLIENT) { 

     AddDisplayString("A Client has Joined!!"); 

      RestoreIfMined(); 

      for (i = 0; i < 64; i++) { 

        if (clients[i] == NULL) { 

          clients[i] = malloc(sizeof(geCSNetMgr_NetClient)); 

          memcpy(clients[i], msg_copy, sizeof(geCSNetMgr_NetClient)); 

          break; 

        } 

      } 

    } 
    if (MsgType == NET_MSG_DESTROY_CLIENT) { 

     AddDisplayString("A Client has left."); 

      for (i = 0; i < 64; i++) { 

      if (clients[i] != NULL) { 

       if (clients[i]->Id == ((geCSNetMgr_NetClient *)(msg_copy))->Id) { 

        free(clients[i]); 

        clients[i] = NULL; 

       } 

        } 

      } 

    } 
  } 

} 
void SendChatToClient(geCSNetMgr_NetClient *client, const char *chat_str) { 

  static char send_buf[2048]; 

  int bufferlen = 0; 

  
  send_buf[0] = SUBTYPE_CHAT; 
  strcpy(send_buf + 1, chat_str); 

  bufferlen = strlen(chat_str) + 2; 
  geCSNetMgr_SendToClient(NetMgr, GE_FALSE, client->Id, send_buf, bufferlen);