]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/stand_gui.cpp
make sure std gui handles all variants of port cmdline option
[taylor/freespace2.git] / src / network / stand_gui.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9
10 #include "stand_gui.h"
11 #include "osregistry.h"
12
13 #include "wx/statline.h"
14 #include "wx/filename.h"
15 #include "wx/stdpaths.h"
16 #include "wx/utils.h"
17 #include "wx/msgdlg.h"
18 #include "wx/process.h"
19 #include "wx/textfile.h"
20 #include "wx/socket.h"
21 #include "wx/cmdline.h"
22
23 #include "SDL.h"
24
25
26 // taken from psnet.h and psnet2.h
27 #ifdef MAKE_FS1
28         #define DEFAULT_GAME_PORT 4000
29 #elif defined(FS2_DEMO)
30         #define DEFAULT_GAME_PORT 7802
31 #else
32         #define DEFAULT_GAME_PORT 7808
33 #endif
34
35 // taken from osregistry.cpp
36 const char *Osreg_company_name = "Volition";
37 #if defined(FS1_DEMO)
38 const char *Osreg_app_name = "FreeSpaceDemo";
39 #define PROFILE_NAME "FreeSpaceDemo.ini"
40 #elif defined(FS2_DEMO)
41 const char *Osreg_app_name = "FreeSpace2Demo";
42 #define PROFILE_NAME "FreeSpace2Demo.ini"
43 #elif defined(OEM_BUILD)
44 const char *Osreg_app_name = "FreeSpace2OEM";
45 #define PROFILE_NAME "FreeSpace2OEM.ini"
46 #elif defined(MAKE_FS1)
47 const char *Osreg_app_name = "FreeSpace";
48 #define PROFILE_NAME "FreeSpace.ini"
49 #else
50 const char *Osreg_app_name = "FreeSpace2";
51 #define PROFILE_NAME "FreeSpace2.ini"
52 #endif
53
54
55 IMPLEMENT_APP(StandaloneApp)
56
57 bool StandaloneApp::OnInit()
58 {
59         if ( !wxApp::OnInit() ) {
60                 return false;
61         }
62
63         std_client = new Standalone(NULL);
64
65         try {
66                 if ( !std_client->startFreeSpace(argc, argv) ) {
67                         throw "Unable to start FreeSpace";
68                 }
69
70                 wxMilliSleep(500);
71
72                 if ( !std_client->wsInitialize() ) {
73                         throw "Unable to initialize WebSocket";
74                 }
75         } catch (const char *err) {
76                 wxMessageBox(err, "Error!", wxOK|wxICON_ERROR|wxCENTRE|wxSTAY_ON_TOP);
77
78                 return false;
79         }
80
81         std_client->Show(true);
82         SetTopWindow(std_client);
83
84         return true;
85 }
86
87 void StandaloneApp::OnInitCmdLine(wxCmdLineParser& parser)
88 {
89         parser.SetCmdLine( wxT("") );
90 }
91
92 bool StandaloneApp::OnCmdLineParsed(wxCmdLineParser& WXUNUSED(parser))
93 {
94         return true;
95 }
96
97 StandaloneTimer::StandaloneTimer(Standalone *stand)
98 {
99         m_stand = stand;
100 }
101
102 StandaloneTimer::~StandaloneTimer()
103 {
104 }
105
106 void StandaloneTimer::Notify()
107 {
108         m_stand->wsDoFrame();
109 }
110
111 StandPopup::StandPopup( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
112 {
113         this->SetSizeHints( wxSize( 300,100 ), wxDefaultSize );
114
115         wxBoxSizer* bSizer2;
116         bSizer2 = new wxBoxSizer( wxVERTICAL );
117
118         wxFlexGridSizer* fgSizer3;
119         fgSizer3 = new wxFlexGridSizer( 0, 2, 0, 0 );
120         fgSizer3->AddGrowableRow(0, 1);
121
122         m_Label1 = new wxStaticText( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
123         m_Label1->Wrap( -1 );
124         fgSizer3->Add( m_Label1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
125
126         m_Label2 = new wxStaticText( this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
127         m_Label2->Wrap( -1 );
128         fgSizer3->Add( m_Label2, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 5 );
129
130
131         bSizer2->Add( fgSizer3, 1, wxALL|wxEXPAND, 10 );
132
133
134         this->SetSizer( bSizer2 );
135         this->Layout();
136         bSizer2->Fit( this );
137
138         this->Centre( wxBOTH );
139 }
140
141 StandPopup::~StandPopup()
142 {
143 }
144
145 wxBEGIN_EVENT_TABLE(Standalone, wxDialog)
146         EVT_CLOSE(Standalone::OnClose)
147         EVT_BUTTON(ID_B_SHUTDOWN, Standalone::OnShutdown)
148         EVT_BUTTON(ID_B_KICK, Standalone::OnKick)
149         EVT_BUTTON(ID_B_MREFRESH, Standalone::OnMissionRefresh)
150         EVT_BUTTON(ID_B_RESET_ALL, Standalone::OnResetAll)
151         EVT_SLIDER(ID_FPS_SLIDER, Standalone::OnFPSSel)
152         EVT_TEXT_ENTER(ID_T_MSG, Standalone::OnServerMsg)
153         EVT_TEXT_ENTER(ID_T_SERVER_NAME, Standalone::OnServerNameChange)
154         EVT_TEXT_ENTER(ID_T_HOST_PASS, Standalone::OnHostPassChange)
155         EVT_CHOICE(ID_C_P_PLAYERS, Standalone::OnPinfoPlayer)
156 wxEND_EVENT_TABLE()
157
158 Standalone::Standalone( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
159 {
160         this->SetSizeHints( wxDefaultSize, wxDefaultSize );
161
162         fspid = 0;
163         fsport = DEFAULT_GAME_PORT;
164         stand_context = NULL;
165         wsi_standalone = NULL;
166         m_rate_limit = 0;
167
168         m_timer = new StandaloneTimer(this);
169         m_timer->Start(1000/30);
170
171         m_popup = new StandPopup(this);
172         m_popup->Show(false);
173
174         wxBoxSizer* bSizer1;
175         bSizer1 = new wxBoxSizer( wxVERTICAL );
176
177         wxNotebook* nbook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
178
179         createTab_Server(nbook);
180         createTab_Multi(nbook);
181         createTab_Player(nbook);
182         createTab_GodStuff(nbook);
183         createTab_Debug(nbook);
184
185         bSizer1->Add( nbook, 1, wxEXPAND | wxALL, 5 );
186
187         wxButton* bshutdown = new wxButton( this, ID_B_SHUTDOWN, wxT("Shutdown"), wxDefaultPosition, wxDefaultSize, 0 );
188         bSizer1->Add( bshutdown, 0, wxALIGN_CENTER|wxALL, 5 );
189
190
191         this->SetSizer( bSizer1 );
192         bSizer1->Fit(this);
193         bSizer1->SetSizeHints(this);
194
195         this->Centre( wxBOTH );
196 }
197
198 Standalone::~Standalone()
199 {
200         delete m_timer;
201 }
202
203 void Standalone::OnClose( wxCloseEvent& WXUNUSED(event) )
204 {
205         Shutdown();
206 }
207
208 void Standalone::OnShutdown( wxCommandEvent& WXUNUSED(event) )
209 {
210         Shutdown();
211 }
212
213 void Standalone::Shutdown()
214 {
215         m_timer->Stop();
216
217         if (stand_context) {
218                 std::string msg("shutdown");
219                 wsSend(msg);
220
221                 lws_service(stand_context, 50);
222
223                 lws_context_destroy(stand_context);
224         }
225
226         if ( wxProcess::Exists(fspid) ) {
227                 // wait a little bit, and if process still exists then kill it
228                 wxSleep(1);
229
230                 if ( wxProcess::Exists(fspid) ) {
231                         wxProcess::Kill(fspid, wxSIGTERM);
232                 }
233         }
234
235         Destroy();
236 }
237
238 void Standalone::OnServerNameChange(wxCommandEvent& WXUNUSED(event) )
239 {
240         std::string msg("S:name ");
241
242         msg.append( m_S_ServerName->GetValue().c_str() );
243
244         wsSend(msg);
245 }
246
247 void Standalone::OnHostPassChange(wxCommandEvent& WXUNUSED(event) )
248 {
249         std::string msg("S:pass ");
250
251         msg.append( m_S_HostPass->GetValue().c_str() );
252
253         wsSend(msg);
254 }
255
256 void Standalone::OnKick( wxCommandEvent& WXUNUSED(event) )
257 {
258         std::string msg("S:kick ");
259
260         long col, row;
261
262         if ( !m_S_Connections->PositionToXY(m_S_Connections->GetInsertionPoint(), &col, &row) ) {
263                 return;
264         }
265
266         wxString line = m_S_Connections->GetLineText(row);
267
268         if ( line.IsEmpty() ) {
269                 return;
270         }
271
272         wxArrayString ipaddr = wxSplit(line, ',');
273
274         msg.append( ipaddr.Item(0).c_str() );
275
276         wsSend(msg);
277 }
278
279 void Standalone::OnMissionRefresh( wxCommandEvent& WXUNUSED(event) )
280 {
281         std::string msg("G:mrefresh");
282
283         wsSend(msg);
284 }
285
286 void Standalone::OnResetAll( wxCommandEvent& WXUNUSED(event) )
287 {
288         std::string msg("reset");
289
290         wsSend(msg);
291 }
292
293 void Standalone::OnFPSSel( wxCommandEvent& WXUNUSED(event) )
294 {
295         wxString fps = wxString::Format("%d", m_M_sliderFPS->GetValue());
296
297         m_M_FPS->SetLabel(fps);
298
299         std::string msg("M:fps ");
300         msg.append(fps);
301
302         wsSend(msg);
303 }
304
305 void Standalone::OnServerMsg( wxCommandEvent& WXUNUSED(event) )
306 {
307         if ( m_GS_msg->GetValue().IsEmpty() ) {
308                 return;
309         }
310
311         std::string msg("G:smsg ");
312
313         msg.append( m_GS_msg->GetValue().c_str() );
314
315         // strip off return char which GetValue() has
316         size_t pos = msg.find_last_not_of("\r\n");
317
318         if (pos != std::string::npos) {
319                 msg.erase(pos+1);
320         }
321
322         wsSend(msg);
323
324         m_GS_msg->Clear();
325 }
326
327 void Standalone::OnPinfoPlayer(wxCommandEvent& WXUNUSED(event) )
328 {
329         int idx = m_P_Players->GetCurrentSelection();
330
331         if (idx == wxNOT_FOUND) {
332                 return;
333         }
334
335         std::string msg("P:info ");
336
337         msg.append( m_P_Players->GetString(idx).c_str() );
338
339         wsSend(msg);
340 }
341
342 void Standalone::createTab_Server(wxNotebook* parent)
343 {
344         wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
345
346         wxBoxSizer *panelSizer = new wxBoxSizer( wxVERTICAL );
347
348         wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
349
350         wxFlexGridSizer* fgSizer5 = new wxFlexGridSizer( 0, 2, 0, 0 );
351         fgSizer5->AddGrowableCol(1, 1);
352
353         wxStaticText* m_staticText6 = new wxStaticText( panel, wxID_ANY, wxT("Server Name"), wxDefaultPosition, wxDefaultSize, 0 );
354         fgSizer5->Add( m_staticText6, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
355
356         m_S_ServerName = new wxTextCtrl( panel, ID_T_SERVER_NAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
357         m_S_ServerName->SetMaxLength( 31 );
358         fgSizer5->Add( m_S_ServerName, 0, wxALL|wxEXPAND, 5 );
359
360         wxStaticText* m_staticText7 = new wxStaticText( panel, wxID_ANY, wxT("Host Password"), wxDefaultPosition, wxDefaultSize, 0 );
361         fgSizer5->Add( m_staticText7, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
362
363         m_S_HostPass = new wxTextCtrl( panel, ID_T_HOST_PASS, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
364         m_S_HostPass->SetMaxLength( 16 );
365         fgSizer5->Add( m_S_HostPass, 0, wxALL|wxEXPAND, 5 );
366
367         bSizer->Add( fgSizer5, 0, wxEXPAND, 0 );
368
369         wxStaticLine* m_staticline1 = new wxStaticLine( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
370         bSizer->Add( m_staticline1, 0, wxEXPAND | wxALL, 5 );
371
372         wxFlexGridSizer* fgSizer7 = new wxFlexGridSizer( 0, 2, 0, 0 );
373         fgSizer7->SetFlexibleDirection( wxBOTH );
374         fgSizer7->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
375
376         wxStaticText* m_staticText9 = new wxStaticText( panel, wxID_ANY, wxT("# Connections :"), wxDefaultPosition, wxDefaultSize, 0 );
377         fgSizer7->Add( m_staticText9, 0, wxALL, 5 );
378
379         m_S_NumConn = new wxStaticText( panel, wxID_ANY, wxT("0"), wxDefaultPosition, wxDefaultSize, 0 );
380         fgSizer7->Add( m_S_NumConn, 0, wxALL, 5 );
381
382         bSizer->Add( fgSizer7, 0, wxEXPAND, 5 );
383
384         wxStaticText* m_staticText8 = new wxStaticText( panel, wxID_ANY, wxT("Address and Ping"), wxDefaultPosition, wxDefaultSize, 0 );
385         bSizer->Add( m_staticText8, 0, wxALL, 5 );
386
387         wxFlexGridSizer* fgSizer3 = new wxFlexGridSizer( 0, 2, 0, 0 );
388         fgSizer3->SetFlexibleDirection( wxBOTH );
389         fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
390
391         m_S_Connections = new wxTextCtrl( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( 300,300 ), wxTE_MULTILINE|wxTE_READONLY|wxTE_NO_VSCROLL );
392         fgSizer3->Add( m_S_Connections, 0, wxALL|wxEXPAND, 5 );
393
394         wxBoxSizer* bbSizer = new wxBoxSizer( wxVERTICAL );
395
396         m_S_btnKick = new wxButton( panel, ID_B_KICK, wxT("Kick"), wxDefaultPosition, wxSize( -1,-1 ), 0 );
397         bbSizer->Add( m_S_btnKick, 1, wxALL, 5 );
398
399         m_S_btnMRefresh = new wxButton( panel, ID_B_MREFRESH, wxT(" Refresh\nMissions"), wxDefaultPosition, wxDefaultSize, 0 );
400         bbSizer->Add( m_S_btnMRefresh, 2, wxALL, 5 );
401
402         m_S_btnResetAll = new wxButton( panel, ID_B_RESET_ALL, wxT("Reset All"), wxDefaultPosition, wxDefaultSize, 0 );
403         bbSizer->Add( m_S_btnResetAll, 3, wxALL, 5 );
404
405         fgSizer3->Add( bbSizer, 1, wxEXPAND, 5 );
406
407         bSizer->Add( fgSizer3, 0, wxEXPAND, 5 );
408
409         panelSizer->Add( bSizer, 1, wxALL|wxEXPAND, 5 );
410
411         panel->SetSizer( panelSizer );
412         panelSizer->Fit( panel );
413         panelSizer->SetSizeHints( panel );
414
415         parent->AddPage( panel, wxT("Server"), true );
416 }
417
418 void Standalone::createTab_Multi(wxNotebook* parent)
419 {
420         wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
421
422         wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL);
423
424         wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
425
426         m_M_sliderFPS = new wxSlider( panel, ID_FPS_SLIDER, 30, 15, 60, wxDefaultPosition, wxDefaultSize, wxSL_HORIZONTAL );
427         bSizer->Add( m_M_sliderFPS, 0, wxALL|wxEXPAND, 5 );
428
429         wxFlexGridSizer* fgSizer8;
430         fgSizer8 = new wxFlexGridSizer( 0, 5, 0, 0 );
431         fgSizer8->AddGrowableCol(2, 1);
432
433         wxStaticText* m_staticText71 = new wxStaticText( panel, wxID_ANY, wxT("Frame Cap : "), wxDefaultPosition, wxDefaultSize, 0 );
434         m_staticText71->Wrap( -1 );
435         fgSizer8->Add( m_staticText71, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
436
437         wxPanel* fpsPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
438         wxBoxSizer* fpsSizer = new wxBoxSizer(wxHORIZONTAL);
439         m_M_FPS = new wxStaticText( fpsPanel, wxID_ANY, "30", wxDefaultPosition, wxSize(60, -1), 0 );
440         m_M_FPS->Wrap( -1 );
441         fpsSizer->Add( m_M_FPS, 0, wxALL, 2 );
442         fpsPanel->SetSizer(fpsSizer);
443         fpsSizer->Fit(fpsPanel);
444         fgSizer8->Add(fpsPanel, 0, wxALL, 3);
445
446
447         fgSizer8->Add( 0, 0, 1, wxEXPAND );
448
449         wxStaticText* m_staticText81 = new wxStaticText( panel, wxID_ANY, wxT("Realized FPS : "), wxDefaultPosition, wxDefaultSize, 0 );
450         m_staticText81->Wrap( -1 );
451         fgSizer8->Add( m_staticText81, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
452
453         wxPanel*fpscapPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
454         wxBoxSizer* fpscapSizer = new wxBoxSizer(wxHORIZONTAL);
455         m_M_FPSRel = new wxStaticText( fpscapPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(60, -1), 0 );
456         m_M_FPSRel->Wrap( -1 );
457         fpscapSizer->Add( m_M_FPSRel, 0, wxALL, 2 );
458         fpscapPanel->SetSizer(fpscapSizer);
459         fpscapSizer->Fit(fpscapPanel);
460         fgSizer8->Add(fpscapPanel, 0, wxALL, 3);
461
462
463         bSizer->Add( fgSizer8, 0, wxEXPAND, 5 );
464
465         wxStaticLine* m_staticline2 = new wxStaticLine( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
466         bSizer->Add( m_staticline2, 0, wxEXPAND | wxALL, 5 );
467
468
469         bSizer->Add( 0, 0, 0, wxALL, 20 );
470
471
472         wxFlexGridSizer* fgSizer9;
473         fgSizer9 = new wxFlexGridSizer( 0, 2, 0, 0 );
474         fgSizer9->SetFlexibleDirection( wxBOTH );
475         fgSizer9->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
476
477         wxStaticText* m_staticText91 = new wxStaticText( panel, wxID_ANY, wxT("Mission Name : "), wxDefaultPosition, wxDefaultSize, 0 );
478         m_staticText91->Wrap( -1 );
479         fgSizer9->Add( m_staticText91, 0, wxALL, 5 );
480
481         wxPanel* mnPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
482         wxBoxSizer* mnSizer = new wxBoxSizer(wxHORIZONTAL);
483         m_M_MissionName = new wxStaticText( mnPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0 );
484         m_M_MissionName->Wrap( -1 );
485         mnSizer->Add( m_M_MissionName, 0, wxALL, 2 );
486         mnPanel->SetSizer(mnSizer);
487         mnSizer->Fit(mnPanel);
488         fgSizer9->Add(mnPanel, 0, wxALL, 3);
489
490         wxStaticText* m_staticText101 = new wxStaticText( panel, wxID_ANY, wxT("Mission Time : "), wxDefaultPosition, wxDefaultSize, 0 );
491         m_staticText101->Wrap( -1 );
492         fgSizer9->Add( m_staticText101, 0, wxALL, 5 );
493
494         wxPanel* mtPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
495         wxBoxSizer* mtSizer = new wxBoxSizer(wxHORIZONTAL);
496         m_M_MissionTime = new wxStaticText( mtPanel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(150, -1), 0 );
497         m_M_MissionTime->Wrap( -1 );
498         mtSizer->Add( m_M_MissionTime, 0, wxALL, 2 );
499         mtPanel->SetSizer(mtSizer);
500         mtSizer->Fit(mtPanel);
501         fgSizer9->Add(mtPanel, 0, wxALL, 3);
502
503
504         bSizer->Add( fgSizer9, 0, wxEXPAND, 5 );
505
506
507         bSizer->Add( 0, 0, 0, wxALL, 20 );
508
509         wxBoxSizer* bSizer3 = new wxBoxSizer(wxHORIZONTAL);
510
511         wxBoxSizer* bSizer4 = new wxBoxSizer(wxVERTICAL);
512
513         wxStaticText* m_staticText11 = new wxStaticText( panel, wxID_ANY, wxT("Mission Goals"), wxDefaultPosition, wxDefaultSize, 0 );
514         m_staticText11->Wrap( -1 );
515         bSizer4->Add( m_staticText11, 0, wxALL, 5 );
516
517         m_M_Goals = new wxTreeCtrl( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT|wxTR_NO_BUTTONS );
518
519         wxImageList *goal_imgs = new wxImageList(16, 16);
520
521         goal_imgs->Add( wxBitmap(goal_ord_xpm), wxColour(0xff, 0x0, 0xff) );
522         goal_imgs->Add( wxBitmap(goal_none_xpm), wxColour(0xff, 0xff, 0xff) );
523         goal_imgs->Add( wxBitmap(goal_inc_xpm), wxColour(0xff, 0xff, 0xff) );
524         goal_imgs->Add( wxBitmap(goal_com_xpm), wxColour(0xff, 0xff, 0xff) );
525         goal_imgs->Add( wxBitmap(goal_fail_xpm), wxColour(0xff, 0xff, 0xff) );
526
527         m_M_Goals->AssignImageList(goal_imgs);
528
529         wxTreeItemId root = m_M_Goals->AddRoot( wxT("Goals") );
530
531         m_M_GoalItems[0] = m_M_Goals->AppendItem(root, wxT("Primary Objectives"), 0);
532         m_M_GoalItems[1] = m_M_Goals->AppendItem(root, wxT("Secondary Objectives"), 0);
533         m_M_GoalItems[2] = m_M_Goals->AppendItem(root, wxT("Bonus Objectives"), 0);
534
535         bSizer4->Add( m_M_Goals, 1, wxALL|wxEXPAND, 5 );
536
537         bSizer3->Add(bSizer4, 1, wxEXPAND);
538
539         wxBoxSizer* bSizer5 = new wxBoxSizer(wxVERTICAL);
540
541         wxStaticText* m_staticText12 = new wxStaticText( panel, wxID_ANY, wxT("Netgame Information"), wxDefaultPosition, wxDefaultSize, 0 );
542         m_staticText12->Wrap( -1 );
543         bSizer5->Add( m_staticText12, 0, wxALIGN_CENTER_HORIZONTAL|wxALL, 5 );
544
545         wxFlexGridSizer* fgSizer11;
546         fgSizer11 = new wxFlexGridSizer( 0, 2, 0, 15 );
547         fgSizer11->AddGrowableRow(0, 1);
548         fgSizer11->AddGrowableRow(1, 1);
549         fgSizer11->AddGrowableRow(2, 1);
550         fgSizer11->AddGrowableRow(3, 1);
551
552         wxStaticText* m_staticText13 = new wxStaticText( panel, wxID_ANY, wxT("Max Players"), wxDefaultPosition, wxDefaultSize, 0 );
553         m_staticText13->Wrap( -1 );
554         fgSizer11->Add( m_staticText13, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
555
556         m_M_ngMaxPlayers = new wxStaticText( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
557         m_M_ngMaxPlayers->Wrap( -1 );
558         fgSizer11->Add( m_M_ngMaxPlayers, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
559
560         wxStaticText* m_staticText15 = new wxStaticText( panel, wxID_ANY, wxT("Max Observers"), wxDefaultPosition, wxDefaultSize, 0 );
561         m_staticText15->Wrap( -1 );
562         fgSizer11->Add( m_staticText15, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
563
564         m_M_ngMaxObservers = new wxStaticText( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
565         m_M_ngMaxObservers->Wrap( -1 );
566         fgSizer11->Add( m_M_ngMaxObservers, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
567
568         wxStaticText* m_staticText17 = new wxStaticText( panel, wxID_ANY, wxT("Security"), wxDefaultPosition, wxDefaultSize, 0 );
569         m_staticText17->Wrap( -1 );
570         fgSizer11->Add( m_staticText17, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
571
572         m_M_ngSecurity = new wxStaticText( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
573         m_M_ngSecurity->Wrap( -1 );
574         fgSizer11->Add( m_M_ngSecurity, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
575
576         wxStaticText* m_staticText19 = new wxStaticText( panel, wxID_ANY, wxT("Respawns"), wxDefaultPosition, wxDefaultSize, 0 );
577         m_staticText19->Wrap( -1 );
578         fgSizer11->Add( m_staticText19, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
579
580         m_M_ngRespawns = new wxStaticText( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
581         m_M_ngRespawns->Wrap( -1 );
582         fgSizer11->Add( m_M_ngRespawns, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
583
584         bSizer5->Add( fgSizer11, 1, wxEXPAND );
585
586         bSizer3->Add(bSizer5, 1, wxEXPAND);
587
588         bSizer->Add( bSizer3, 1, wxEXPAND, 5 );
589
590         panelSizer->Add(bSizer, 1, wxALL|wxEXPAND, 5);
591
592         panel->SetSizer( panelSizer );
593         panelSizer->Fit( panel );
594         panelSizer->SetSizeHints( panel );
595
596         parent->AddPage( panel, wxT("Multi-Player") );
597 }
598
599 void Standalone::createTab_Player(wxNotebook* parent)
600 {
601         wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
602
603         wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
604
605         wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
606
607         wxBoxSizer* bSizer21;
608         bSizer21 = new wxBoxSizer( wxHORIZONTAL );
609
610         wxStaticText* m_staticText211 = new wxStaticText( panel, wxID_ANY, wxT("Player"), wxDefaultPosition, wxDefaultSize, 0 );
611         m_staticText211->Wrap( -1 );
612         bSizer21->Add( m_staticText211, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
613
614         wxArrayString m_choice11Choices;
615         m_P_Players = new wxChoice( panel, ID_C_P_PLAYERS, wxDefaultPosition, wxSize(200, -1), m_choice11Choices, 0 );
616         m_P_Players->SetSelection( wxNOT_FOUND );
617         bSizer21->Add( m_P_Players, 0, wxALL, 5 );
618
619         bSizer->Add( bSizer21, 0, wxEXPAND );
620
621
622         wxStaticLine* m_staticline4 = new wxStaticLine( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL );
623         bSizer->Add( m_staticline4, 0, wxEXPAND | wxALL, 5 );
624
625         wxFlexGridSizer* fgSizer14;
626         fgSizer14 = new wxFlexGridSizer( 0, 2, 0, 0 );
627
628         wxStaticText* m_staticText26 = new wxStaticText( panel, wxID_ANY, wxT("Ship Type"), wxDefaultPosition, wxDefaultSize, 0 );
629         m_staticText26->Wrap( -1 );
630         fgSizer14->Add( m_staticText26, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
631
632         wxPanel* stPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
633         wxBoxSizer* stSizer = new wxBoxSizer(wxHORIZONTAL);
634         m_P_ShipType = new wxStaticText( stPanel, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(150, -1), 0 );
635         m_P_ShipType->Wrap( -1 );
636         stSizer->Add(m_P_ShipType, 0, wxALL, 2);
637         stPanel->SetSizer(stSizer);
638         stSizer->Fit(stPanel);
639         fgSizer14->Add( stPanel, 0, wxALL, 5 );
640
641         wxStaticText* m_staticText28 = new wxStaticText( panel, wxID_ANY, wxT("Avg Ping"), wxDefaultPosition, wxDefaultSize, 0 );
642         m_staticText28->Wrap( -1 );
643         fgSizer14->Add( m_staticText28, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
644
645         wxPanel* apPanel = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
646         wxBoxSizer* apSizer = new wxBoxSizer(wxHORIZONTAL);
647         m_P_AvgPing = new wxStaticText( apPanel, wxID_ANY, wxT(""), wxDefaultPosition, wxSize(150, -1), 0 );
648         m_P_AvgPing->Wrap( -1 );
649         apSizer->Add(m_P_AvgPing, 0, wxALL, 2);
650         apPanel->SetSizer(apSizer);
651         apSizer->Fit(apPanel);
652         fgSizer14->Add( apPanel, 0, wxALL, 5 );
653
654         bSizer->Add( fgSizer14, 0, wxEXPAND );
655
656         bSizer->Add( 0, 0, 0, wxALL, 10);
657
658         wxBoxSizer* bSizer6;
659         bSizer6 = new wxBoxSizer( wxHORIZONTAL );
660
661         //
662         // All-Time Stats
663         //
664
665         wxBoxSizer* atsboxSizer = new wxBoxSizer( wxVERTICAL );
666
667         wxStaticText* atsText = new wxStaticText( panel, wxID_ANY, wxT("All Time Stats") );
668         atsboxSizer->Add( atsText, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
669
670         wxFlexGridSizer* atsgSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
671         atsgSizer->AddGrowableCol(1, 1);
672
673         // primary shots
674         {
675                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Shots"), wxDefaultPosition, wxDefaultSize, 0 );
676                 itxt->Wrap( -1 );
677                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
678                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
679                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
680                 m_P_atsPriShots = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
681                 m_P_atsPriShots->Wrap( -1 );
682                 p1sizer->Add( m_P_atsPriShots, 1, wxALL|wxEXPAND, 1);
683                 p1->SetSizer( p1sizer );
684                 p1sizer->Fit( p1 );
685                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
686         }
687
688         // primary hits
689         {
690                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Hits"), wxDefaultPosition, wxDefaultSize, 0 );
691                 itxt->Wrap( -1 );
692                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
693                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
694                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
695                 m_P_atsPriHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
696                 m_P_atsPriHits->Wrap( -1 );
697                 p1sizer->Add( m_P_atsPriHits, 0, wxALL|wxEXPAND, 1);
698                 p1->SetSizer( p1sizer );
699                 p1sizer->Fit( p1 );
700                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
701         }
702
703         // primary BH hits
704         {
705                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary BH Hits"), wxDefaultPosition, wxDefaultSize, 0 );
706                 itxt->Wrap( -1 );
707                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
708                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
709                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
710                 m_P_atsPriBHHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
711                 m_P_atsPriBHHits->Wrap( -1 );
712                 p1sizer->Add( m_P_atsPriBHHits, 0, wxALL|wxEXPAND, 1);
713                 p1->SetSizer( p1sizer );
714                 p1sizer->Fit( p1 );
715                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
716         }
717
718         // primary hit %
719         {
720                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
721                 itxt->Wrap( -1 );
722                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
723                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
724                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
725                 m_P_atsPriHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
726                 m_P_atsPriHitPer->Wrap( -1 );
727                 p1sizer->Add( m_P_atsPriHitPer, 0, wxALL|wxEXPAND, 1);
728                 p1->SetSizer( p1sizer );
729                 p1sizer->Fit( p1 );
730                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
731         }
732
733         // primary BH hit %
734         {
735                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary BH Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
736                 itxt->Wrap( -1 );
737                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
738                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
739                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
740                 m_P_atsPriBHHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
741                 m_P_atsPriBHHitPer->Wrap( -1 );
742                 p1sizer->Add( m_P_atsPriBHHitPer, 0, wxALL|wxEXPAND, 1);
743                 p1->SetSizer( p1sizer );
744                 p1sizer->Fit( p1 );
745                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
746         }
747
748         // secondary shots
749         {
750                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Shots"), wxDefaultPosition, wxDefaultSize, 0 );
751                 itxt->Wrap( -1 );
752                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
753                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
754                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
755                 m_P_atsSecShots = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
756                 m_P_atsSecShots->Wrap( -1 );
757                 p1sizer->Add( m_P_atsSecShots, 0, wxALL|wxEXPAND, 1);
758                 p1->SetSizer( p1sizer );
759                 p1sizer->Fit( p1 );
760                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
761         }
762
763         // secondary hits
764         {
765                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Hits"), wxDefaultPosition, wxDefaultSize, 0 );
766                 itxt->Wrap( -1 );
767                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
768                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
769                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
770                 m_P_atsSecHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
771                 m_P_atsSecHits->Wrap( -1 );
772                 p1sizer->Add( m_P_atsSecHits, 0, wxALL|wxEXPAND, 1);
773                 p1->SetSizer( p1sizer );
774                 p1sizer->Fit( p1 );
775                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
776         }
777
778         // secondary BH hits
779         {
780                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary BH Hits"), wxDefaultPosition, wxDefaultSize, 0 );
781                 itxt->Wrap( -1 );
782                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
783                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
784                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
785                 m_P_atsSecBHHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
786                 m_P_atsSecBHHits->Wrap( -1 );
787                 p1sizer->Add( m_P_atsSecBHHits, 0, wxALL|wxEXPAND, 1);
788                 p1->SetSizer( p1sizer );
789                 p1sizer->Fit( p1 );
790                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
791         }
792
793         // secondary hit %
794         {
795                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
796                 itxt->Wrap( -1 );
797                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
798                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
799                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
800                 m_P_atsSecHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
801                 m_P_atsSecHitPer->Wrap( -1 );
802                 p1sizer->Add( m_P_atsSecHitPer, 0, wxALL|wxEXPAND, 1);
803                 p1->SetSizer( p1sizer );
804                 p1sizer->Fit( p1 );
805                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
806         }
807
808         // secondary BH hit %
809         {
810                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary BH Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
811                 itxt->Wrap( -1 );
812                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
813                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
814                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
815                 m_P_atsSecBHHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
816                 m_P_atsSecBHHitPer->Wrap( -1 );
817                 p1sizer->Add( m_P_atsSecBHHitPer, 0, wxALL|wxEXPAND, 1);
818                 p1->SetSizer( p1sizer );
819                 p1sizer->Fit( p1 );
820                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
821         }
822
823         // assists
824         {
825                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Assists"), wxDefaultPosition, wxDefaultSize, 0 );
826                 itxt->Wrap( -1 );
827                 atsgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
828                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
829                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
830                 m_P_atsAssists = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
831                 m_P_atsAssists->Wrap( -1 );
832                 p1sizer->Add( m_P_atsAssists, 0, wxALL|wxEXPAND, 1);
833                 p1->SetSizer( p1sizer );
834                 p1sizer->Fit( p1 );
835                 atsgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
836         }
837
838         atsboxSizer->Add( atsgSizer, 1, wxEXPAND );
839
840         bSizer6->Add( atsboxSizer, 1, wxEXPAND, 5 );
841
842         wxStaticLine* m_staticline3 = new wxStaticLine( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_VERTICAL );
843         bSizer6->Add( m_staticline3, 0, wxEXPAND | wxALL, 5 );
844
845         //
846         // Mission Stats
847         //
848
849         wxBoxSizer* msboxSizer = new wxBoxSizer( wxVERTICAL );
850
851         wxStaticText* msText = new wxStaticText( panel, wxID_ANY, wxT("Mission Stats") );
852         msboxSizer->Add( msText, 0, wxALL|wxALIGN_CENTER_HORIZONTAL, 5 );
853
854         wxFlexGridSizer* msgSizer = new wxFlexGridSizer( 0, 2, 0, 0 );
855         msgSizer->AddGrowableCol(1, 1);
856
857         // primary shots
858         {
859                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Shots"), wxDefaultPosition, wxDefaultSize, 0 );
860                 itxt->Wrap( -1 );
861                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
862                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
863                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
864                 m_P_msPriShots = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
865                 m_P_msPriShots->Wrap( -1 );
866                 p1sizer->Add( m_P_msPriShots, 1, wxALL|wxEXPAND, 1);
867                 p1->SetSizer( p1sizer );
868                 p1sizer->Fit( p1 );
869                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
870         }
871
872         // primary hits
873         {
874                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Hits"), wxDefaultPosition, wxDefaultSize, 0 );
875                 itxt->Wrap( -1 );
876                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
877                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
878                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
879                 m_P_msPriHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
880                 m_P_msPriHits->Wrap( -1 );
881                 p1sizer->Add( m_P_msPriHits, 0, wxALL|wxEXPAND, 1);
882                 p1->SetSizer( p1sizer );
883                 p1sizer->Fit( p1 );
884                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
885         }
886
887         // primary BH hits
888         {
889                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary BH Hits"), wxDefaultPosition, wxDefaultSize, 0 );
890                 itxt->Wrap( -1 );
891                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
892                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
893                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
894                 m_P_msPriBHHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
895                 m_P_msPriBHHits->Wrap( -1 );
896                 p1sizer->Add( m_P_msPriBHHits, 0, wxALL|wxEXPAND, 1);
897                 p1->SetSizer( p1sizer );
898                 p1sizer->Fit( p1 );
899                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
900         }
901
902         // primary hit %
903         {
904                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
905                 itxt->Wrap( -1 );
906                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
907                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
908                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
909                 m_P_msPriHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
910                 m_P_msPriHitPer->Wrap( -1 );
911                 p1sizer->Add( m_P_msPriHitPer, 0, wxALL|wxEXPAND, 1);
912                 p1->SetSizer( p1sizer );
913                 p1sizer->Fit( p1 );
914                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
915         }
916
917         // primary BH hit %
918         {
919                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Primary BH Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
920                 itxt->Wrap( -1 );
921                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
922                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
923                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
924                 m_P_msPriBHHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
925                 m_P_msPriBHHitPer->Wrap( -1 );
926                 p1sizer->Add( m_P_msPriBHHitPer, 0, wxALL|wxEXPAND, 1);
927                 p1->SetSizer( p1sizer );
928                 p1sizer->Fit( p1 );
929                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
930         }
931
932         // secondary shots
933         {
934                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Shots"), wxDefaultPosition, wxDefaultSize, 0 );
935                 itxt->Wrap( -1 );
936                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
937                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
938                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
939                 m_P_msSecShots = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
940                 m_P_msSecShots->Wrap( -1 );
941                 p1sizer->Add( m_P_msSecShots, 0, wxALL|wxEXPAND, 1);
942                 p1->SetSizer( p1sizer );
943                 p1sizer->Fit( p1 );
944                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
945         }
946
947         // secondary hits
948         {
949                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Hits"), wxDefaultPosition, wxDefaultSize, 0 );
950                 itxt->Wrap( -1 );
951                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
952                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
953                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
954                 m_P_msSecHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
955                 m_P_msSecHits->Wrap( -1 );
956                 p1sizer->Add( m_P_msSecHits, 0, wxALL|wxEXPAND, 1);
957                 p1->SetSizer( p1sizer );
958                 p1sizer->Fit( p1 );
959                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
960         }
961
962         // secondary BH hits
963         {
964                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary BH Hits"), wxDefaultPosition, wxDefaultSize, 0 );
965                 itxt->Wrap( -1 );
966                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
967                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
968                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
969                 m_P_msSecBHHits = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
970                 m_P_msSecBHHits->Wrap( -1 );
971                 p1sizer->Add( m_P_msSecBHHits, 0, wxALL|wxEXPAND, 1);
972                 p1->SetSizer( p1sizer );
973                 p1sizer->Fit( p1 );
974                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
975         }
976
977         // secondary hit %
978         {
979                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
980                 itxt->Wrap( -1 );
981                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
982                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
983                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
984                 m_P_msSecHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
985                 m_P_msSecHitPer->Wrap( -1 );
986                 p1sizer->Add( m_P_msSecHitPer, 0, wxALL|wxEXPAND, 1);
987                 p1->SetSizer( p1sizer );
988                 p1sizer->Fit( p1 );
989                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
990         }
991
992         // secondary BH hit %
993         {
994                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Secondary BH Hit %"), wxDefaultPosition, wxDefaultSize, 0 );
995                 itxt->Wrap( -1 );
996                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
997                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
998                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
999                 m_P_msSecBHHitPer = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
1000                 m_P_msSecBHHitPer->Wrap( -1 );
1001                 p1sizer->Add( m_P_msSecBHHitPer, 0, wxALL|wxEXPAND, 1);
1002                 p1->SetSizer( p1sizer );
1003                 p1sizer->Fit( p1 );
1004                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
1005         }
1006
1007         // assists
1008         {
1009                 wxStaticText* itxt = new wxStaticText( panel, wxID_ANY, wxT("Assists"), wxDefaultPosition, wxDefaultSize, 0 );
1010                 itxt->Wrap( -1 );
1011                 msgSizer->Add( itxt, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
1012                 wxPanel* p1 = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER);
1013                 wxBoxSizer* p1sizer = new wxBoxSizer( wxHORIZONTAL );
1014                 m_P_msAssists = new wxStaticText( p1, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, 0 );
1015                 m_P_msAssists->Wrap( -1 );
1016                 p1sizer->Add( m_P_msAssists, 0, wxALL|wxEXPAND, 1);
1017                 p1->SetSizer( p1sizer );
1018                 p1sizer->Fit( p1 );
1019                 msgSizer->Add( p1, 0, wxALL|wxEXPAND, 1 );
1020         }
1021
1022         msboxSizer->Add( msgSizer, 1, wxEXPAND );
1023
1024         bSizer6->Add( msboxSizer, 1, wxEXPAND );
1025
1026         bSizer->Add( bSizer6, 1, wxEXPAND );
1027
1028         panelSizer->Add( bSizer, 1, wxALL|wxEXPAND, 5 );
1029
1030         panel->SetSizer( panelSizer );
1031         panelSizer->Fit( panel );
1032         panelSizer->SetSizeHints(panel);
1033
1034         parent->AddPage( panel, wxT("Player Info") );
1035 }
1036
1037 void Standalone::createTab_GodStuff(wxNotebook* parent)
1038 {
1039         wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
1040
1041         wxBoxSizer* panelSizer = new wxBoxSizer( wxVERTICAL );
1042
1043         wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
1044
1045         wxBoxSizer* bSizer2 = new wxBoxSizer( wxHORIZONTAL );
1046
1047         wxStaticText* m_staticText21 = new wxStaticText( panel, wxID_ANY, wxT("Player"), wxDefaultPosition, wxDefaultSize, 0 );
1048         bSizer2->Add( m_staticText21, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
1049
1050         wxArrayString m_choice1Choices;
1051         m_GS_Players = new wxChoice( panel, wxID_ANY, wxDefaultPosition, wxSize(200, -1), m_choice1Choices, 0 );
1052         m_GS_Players->SetSelection( wxNOT_FOUND );
1053         bSizer2->Add( m_GS_Players, 0, wxALL|wxEXPAND, 5 );
1054
1055         bSizer->Add( bSizer2, 0, wxEXPAND, 5 );
1056
1057         bSizer->Add( 0, 0, 0, wxALL, 10 );
1058
1059         wxStaticText* m_staticText22 = new wxStaticText( panel, wxID_ANY, wxT("Server Message"), wxDefaultPosition, wxDefaultSize, 0 );
1060         bSizer->Add( m_staticText22, 0, wxALL, 5 );
1061
1062         m_GS_msg = new wxTextCtrl( panel, ID_T_MSG, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
1063         m_GS_msg->SetMaxLength(150);
1064         bSizer->Add( m_GS_msg, 0, wxALL|wxEXPAND, 5 );
1065
1066         bSizer->Add( 0, 0, 0, wxALL, 5 );
1067
1068         m_GS_Messages = new wxTextCtrl( panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY );
1069         bSizer->Add( m_GS_Messages, 1, wxALL|wxEXPAND, 5 );
1070
1071         panelSizer->Add( bSizer, 1, wxALL|wxEXPAND, 5 );
1072
1073         panel->SetSizer( panelSizer );
1074         panelSizer->Fit( panel );
1075         panelSizer->SetSizeHints( panel );
1076
1077         parent->AddPage( panel, wxT("God Stuff") );
1078 }
1079
1080 void Standalone::createTab_Debug(wxNotebook* parent)
1081 {
1082         wxPanel* panel = new wxPanel( parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
1083
1084         wxBoxSizer* bSizer = new wxBoxSizer( wxVERTICAL );
1085
1086         wxBoxSizer* hbSizer = new wxBoxSizer( wxHORIZONTAL );
1087
1088         wxStaticText* m_staticText1 = new wxStaticText( panel, wxID_ANY, wxT("Standalone State :"), wxDefaultPosition, wxDefaultSize, 0 );
1089         hbSizer->Add( m_staticText1, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
1090
1091         wxPanel* p1 = new wxPanel( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER );
1092
1093         wxBoxSizer* p1Sizer = new wxBoxSizer( wxHORIZONTAL );
1094
1095         m_D_State = new wxStaticText( p1, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
1096         p1Sizer->Add( m_D_State, 1, wxALL|wxEXPAND, 2);
1097
1098         p1->SetSizer( p1Sizer );
1099         p1Sizer->Fit( p1 );
1100
1101         hbSizer->Add( p1, 1, wxALL|wxEXPAND, 5 );
1102
1103         bSizer->Add( hbSizer, 0, wxALL|wxEXPAND, 5 );
1104
1105         panel->SetSizer( bSizer );
1106         panel->Layout();
1107
1108         bSizer->Fit( panel );
1109
1110         parent->AddPage( panel, wxT("Debug") );
1111 }
1112
1113 bool Standalone::startFreeSpace(int argc, wxCmdLineArgsArray &argv)
1114 {
1115         wxString epath = wxFileName(wxStandardPaths::Get().GetExecutablePath()).GetPath(true);
1116         bool cmd_port = false;
1117
1118         epath.Append( wxT("fs") );
1119
1120 #ifndef MAKE_FS1
1121         epath.Append( wxT("2") );
1122 #endif
1123
1124 #if defined(FS1_DEMO) || defined(FS2_DEMO)
1125         epath.Append( wxT("demo") );
1126 #endif
1127
1128 #ifdef _WIN32
1129         epath.Append( wxT(".exe") );
1130 #endif
1131
1132         epath.Append( wxT(" -standalone") );
1133
1134         for (int i = 1; i < argc; i++) {
1135                 wxString arg( argv[i] );
1136
1137                 // check if -port argument and set var
1138                 if ( (argc > i+1) && (arg.Contains( wxT("-port") ) || arg.IsSameAs( wxT("-o") )) ) {
1139                         fsport = wxAtoi(argv[i+1]);
1140                         cmd_port = true;
1141                 }
1142
1143                 epath.Append( wxT(" ") );
1144                 epath.Append(arg);
1145         }
1146
1147         // if port isn't specified on cmdline, check ini file for "ForcePort" value
1148         if ( !cmd_port ) {
1149                 char *u_path = SDL_GetPrefPath(Osreg_company_name, Osreg_app_name);
1150
1151                 if (u_path) {
1152                         wxFileName ini_name(u_path, PROFILE_NAME);
1153                         wxTextFile fs_ini( ini_name.GetFullPath() );
1154
1155                         if ( fs_ini.Open() ) {
1156                                 wxString line;
1157
1158                                 for (line = fs_ini.GetLastLine(); fs_ini.GetCurrentLine() > 0; line = fs_ini.GetPrevLine()) {
1159                                         wxArrayString chk = wxSplit(line, '=');
1160
1161                                         if (chk.GetCount() == 2) {
1162                                                 if (chk.Item(0) == "ForcePort") {
1163                                                         int port = wxAtoi( chk.Item(1) );
1164
1165                                                         if (port > 0) {
1166                                                                 fsport = port;
1167                                                         }
1168
1169                                                         break;
1170                                                 }
1171                                         }
1172                                 }
1173
1174                                 fs_ini.Close();
1175                         }
1176
1177                         SDL_free(u_path);
1178                 }
1179         }
1180
1181         // test if socket is in use (in case exising instance is running on same port)
1182         wxDatagramSocket *sock;
1183         wxIPV4address addr;
1184
1185         addr.AnyAddress();
1186         addr.Service( (unsigned short)fsport );
1187
1188         sock = new wxDatagramSocket(addr);
1189
1190         bool isok = sock->IsOk();
1191
1192         sock->Destroy();
1193
1194         if ( !isok ) {
1195                 throw "Unable to start FreeSpace\n\nAn instance is already running";
1196         }
1197
1198         // start game executable
1199         fspid = wxExecute(epath, wxEXEC_ASYNC | wxEXEC_MAKE_GROUP_LEADER | wxEXEC_HIDE_CONSOLE);
1200
1201         return (fspid > 0);
1202 }
1203
1204 void Standalone::ResetAll()
1205 {
1206         m_popup->Show(false);
1207
1208         m_S_ServerName->Clear();
1209         m_S_HostPass->Clear();
1210         m_S_NumConn->SetLabel( wxT("0") );
1211         m_S_Connections->Clear();
1212
1213         m_M_sliderFPS->SetValue(30);
1214         m_M_FPS->SetLabel("30");
1215         m_M_FPSRel->SetLabel( wxT("0.0") );
1216         m_M_MissionName->SetLabel("");
1217         m_M_MissionTime->SetLabel("");
1218         m_M_ngMaxPlayers->SetLabel("");
1219         m_M_ngMaxObservers->SetLabel("");
1220         m_M_ngSecurity->SetLabel("");
1221         m_M_ngRespawns->SetLabel("");
1222
1223         for (int idx = 0; idx < 3; idx++) {
1224                 m_M_Goals->DeleteChildren( m_M_GoalItems[idx] );
1225         }
1226
1227         m_P_Players->Clear();
1228         m_P_ShipType->SetLabel("");
1229         m_P_AvgPing->SetLabel("");
1230         m_P_atsPriShots->SetLabel("");
1231         m_P_atsPriHits->SetLabel("");
1232         m_P_atsPriBHHits->SetLabel("");
1233         m_P_atsPriHitPer->SetLabel("");
1234         m_P_atsPriBHHitPer->SetLabel("");
1235         m_P_atsSecShots->SetLabel("");
1236         m_P_atsSecHits->SetLabel("");
1237         m_P_atsSecBHHits->SetLabel("");
1238         m_P_atsSecHitPer->SetLabel("");
1239         m_P_atsSecBHHitPer->SetLabel("");
1240         m_P_atsAssists->SetLabel("");
1241         m_P_msPriShots->SetLabel("");
1242         m_P_msPriHits->SetLabel("");
1243         m_P_msPriBHHits->SetLabel("");
1244         m_P_msPriHitPer->SetLabel("");
1245         m_P_msPriBHHitPer->SetLabel("");
1246         m_P_msSecShots->SetLabel("");
1247         m_P_msSecHits->SetLabel("");
1248         m_P_msSecBHHits->SetLabel("");
1249         m_P_msSecHitPer->SetLabel("");
1250         m_P_msSecBHHitPer->SetLabel("");
1251         m_P_msAssists->SetLabel("");
1252
1253         m_GS_Players->Clear();
1254         m_GS_msg->Clear();
1255         m_GS_Messages->Clear();
1256
1257         m_D_State->SetLabel("");
1258 }
1259
1260 static int callback_standalone_client(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
1261 {
1262         #define MAX_BUF_SIZE    1050
1263         unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_BUF_SIZE + LWS_SEND_BUFFER_POST_PADDING];
1264         unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
1265         int rval;
1266         int size;
1267
1268         switch (reason) {
1269                 case LWS_CALLBACK_CLIENT_ESTABLISHED:
1270                         lws_callback_on_writable(wsi);
1271                         break;
1272
1273                 case LWS_CALLBACK_CLOSED:
1274                         wxGetApp().Client().wsDisconnect();
1275                         break;
1276
1277                 case LWS_CALLBACK_CLIENT_RECEIVE:
1278                         wxGetApp().Client().wsMessage( (const char *)in, len );
1279                         break;
1280
1281                 case LWS_CALLBACK_CLIENT_WRITEABLE: {
1282                         while ( !wxGetApp().Client().wsGetSendBuffer().empty() ) {
1283                                 std::string msg = wxGetApp().Client().wsGetSendBuffer().front();
1284
1285                                 size = wxStrlcpy((char *)p, msg.c_str(), MAX_BUF_SIZE);
1286
1287                                 rval = lws_write(wsi, p, size, LWS_WRITE_TEXT);
1288
1289                                 if (rval < size) {
1290                                         lwsl_err("ERROR sending buffer!\n");
1291                                         return -1;
1292                                 }
1293
1294                                 wxGetApp().Client().wsGetSendBuffer().pop_front();
1295
1296                                 if ( lws_send_pipe_choked(wsi) ) {
1297                                         lws_callback_on_writable(wsi);
1298
1299                                         break;
1300                                 }
1301                         }
1302
1303                         break;
1304                 }
1305
1306                 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
1307                         wxGetApp().Client().wsDisconnect();
1308                         break;
1309
1310                 default:
1311                         break;
1312         }
1313
1314         return 0;
1315 }
1316
1317 static struct lws_protocols protocols[] = {
1318         {
1319                 "standalone",
1320                 callback_standalone_client,
1321                 0,
1322                 0
1323         },
1324         { NULL, NULL, 0, 0 }
1325 };
1326
1327 bool Standalone::wsInitialize()
1328 {
1329         struct lws_context_creation_info info;
1330
1331         memset(&info, 0, sizeof(info));
1332         memset(&ccinfo, 0, sizeof(ccinfo));
1333
1334         info.port = CONTEXT_PORT_NO_LISTEN;
1335         info.protocols = protocols;
1336         info.gid = -1;
1337         info.uid = -1;
1338
1339         stand_context = lws_create_context(&info);
1340
1341         if (stand_context == NULL) {
1342                 return false;
1343         }
1344
1345         ccinfo.context = stand_context;
1346         ccinfo.address = "127.0.0.1";
1347         ccinfo.port = fsport;
1348         ccinfo.path = "/";
1349         ccinfo.host = ccinfo.address;
1350         ccinfo.origin = ccinfo.address;
1351         ccinfo.ietf_version_or_minus_one = -1;
1352         ccinfo.protocol = protocols[0].name;
1353
1354         // async call, actual connection to occur later
1355         wsi_standalone = lws_client_connect_via_info(&ccinfo);
1356
1357         lws_service(stand_context, 0);
1358
1359         return true;
1360 }
1361
1362 void Standalone::wsDoFrame()
1363 {
1364         if ( !stand_context ) {
1365                 return;
1366         }
1367
1368         // if connection is lost attempt reconnect every 2 seconds
1369         if ( !wsi_standalone && !(++m_rate_limit % 60) ) {
1370                 // restart game process if it terminated
1371                 if ( !wxProcess::Exists(fspid) ) {
1372                         wxCmdLineArgsArray empty;
1373
1374                         try {
1375                                 startFreeSpace(0, empty);
1376                         } catch (const char *err) {
1377                                 wxMessageBox(err, "Error!", wxOK|wxICON_ERROR|wxCENTRE|wxSTAY_ON_TOP);
1378
1379                                 Shutdown();
1380                         }
1381
1382                         wxMilliSleep(500);
1383                 }
1384
1385                 wsi_standalone = lws_client_connect_via_info(&ccinfo);
1386                 m_rate_limit = 0;
1387         }
1388
1389         lws_service(stand_context, 0);
1390 }
1391
1392 void Standalone::wsDisconnect()
1393 {
1394         wsi_standalone = NULL;
1395 }
1396
1397 void Standalone::wsMessage(const char *msg, size_t len)
1398 {
1399         if (msg == NULL || len < 5) {
1400                 return;
1401         }
1402
1403         if ( !wxStrcmp(msg, "reset") ) {
1404                 ResetAll();
1405                 return;
1406         }
1407
1408         if ( !wxStrncmp(msg, "popup ", 6) ) {
1409                 if (len == 6) {
1410                         m_popup->Show(false);
1411                 } else {
1412                         wxArrayString popmsg = wxSplit(msg+6, ';');
1413
1414                         m_popup->SetTitle( popmsg.Item(0) );
1415                         m_popup->SetLabel1( popmsg.Item(1) );
1416                         m_popup->SetLabel2( popmsg.Item(2) );
1417
1418                         m_popup->Layout(); // layout required to deal with label size changes
1419                         m_popup->CenterOnParent();
1420                         m_popup->Show(true);
1421                 }
1422
1423                 return;
1424         }
1425
1426         char mtype = msg[0];
1427
1428         if (mtype == 'T') {
1429                 SetTitle(msg+2);
1430         } else if (mtype == 'D') {
1431                 m_D_State->SetLabel(msg+2);
1432         }
1433         // server tab
1434         else if (mtype == 'S') {
1435                 wxString cmd(msg+2, 4);
1436
1437                 if (cmd == "name") {
1438                         m_S_ServerName->SetValue(msg+7);
1439                 } else if (cmd == "pass") {
1440                         m_S_HostPass->SetValue(msg+7);
1441                 } else if (cmd == "conn") {
1442                         wxArrayString conns = wxSplit(msg+7, ';');
1443
1444                         size_t n_conn = conns.size();
1445
1446                         m_S_NumConn->SetLabel( wxString::Format("%u", (unsigned int)n_conn) );
1447
1448                         m_S_Connections->Clear();
1449
1450                         for (size_t idx = 0; idx < n_conn; idx++) {
1451                                 wxArrayString m_conn = wxSplit(conns.Item(idx), ',');
1452
1453                                 m_P_Players->Append( m_conn.Item(0) );
1454                                 m_GS_Players->Append( m_conn.Item(0) );
1455
1456                                 m_S_Connections->AppendText( m_conn.Item(1) + wxT(", \n") );
1457                         }
1458                 } else if (cmd == "ping") {
1459                         wxArrayString ping_list = wxSplit(msg+7, ',');
1460
1461                         size_t n_pings = ping_list.size();
1462
1463                         size_t offset_pos = 0;
1464                         long col = 0, row = 0;
1465
1466                         m_S_Connections->PositionToXY(m_S_Connections->GetInsertionPoint(), &col, &row);
1467
1468                         for (size_t idx = 0; idx < n_pings; idx++) {
1469                                 size_t from_pos = m_S_Connections->GetValue().find(", ", offset_pos);
1470
1471                                 if (from_pos == wxString::npos) {
1472                                         break;
1473                                 } else {
1474                                         from_pos += 2;
1475                                 }
1476
1477                                 size_t to_pos = m_S_Connections->GetValue().find_first_of("\n", from_pos);
1478
1479                                 m_S_Connections->Replace(from_pos, to_pos, ping_list.Item(idx));
1480
1481                                 offset_pos = to_pos;
1482                         }
1483
1484                         // move intersion point to start of original line (avoids accidental kick)
1485                         m_S_Connections->SetInsertionPoint( m_S_Connections->XYToPosition(0, row) );
1486                 }
1487         }
1488         // multi-player tab
1489         else if (mtype == 'M') {
1490                 wxString cmd(msg+2, 4);
1491
1492                 if (cmd == "name") {
1493                         m_M_MissionName->SetLabel(msg+7);
1494                 } else if (cmd == "time") {
1495                         m_M_MissionTime->SetLabel(msg+7);
1496                 } else if (cmd == "info") {
1497                         wxArrayString ng_info = wxSplit(msg+7, ',');
1498
1499                         m_M_ngMaxPlayers->SetLabel( ng_info.Item(0) );
1500                         m_M_ngMaxObservers->SetLabel( ng_info.Item(1) );
1501                         m_M_ngSecurity->SetLabel( ng_info.Item(2) );
1502                         m_M_ngRespawns->SetLabel( ng_info.Item(3) );
1503                 } else if (cmd == "rfps") {
1504                         m_M_FPSRel->SetLabel(msg+7);
1505                 } else if (cmd == "goal") {
1506                         wxArrayString objectives = wxSplit(msg+7, ';');
1507
1508                         size_t n_objectives = objectives.size();
1509                         wxASSERT(n_objectives == 3);
1510
1511                         for (size_t idx = 0; idx < n_objectives; idx++) {
1512                                 wxArrayString goals = wxSplit( objectives.Item(idx), ',' );
1513
1514                                 size_t n_goals = goals.size();
1515
1516                                 m_M_Goals->DeleteChildren(m_M_GoalItems[idx]);
1517
1518                                 for (size_t j = 0; j < n_goals; j++) {
1519                                         char status = goals.Item(j).GetChar(0);
1520                                         wxString goal = goals.Item(j).substr(2);
1521                                         int img = 1;
1522
1523                                         switch (status) {
1524                                                 case 'i': {
1525                                                         if (goal == "none") {
1526                                                                 img = 1;
1527                                                         } else {
1528                                                                 img = 2;
1529                                                         }
1530
1531                                                         break;
1532                                                 }
1533
1534                                                 case 'c':
1535                                                         img = 3;
1536                                                         break;
1537
1538                                                 case 'f':
1539                                                         img = 4;
1540                                                         break;
1541
1542                                                 default:
1543                                                         break;
1544                                         }
1545
1546                                         m_M_Goals->AppendItem(m_M_GoalItems[idx], goal, img);
1547                                 }
1548                         }
1549
1550                         m_M_Goals->ExpandAll();
1551                 }
1552         }
1553         // player tab
1554         else if (mtype == 'P') {
1555                 wxString cmd(msg+2, 4);
1556
1557                 if (cmd == "info") {
1558                         wxArrayString pinfo = wxSplit(msg+7, ';');
1559                         wxArrayString stats;
1560
1561                         m_P_ShipType->SetLabel( pinfo.Item(0) );
1562                         m_P_AvgPing->SetLabel( pinfo.Item(1) );
1563
1564                         stats = wxSplit(pinfo.Item(2), ',');
1565
1566                         m_P_atsPriShots->SetLabel( stats.Item(0) );
1567                         m_P_atsPriHits->SetLabel( stats.Item(1) );
1568                         m_P_atsPriBHHits->SetLabel( stats.Item(2) );
1569                         m_P_atsPriHitPer->SetLabel( stats.Item(3) );
1570                         m_P_atsPriBHHitPer->SetLabel( stats.Item(4) );
1571                         m_P_atsSecShots->SetLabel( stats.Item(5) );
1572                         m_P_atsSecHits->SetLabel( stats.Item(6) );
1573                         m_P_atsSecBHHits->SetLabel( stats.Item(7) );
1574                         m_P_atsSecHitPer->SetLabel( stats.Item(8) );
1575                         m_P_atsSecBHHitPer->SetLabel( stats.Item(9) );
1576                         m_P_atsAssists->SetLabel( stats.Item(10) );
1577
1578                         stats = wxSplit(pinfo.Item(3), ',');
1579
1580                         m_P_msPriShots->SetLabel( stats.Item(0) );
1581                         m_P_msPriHits->SetLabel( stats.Item(1) );
1582                         m_P_msPriBHHits->SetLabel( stats.Item(2) );
1583                         m_P_msPriHitPer->SetLabel( stats.Item(3) );
1584                         m_P_msPriBHHitPer->SetLabel( stats.Item(4) );
1585                         m_P_msSecShots->SetLabel( stats.Item(5) );
1586                         m_P_msSecHits->SetLabel( stats.Item(6) );
1587                         m_P_msSecBHHits->SetLabel( stats.Item(7) );
1588                         m_P_msSecHitPer->SetLabel( stats.Item(8) );
1589                         m_P_msSecBHHitPer->SetLabel( stats.Item(9) );
1590                         m_P_msAssists->SetLabel( stats.Item(10) );
1591                 }
1592         }
1593         // god stuff tab
1594         else if (mtype == 'G') {
1595                 wxString cmd(msg+2, 4);
1596
1597                 if (cmd == "mesg") {
1598                         m_GS_Messages->AppendText(msg+7);
1599                 }
1600         }
1601 }
1602
1603 void Standalone::wsSend(std::string &msg)
1604 {
1605         send_buf.push_back(msg);
1606
1607         if (wsi_standalone) {
1608                 lws_callback_on_writable(wsi_standalone);
1609         }
1610 }