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