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