]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/standalone.html
handle for connections and ping updates
[taylor/freespace2.git] / src / network / standalone.html
1 <!DOCTYPE html>
2 <html lang="en">
3 <head>
4 <meta charset="utf-8" http-equiv="Content-Language" content="en"/>
5 <title>FreeSpace Standalone</title>
6
7 <script>
8 var standalone_socket = new WebSocket("ws://" + document.URL.substr(7).split('/')[0], "standalone");
9
10 try {
11   standalone_socket.onmessage = function got_packet(msg) {
12     if (msg.data == "reset") {
13       reset_ui();
14     } else if (msg.data.substring(0, 6) == "popup ") {
15       popup(msg.data.substr(6));
16     } else if (msg.data[0] == 'D') {
17       document.getElementById("debug").textContent = msg.data.substr(2);
18     } else if (msg.data[0] == 'T') {
19       document.title = msg.data.substr(2);
20     } else if (msg.data[0] == 'S') {
21       do_server_tab(msg.data.substr(2));
22     } else if (msg.data[0] == 'M') {
23       do_multi_tab(msg.data.substr(2));
24     } else if (msg.data[0] == 'P') {
25       do_player_tab(msg.data.substr(2));
26     } else if (msg.data[0] == 'G') {
27       do_gs_tab(msg.data.substr(2));
28     }
29   };
30 } catch (exception) {
31   alert('<p>Error ' + exception);
32 }
33
34 function shutdown() {
35   standalone_socket.send("shutdown");
36   standalone_socket.close();
37   reset_ui();
38   popup("Shutdown;Shutdown complete;");
39 }
40
41 function popup(msg) {
42   var modal = document.getElementById("popup");
43   var content = [ document.getElementById("modal-title"), document.getElementById("field1"), document.getElementById("field2") ];
44   var x;
45
46   if (msg.length) {
47     var fields = msg.split(';');
48
49     for (x = 0; x < fields.length; x++) {
50       content[x].textContent = fields[x];
51     }
52
53     modal.style.display = "block";
54   } else {
55     modal.style.display = "none";
56   }
57 }
58
59 function reset_ui() {
60   var table;
61   var len;
62   var j;
63
64   /* hide popup */
65   document.getElementById("popup").style.display = "none";
66
67   /* server tab */
68   document.getElementById("n_conn").textContent = "0";
69   table = document.getElementById("connections");
70   len = table.rows.length;
71
72   for (j = 1; j < len; j++) {
73     table.rows[j].id = '';
74     table.rows[j].onclick = '';
75     table.rows[j].cells[0].innerHTML = '&nbsp;';
76     table.rows[j].cells[1].innerHTML = "";
77   }
78
79   /* multi-player tab */
80   document.getElementById("fps_rel").textContent = "0.0";
81   document.getElementById("mission_name").textContent = "";
82   document.getElementById("mission_time").textContent = "";
83
84   document.getElementById("max_players").textContent = "";
85   document.getElementById("max_observers").textContent = "";
86   document.getElementById("security").textContent = "";
87   document.getElementById("respawns").textContent = "";
88
89   table = document.getElementById("mgoals");
90   len = table.rows.length;
91
92   for (j = 1; j < len; j++) {
93     var cell = table.rows[j].cells[0];
94
95     if (cell.childNodes.length > 1) {
96       cell.removeChild(cell.childNodes[1]);
97     }
98   }
99
100   /* player info tab */
101   document.getElementById("players").selectedIndex = -1;
102   document.getElementById("ship_type").textContent = "";
103   document.getElementById("player_ping").textContent = "";
104
105   table = document.getElementById("stats");
106   len = table.rows.length;
107
108   for (j = 1; j < len; j++) {
109     table.rows[j].cells[1].innerHTML = "";
110     table.rows[j].cells[2].innerHTML = "";
111   }
112
113   /* gs tab */
114   document.getElementById("gs_players").selectedIndex = -1;
115   document.getElementById("smsg_box").value = "";
116 }
117
118 function do_server_tab(msg) {
119   var table;
120   var row;
121   var j;
122   var len;
123
124   if (msg.substring(0, 5) == "name ") {
125     document.getElementById("server_name").value = msg.substr(5);
126   } else if (msg.substring(0, 5) == "pass ") {
127     document.getElementById("host_pass").value = msg.substr(5);
128   } else if (msg.substring(0, 5) == "ping ") {
129     table = document.getElementById("connections");
130     var ping_list = msg.substr(5).split(';', 12);
131     len = ping_list.length;
132
133     for (j = 0; j < len; j++) {
134       var ping = ping_list[j].split(',');
135       row = table.rows.namedItem(ping[0]);
136
137       if (Boolean(row)) {
138           row.cells[1].innerHTML = ping[1];
139       }
140     }
141   } else if (msg.substring(0, 5) == "conn ") {
142     table = document.getElementById("connections");
143     var players = document.getElementById("players");
144     var cur_player;
145     var gs_players = document.getElementById("gs_players");
146     var connections = msg.substr(5).split(';', 12);
147     len = connections.length - 1;
148
149     if (players.selectedIndex != -1) {
150       cur_player = players.value;
151     }
152
153     while (players.options.length) {
154       players.remove(0);
155       gs_players.remove(0);
156     }
157
158     for (j = 0; j < len; j++) {
159       var conn = connections[j].split(',');
160       row = table.rows[j+1];
161
162       var option = document.createElement("option");
163       option.text = conn[0];
164       gs_players.add(option);
165
166       option = document.createElement("option");
167       option.text = conn[0];
168       players.add(option);
169
170       row.id = conn[1];
171       row.cells[0].innerHTML = conn[1];
172
173       if (Boolean(conn[2])) {
174         row.cells[1].innerHTML = conn[2];
175       }
176
177       row.onclick = function() { kick_conn(conn[0], conn[1]); };
178     }
179
180     gs_players.selectedIndex = -1;
181     players.value = cur_player;
182     document.getElementById("n_conn").textContent = j;
183
184     len = table.rows.length;
185
186     for (j = j+1; j < len; j++) {
187       row = table.rows[j];
188
189       row.id = '';
190       row.onclick = '';
191       row.cells[0].innerHTML = "&nbsp;"
192       row.cells[1].innetHTML = "";
193     }
194   }
195 }
196
197 function do_multi_tab(msg) {
198   if (msg.substring(0, 4) == "fps ") {
199     document.getElementById("fps_rel").textContent = msg.substr(4);
200   } else if (msg.substring(0, 5) == "name ") {
201     document.getElementById("mission_name").textContent = msg.substr(5);
202   } else if (msg.substring(0, 5) == "time ") {
203     document.getElementById("mission_time").textContent = msg.substr(5);
204   } else if (msg.substring(0, 5) == "info ") {
205     var nginfo = msg.substr(5).split(',');
206     document.getElementById("max_players").textContent = nginfo[0];
207     document.getElementById("max_observers").textContent = nginfo[1];
208     document.getElementById("security").textContent = nginfo[2];
209     document.getElementById("respawns").textContent = nginfo[3];
210   } else if (msg.substring(0, 5) == "goal ") {
211     var objectives = msg.substr(5).split(';');
212     var gtable = document.getElementById("mgoals");
213     var j;
214     var j_len;
215     var x;
216     var x_len;
217
218     j_len = objectives.length;
219
220     for (j = 0; j < j_len; j++) {
221       var goals = objectives[j].split(',');
222       var cell = gtable.rows[j+1].cells[0];
223
224       if (cell.childNodes.length > 1) {
225         cell.removeChild(cell.childNodes[1]);
226       }
227
228       var list = document.createElement("ul");
229  
230       x_len = goals.length;
231
232       for (x = 0; x < x_len; x++) {
233         var item = document.createElement("li");
234         item.innerHTML = goals[x].substr(2);
235
236         var status = goals[x].substring(0, 2);
237         if (status == "c ") {
238           item.style.color = "green";
239         } else if (status == "f ") {
240           item.style.color = "red";
241         } else {
242           item.style.color = "#555";
243         }
244
245         list.appendChild(item);
246       }
247
248       cell.appendChild(list);
249     }
250   }
251 }
252
253 function do_player_tab(msg) {
254   if (msg.substring(0, 5) == "info ") {
255     var pinfo = msg.substr(5).split(';');
256
257     document.getElementById("ship_type").textContent = pinfo[0];
258     document.getElementById("player_ping").textContent = pinfo[1];
259
260     var all_stats = pinfo[2].split(',');
261     var mission_stats = pinfo[3].split(',');
262     var table = document.getElementById("stats");
263     var j;
264     var len = table.rows.length;
265
266     for (j = 1; j < len; j++) {
267       var row = table.rows[j];
268
269       row.cells[1].innerHTML = all_stats[j-1];
270       row.cells[2].innerHTML = mission_stats[j-1];
271     }
272   }
273 }
274
275 function do_gs_tab(msg) {
276   if (msg.substring(0, 5) == "mesg ") {
277     document.getElementById("smsg_box").value += msg.substr(5);
278   }
279 }
280
281 function change_server_name() {
282   standalone_socket.send("S:name " + document.getElementById("server_name").value);
283 }
284
285 function change_host_pass() {
286   standalone_socket.send("S:pass " + document.getElementById("host_pass").value);
287 }
288
289 function kick_conn(name, addr) {
290   var kick = confirm("Kick player? \n\n" + name + "@" + addr);
291
292   if (kick) {
293     standalone_socket.send("S:kick " + addr);
294   }
295 }
296
297 function send_server_message() {
298   var smsg = document.getElementById("server_message");
299   standalone_socket.send("G:smsg " + smsg.value);
300   smsg.value = "";
301 }
302
303 function refresh_missions() {
304   standalone_socket.send("G:mrefresh");
305 }
306
307 function switch_tab(tab) {
308   var tabbox = document.getElementById("tab-box");
309   var topnav = document.getElementById("topnav");
310   var x;
311
312   for (x = 0; x < 4; x++) {
313     if (tab == x) {
314       topnav.children[x].children[0].className = "active";
315       tabbox.children[x].className = "tab-visible";
316     } else {
317       topnav.children[x].children[0].className = '';
318       tabbox.children[x].className = "tab-hidden";
319     }
320   }
321 }
322
323 function smsg_submit(e) {
324   e = e || window.event;
325
326   if (e.keyCode == 13) {
327     document.getElementById("smsg_submit").click();
328     return false;
329   }
330
331   return true;
332 }
333
334 function get_player_info() {
335   var n = document.getElementById("players").selectedIndex;
336
337   if (n != -1) {
338     standalone_socket.send("P:info " + document.getElementById("players").options[n].text);
339   }
340 }
341
342 function set_fps() {
343   var fps = document.getElementById("multi_fps").value;
344   document.getElementById("fps_cap").textContent = fps;
345
346   standalone_socket.send("M:fps " + fps);
347 }
348 </script>
349
350 <style type="text/css">
351 body {
352   font-family: Arial, Helvetica, sans-serif
353 }
354
355 .container {
356   width: 565px;
357   margin: 0 auto
358 }
359
360 .tab-box {
361   background-color: #ddd;
362   padding: 10px 25px
363 }
364
365 .tab-hidden {
366   display: none
367 }
368
369 .tab-visible {
370   display: inline-block;
371   width: 100%
372 }
373
374 .button {
375   border: none;
376   border-radius: 4px;
377   background-color: #444;
378   color: #eee;
379   padding: 5px 10px;
380   text-align: center;
381   text-decoration: none;
382   display: inline-block;
383   font-size: 14px;
384   cursor: pointer
385 }
386
387 .button:hover {
388   background-color: #111;
389   color: #fff
390 }
391
392 ul.topnav {
393   list-style-type: none;
394   margin: 0;
395   padding: 0;
396   overflow: hidden;
397   background-color: #3a3a3a
398 }
399
400 ul.topnav li {
401   float: left
402 }
403
404 ul.topnav a {
405   display: inline-block;
406   color: #e2e2e2;
407   text-align: center;
408   padding: 14px 16px;
409   text-decoration: none;
410   transition: .3s;
411   font-size: 17px;
412   cursor: pointer;
413   outline: 0
414 }
415
416 a:hover,
417 ul.topnav a.active {
418   background-color: #111;
419   color: #fff
420 }
421
422 ul.topnav li.shutdown {
423   float: right;
424   padding-left: 40px
425 }
426
427 ul.topnav li.shutdown a {
428   background-color: #8b0000
429 }
430
431 ul.topnav li.shutdown a:hover {
432   background-color: red;
433   color: #fff
434 }
435
436 .debug_state {
437   font-size: smaller;
438   color: #696969;
439   float: right;
440   padding: 2px 4px
441 }
442
443 #server_name,
444 #host_pass {
445   width: 20em;
446   border: none;
447   padding: 5px 10px
448 }
449
450 #connections,
451 #stats {
452   border: 1px solid #444;
453   border-spacing: 0;
454   border-collapse: collapse;
455   width: 100%;
456   min-height: 13em
457 }
458
459 #connections th,
460 #stats th {
461   background-color: #444;
462   color: #fff;
463   padding: 5px 10px;
464   text-align: left;
465   font-size: small
466 }
467
468 #connections td,
469 #stats td {
470   padding: 5px 15px;
471   text-align: left;
472   font-family: Courier New, Courier, monospace;
473   font-size: small
474 }
475
476 #connections tr:nth-child(odd),
477 #stats tr:nth-child(odd) {
478   background-color: #d5d5d5
479 }
480
481 #connections tr:hover,
482 #connections tr:hover:nth-child(odd) {
483   background-color: #eaeaea
484 }
485
486 table.name_pass td {
487   padding: 5px 0
488 }
489
490 table.name_pass label {
491   padding-right: 10px
492 }
493
494 #server_message {
495   margin-top: 10px;
496   padding: 5px 10px;
497   border: none;
498   width: 100%;
499   box-sizing: border-box
500 }
501
502 .players,
503 .players option {
504   min-width: 20em;
505   padding: 2px 8px;
506   margin-left: 10px
507 }
508
509 #smsg_box {
510   width: 100%;
511   box-sizing: border-box;
512   resize: none;
513   background-color: #fff;
514   border: 1px solid #bbb;
515   border-radius: 2px
516 }
517
518 table.minfo,
519 table.pinfo {
520   margin-top: 30px;
521   margin-bottom: 30px;
522   border: none;
523   border-collapse: collapse
524 }
525
526 table.minfo td,
527 table.pinfo td {
528   padding: 5px 0
529 }
530
531 table.minfo span,
532 table.pinfo span {
533   font-family: Courier New, Courier, monospace;
534   font-size: small;
535   padding: 5px 10px;
536   min-width: 12em;
537   margin-left: 10px
538 }
539
540 .player_sel {
541   margin-top: 10px
542 }
543
544 .fps_block {
545   display: inline-block;
546   width: 100%;
547   margin-bottom: 20px
548 }
549
550 #multi_fps {
551   width: 100%;
552   margin: 10px 0;
553   box-sizing: border-box
554 }
555
556 .fps_cap {
557   display: block;
558   float: left
559 }
560
561 .fps_rel {
562   display: block;
563   float: right
564 }
565
566 .fps_cap span,
567 .fps_rel span {
568   font-family: Courier New, Courier, monospace;
569   padding: 5px 0 5px 10px
570 }
571
572 .mgoals,
573 .nginfo {
574   border: none;
575   border-collapse: collapse;
576   display: inline-block
577 }
578
579 .nginfo {
580   float: right
581 }
582
583 .mgoals {
584   float: left;
585   min-width: 15em;
586   min-height: 17em;
587   background-color: #fff;
588   margin-bottom: 15px
589 }
590
591 .mgoals th,
592 .nginfo th {
593   padding: 5px;
594   text-align: left
595 }
596
597 .nginfo th {
598   border-bottom: 1px solid #444
599 }
600
601 .mgoals th {
602   background-color: #ddd;
603   width: 15em
604 }
605
606 .mgoals td,
607 .nginfo td {
608   padding: 5px 10px;
609   font-size: small
610 }
611
612 .mgoals ul {
613   padding: 0 1.5em;
614   margin: 0
615 }
616
617 .modal {
618   display: none;
619   position: fixed;
620   z-index: 1;
621   left: 0;
622   top: 0;
623   width: 100%;
624   height: 100%;
625   overflow: auto;
626   background-color: #000;
627   background-color: rgba(0, 0, 0, .4)
628 }
629
630 .modal-content {
631   background-color: #fefefe;
632   margin: 10em auto;
633   border: 2px solid #111;
634   width: 25em
635 }
636
637 .modal-title {
638   background-color: #444;
639   color: #fff;
640   padding: 10px;
641   font-weight: 700
642 }
643
644 .modal-body {
645   padding: 20px;
646   margin-top: 2em;
647   height: 4em
648 }
649 </style>
650 </head>
651
652 <body>
653
654 <div class="container">
655   <ul class="topnav" id="topnav">
656     <li><a onclick="switch_tab(0);" href="javascript:void(0);" class="active">Server</a></li>
657     <li><a onclick="switch_tab(1);" href="javascript:void(0);">Multi-Player</a></li>
658     <li><a onclick="switch_tab(2);" href="javascript:void(0);">Player Info</a></li>
659     <li><a onclick="switch_tab(3);" href="javascript:void(0);">God Stuff</a></li>
660     <li class="shutdown"><a onclick="shutdown();" href="javascript:void(0);">Shutdown</a></li>
661   </ul>
662
663   <div class="tab-box" id="tab-box">
664     <div id="server_tab" class="tab-visible">
665       <table class="name_pass">
666         <tr><td><label for="server_name">Server Name </label></td><td><input id="server_name" type="text" value="" maxlength="31" onchange="change_server_name();"></td></tr>
667         <tr><td><label for="host_pass">Host Password </label></td><td><input id="host_pass" type="text" value="" maxlength="15" onchange="change_host_pass();"></td></tr>
668       </table>
669       <hr>
670       <p>Connections : <span id="n_conn">0</span></p>
671       <p>
672         <table id="connections">
673           <tr><th style="min-width:20em;">IP Address</th><th style="min-width:5em;">Ping</th></tr>
674           <tr><td>&nbsp;</td><td></td></tr>
675           <tr><td>&nbsp;</td><td></td></tr>
676           <tr><td>&nbsp;</td><td></td></tr>
677           <tr><td>&nbsp;</td><td></td></tr>
678           <tr><td>&nbsp;</td><td></td></tr>
679           <tr><td>&nbsp;</td><td></td></tr>
680           <tr><td>&nbsp;</td><td></td></tr>
681           <tr><td>&nbsp;</td><td></td></tr>
682           <tr><td>&nbsp;</td><td></td></tr>
683           <tr><td>&nbsp;</td><td></td></tr>
684           <tr><td>&nbsp;</td><td></td></tr>
685           <tr><td>&nbsp;</td><td></td></tr>
686         </table>
687       </p>
688     </div>
689
690     <div id="multi_tab" class="tab-hidden">
691       <div class="fps_block">
692         <input type="range" id="multi_fps" min="15" max="60" value="30" step="1" oninput="set_fps();">
693         <div class="fps_cap">
694           Frame Cap: <span id="fps_cap">30</span>
695         </div>
696         <div class="fps_rel">
697           Realiazed FPS: <span id="fps_rel">0.0</span>
698         </div>
699       </div>
700       <hr>
701       <table class="minfo">
702         <tr><td>Mission Name: </td><td><span id="mission_name"></span></td></tr>
703         <tr><td>Mission Time: </td><td><span id="mission_time"></span></td></tr>
704       </table>
705       <table class="mgoals" id="mgoals">
706         <tr><th>Mission Goals</th></tr>
707         <tr><td>Primary Objectives</td></tr>
708         <tr><td>Secondary Objectives</td></tr>
709         <tr><td>Bonus Objectives</td></tr>
710       </table>
711       <table class="nginfo">
712         <tr><th colspan="2">Netgame Information</th></tr>
713         <tr><td>Max Players</td><td><span id="max_players"></span></td></tr>
714         <tr><td>Max Observers</td><td><span id="max_observers"></span></td></tr>
715         <tr><td>Security</td><td><span id="security"></span></td></tr>
716         <tr><td>Respawns</td><td><span id="respawns"></span></td></tr>
717       </table>
718     </div>
719
720     <div id="pilot_tab" class="tab-hidden">
721       <div class="player_sel">
722         <label for="players">Player</label>
723         <select id="players" class="players" onchange="get_player_info();">
724         </select>
725       </div>
726       <table class="pinfo">
727         <tr><td>Ship Type: </td><td><span id="ship_type"></span></td></tr>
728         <tr><td>Avg Ping: </td><td><span id="player_ping"></span></td></tr>
729       </table>
730       <p>
731         <table id="stats">
732           <tr><th>Stats</th><th>All Time</th><th>Mission</th></tr>
733           <tr><td>Primary Shots</td><td></td><td></td></tr>
734           <tr><td>Primary Hits</td><td></td><td></td></tr>
735           <tr><td>Primary BH Hits</td><td></td><td></td></tr>
736           <tr><td>Primary Hit %</td><td></td><td></td></tr>
737           <tr><td>Primary BH Hit %</td><td></td><td></td></tr>
738           <tr><td>Secondary Shots</td><td></td><td></td></tr>
739           <tr><td>Secondary Hits</td><td></td><td></td></tr>
740           <tr><td>Secondary BH Hits</td><td></td><td></td></tr>
741           <tr><td>Secondary Hit %</td><td></td><td></td></tr>
742           <tr><td>Secondary BH Hit %</td><td></td><td></td></tr>
743           <tr><td>Assists</td><td></td><td></td></tr>
744         </table>
745       </p>
746     </div>
747
748     <div id="godstuff_tab" class="tab-hidden">
749       <div class="player_sel">
750         <label for="gs_players">Player</label>
751         <select id="gs_players" class="players">
752         </select>
753       </div>
754       <div style="margin-top: 30px;">
755           <p>
756           <label for="server_message">Server Message</label>
757           <br>
758           <input id="server_message" type="text" value="" onkeypress="return smsg_submit(event);">
759           <input id="smsg_submit" type="button" onclick="send_server_message();" style="display:none;">
760         </p>
761         <textarea readonly rows="20" id="smsg_box" autocomplete="off"></textarea>
762       </div>
763       <p>
764         <input class="button" type="button" value="Refresh Missions" onclick="refresh_missions();">
765       </p>
766     </div>
767   </div>
768
769   <div id="debug" class="debug_state"></div>
770 </div>
771
772 <div id="popup" class="modal">
773   <div class="modal-content">
774     <div class="modal-title" id="modal-title"></div>
775     <div class="modal-body">
776       <span id="field1"></span>&nbsp;<span id="field2"></span>
777     </div>
778   </div>
779 </div>
780
781 </body>
782 </html>