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