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