2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
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
13 #include "stand_server.h"
15 #ifndef __EMSCRIPTEN__
17 #include "osregistry.h"
18 #include "multi_options.h"
19 #include "gamesequence.h"
22 #include "multi_pmsg.h"
23 #include "multi_endgame.h"
24 #include "multimsgs.h"
25 #include "multiutil.h"
26 #include "freespace.h"
27 #include "missiongoals.h"
29 #include "multi_kick.h"
30 #include "multi_fstracker.h"
31 #include "osregistry.h"
32 #include "standalone_html.h"
34 #include <libwebsockets.h>
42 std::string debug_txt;
43 std::string popup_title;
44 std::string popup_field1;
45 std::string popup_field2;
47 std::string active_player;
50 static std_state Standalone_state;
52 static std::list<std::string> Standalone_send_buf;
55 #define STANDALONE_MAX_BAN 50
56 static std::vector<std::string> Standalone_ban_list;
58 #define STD_STATS_UPDATE_TIME 500 // ms between updating player stats
59 #define STD_NG_UPDATE_TIME 1500 // ms between updating netgame information
60 #define STD_PING_UPDATE_TIME 1000 // ms between updating pings
61 #define STD_FPS_UPDATE_TIME 250 // ms between fps updates
63 static int Standalone_stats_stamp = -1;
64 static int Standalone_ng_stamp = -1;
65 static int Standalone_ping_stamp = -1;
66 static int Standalone_fps_stamp = -1;
68 static lws_context *stand_context = NULL;
70 static int startup_reset_stamp;
71 static struct lws *active_wsi = NULL;
74 static int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
76 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + standalone_html_len + LWS_SEND_BUFFER_POST_PADDING];
77 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
78 unsigned char *start = p;
79 unsigned char *end = p + standalone_html_len;
80 bool try_reuse = false;
83 static unsigned int sent = 0;
86 case LWS_CALLBACK_HTTP: {
88 lws_return_http_status(wsi, HTTP_STATUS_BAD_REQUEST, NULL);
94 // no favicon so return 404
95 if ( in && !SDL_strcmp((const char *)in, "/favicon.ico") ) {
96 lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL);
102 // any other request will get our basic html ...
105 FILE *html = fopen("./standalone.html", "rb");
110 rval = lws_serve_http_file(wsi, "./standalone.html", "text/html", NULL, 0);
112 if ( (rval < 0) || ((rval > 0) && lws_http_transaction_completed(wsi)) ) {
113 // error or can't reuse connection, close the socket
119 if ( lws_add_http_header_status(wsi, 200, &p, end) ) {
123 if ( lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, (unsigned char *)"libwebsockets", 13, &p, end) ) {
127 if ( lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/html", 9, &p, end) ) {
131 if ( lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING, (unsigned char *)"gzip", 4, &p, end) ) {
135 if ( lws_add_http_header_content_length(wsi, standalone_html_len, &p, end) ) {
139 if ( lws_finalize_http_header(wsi, &p, end) ) {
143 rval = lws_write(wsi, start, p - start, LWS_WRITE_HTTP_HEADERS);
145 if (rval != (p - start)) {
151 lws_callback_on_writable(wsi);
157 case LWS_CALLBACK_HTTP_BODY_COMPLETION: {
158 lws_return_http_status(wsi, HTTP_STATUS_OK, NULL);
164 case LWS_CALLBACK_HTTP_FILE_COMPLETION: {
170 case LWS_CALLBACK_HTTP_WRITEABLE: {
171 while ( !lws_send_pipe_choked(wsi) && (sent < standalone_html_len) ) {
172 size = standalone_html_len - sent;
174 int pwa = lws_get_peer_write_allowance(wsi);
177 lws_callback_on_writable(wsi);
182 if ( (pwa != -1) && (pwa < size) ) {
186 memcpy(p, standalone_html + sent, size);
188 rval = lws_write(wsi, p, size, LWS_WRITE_HTTP);
195 // while still active, extent timeout
196 lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5);
212 if (lws_http_transaction_completed(wsi)) {
220 static int callback_standalone(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
222 #define MAX_BUF_SIZE 1024
223 unsigned char buf[LWS_SEND_BUFFER_PRE_PADDING + MAX_BUF_SIZE + LWS_SEND_BUFFER_POST_PADDING];
224 unsigned char *p = &buf[LWS_SEND_BUFFER_PRE_PADDING];
229 case LWS_CALLBACK_ESTABLISHED: {
232 if ( timestamp_elapsed(startup_reset_stamp) ) {
233 std_reset_standalone_gui();
239 case LWS_CALLBACK_CLOSED: {
245 case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION: {
253 case LWS_CALLBACK_SERVER_WRITEABLE: {
254 while ( !Standalone_send_buf.empty() ) {
255 size = SDL_strlcpy((char *)p, Standalone_send_buf.front().c_str(), MAX_BUF_SIZE);
257 rval = lws_write(wsi, p, size, LWS_WRITE_TEXT);
260 lwsl_err("ERROR sending buffer!\n");
261 lws_close_reason(wsi, LWS_CLOSE_STATUS_UNEXPECTED_CONDITION, (unsigned char *)"write error", 11);
266 Standalone_send_buf.pop_front();
268 if ( lws_send_pipe_choked(wsi) ) {
269 lws_callback_on_writable(wsi);
278 case LWS_CALLBACK_RECEIVE: {
279 if (in != NULL && len > 0) {
280 const char *msg = (const char *)in;
283 if ( !SDL_strcmp(msg, "shutdown") ) {
284 gameseq_post_event(GS_EVENT_QUIT_GAME);
285 lws_close_reason(wsi, LWS_CLOSE_STATUS_GOINGAWAY, (unsigned char *)"shutdown", 8);
290 if ( !SDL_strcmp(msg, "reset") ) {
291 multi_quit_game(PROMPT_NONE);
292 std_reset_standalone_gui();
300 if ( !SDL_strncmp(msg+2, "name ", 5) ) {
301 SDL_strlcpy(Netgame.name, msg+7, SDL_arraysize(Netgame.name));
302 SDL_strlcpy(Multi_options_g.std_pname, Netgame.name, SDL_arraysize(Multi_options_g.std_pname));
303 } else if ( !SDL_strncmp(msg+2, "pass ", 5) ) {
304 SDL_strlcpy(Multi_options_g.std_passwd, msg+7, SDL_arraysize(Multi_options_g.std_passwd));
305 } else if ( !SDL_strncmp(msg+2, "kick ", 5) ) {
308 for (int i = 0; i < MAX_PLAYERS; i++) {
309 if ( MULTI_CONNECTED(Net_players[i]) ) {
310 psnet_addr_to_string(ip_string, SDL_arraysize(ip_string), &Net_players[i].p_info.addr);
312 if ( !SDL_strcmp(msg+7, ip_string) ) {
313 multi_kick_player(i, 0);
323 else if (mtype == 'M') {
325 if ( !SDL_strncmp(msg+2, "fps ", 4) ) {
326 int fps = SDL_atoi(msg+6);
329 Multi_options_g.std_framecap = fps;
334 else if (mtype == 'P') {
336 if ( !SDL_strncmp(msg+2, "info ", 5) ) {
339 for (i = 0; i < MAX_PLAYERS; i++) {
340 net_player *np = &Net_players[i];
342 if ( MULTI_CONNECTED((*np)) && (Net_player != np) ) {
343 if ( !SDL_strcmp(msg+7, np->player->callsign) ) {
344 Standalone_state.active_player = msg+7;
345 std_pinfo_display_player_info(np);
352 if (i == MAX_PLAYERS) {
353 Standalone_state.active_player.clear();
359 else if (mtype == 'G') {
361 if ( !SDL_strncmp(msg+2, "smsg ", 5) ) {
364 SDL_strlcpy(txt, msg+7, SDL_arraysize(txt));
366 if (SDL_strlen(txt) > 0) {
367 send_game_chat_packet(Net_player, txt, MULTI_MSG_ALL, NULL);
369 std_add_chat_text(txt, MY_NET_PLAYER_NUM, 1);
371 } else if ( !SDL_strcmp(msg+2, "mrefresh") ) {
372 if (MULTI_IS_TRACKER_GAME) {
373 cf_delete(MULTI_VALID_MISSION_FILE, CF_TYPE_DATA);
375 multi_update_valid_missions();
392 static struct lws_protocols stand_protocols[] = {
414 static void std_lws_logger(int level, const char *line)
416 if (level & (LLL_WARN|LLL_ERR)) {
417 mprintf(("STD: %s", line));
418 } else if (level & LLL_NOTICE) {
419 nprintf(("lws", "STD: %s", line));
425 static void std_add_ws_message(const char *id, const char *val)
429 // if no client, and startup stamp elapsed, then don't add more messages
430 if ( (active_wsi == NULL) && timestamp_elapsed(startup_reset_stamp) ) {
440 Standalone_send_buf.push_back(msg);
443 void std_deinit_standalone()
446 lws_cancel_service(stand_context);
447 lws_context_destroy(stand_context);
448 stand_context = NULL;
452 void std_init_standalone()
454 struct lws_context_creation_info info;
462 // basic security measure for admin interface
463 // "1" bind to loopback iface only *default*
464 // "0" bind to any/all
465 // any other value should be ip or iface name to bind
466 // (invalid values will trigger error)
468 const char *stand_iface = os_config_read_string("Network", "RestrictStandAdmin", "1");
470 if ( !SDL_strcmp(stand_iface, "1") ) {
471 info.iface = "127.0.0.1";
472 } else if ( !SDL_strcmp(stand_iface, "0") ) {
475 info.iface = stand_iface;
478 info.port = Multi_options_g.port;
480 info.protocols = stand_protocols;
485 lws_set_log_level(LLL_ERR|LLL_WARN|LLL_NOTICE, std_lws_logger);
487 stand_context = lws_create_context(&info);
489 if (stand_context == NULL) {
490 Error(LOCATION, "Unable to initialize standalone server!");
493 atexit(std_deinit_standalone);
495 // turn off all sound and music
496 Cmdline_freespace_no_sound = 1;
497 Cmdline_freespace_no_music = 1;
500 SDL_snprintf(title, SDL_arraysize(title), "%s %d.%02d.%02d", XSTR("FreeSpace Standalone", 935), FS_VERSION_MAJOR, FS_VERSION_MINOR, FS_VERSION_BUILD);
501 Standalone_state.title = title;
503 // connections > 5 sec after startup should get gui reset
504 startup_reset_stamp = timestamp(5000);
506 std_reset_standalone_gui();
508 std_multi_update_netgame_info_controls();
511 static void std_update_ping_all()
513 std::string ping_upd;
516 for (int i = 0, idx = 0; i < MAX_PLAYERS; i++) {
517 net_player *np = &Net_players[i];
519 if ( MULTI_CONNECTED((*np)) && (Net_player != np) ) {
520 if (np->s_info.ping.ping_avg > -1) {
521 if (np->s_info.ping.ping_avg >= 1000) {
522 SDL_snprintf(ping_str, SDL_arraysize(ping_str), "%s", XSTR("> 1 sec", 914));
524 SDL_snprintf(ping_str, SDL_arraysize(ping_str), "%d%s", np->s_info.ping.ping_avg, XSTR(" ms", 915));
530 // append separator if not first
532 ping_upd.append(",");
535 ping_upd.append(ping_str);
539 if ( !ping_upd.empty() ) {
540 std_add_ws_message("S:ping ", ping_upd.c_str());
544 static void std_update_connections()
546 std::string conn_str;
549 conn_str.reserve(1024);
551 for (int i = 0, idx = 0; i < MAX_PLAYERS; i++) {
552 net_player *np = &Net_players[i];
554 if ( MULTI_CONNECTED((*np)) && (Net_player != np) ) {
555 // append seperator if not first
557 conn_str.append(";");
560 conn_str.append(np->player->callsign);
561 conn_str.append(",");
563 psnet_addr_to_string(ip_address, SDL_arraysize(ip_address), &np->p_info.addr);
564 conn_str.append(ip_address);
568 SDL_assert(conn_str.length() < 1024);
570 if ( !conn_str.empty() ) {
571 std_add_ws_message("S:conn ", conn_str.c_str());
575 void std_do_gui_frame()
577 // maybe update selected player stats
578 if ( ((Standalone_stats_stamp == -1) || timestamp_elapsed(Standalone_stats_stamp)) && !Standalone_state.active_player.empty() ) {
579 Standalone_stats_stamp = timestamp(STD_STATS_UPDATE_TIME);
581 for (int i = 0; i < MAX_PLAYERS; i++) {
582 net_player *np = &Net_players[i];
584 if ( MULTI_CONNECTED((*np)) && (Net_player != np) ) {
585 if ( !SDL_strcmp(Standalone_state.active_player.c_str(), np->player->callsign) ) {
586 std_pinfo_display_player_info(np);
594 // maybe update netgame info
595 if ( (Standalone_ng_stamp == -1) || timestamp_elapsed(Standalone_ng_stamp) ) {
596 Standalone_ng_stamp = timestamp(STD_NG_UPDATE_TIME);
598 std_multi_update_netgame_info_controls();
601 // update connection ping times
602 if ( ((Standalone_ping_stamp == -1) || timestamp_elapsed(Standalone_ping_stamp)) ) {
603 Standalone_ping_stamp = timestamp(STD_PING_UPDATE_TIME);
605 std_update_ping_all();
608 if ( !Standalone_send_buf.empty() ) {
609 lws_callback_on_writable_all_protocol(stand_context, &stand_protocols[1]);
612 lws_service(stand_context, 0);
615 void std_debug_set_standalone_state_string(const char *str)
617 Standalone_state.debug_txt = str;
619 std_add_ws_message("D:", str);
622 void std_connect_set_gamename(const char *name)
625 // if a permanent name exists, use that instead of the default
626 if ( SDL_strlen(Multi_options_g.std_pname) ) {
627 SDL_strlcpy(Netgame.name, Multi_options_g.std_pname, SDL_arraysize(Netgame.name));
629 SDL_strlcpy(Netgame.name, XSTR("Standalone Server", 916), SDL_arraysize(Netgame.name));
632 SDL_strlcpy(Netgame.name, name, SDL_arraysize(Netgame.name));
635 std_add_ws_message("S:name ", Netgame.name);
638 int std_connect_set_connect_count()
642 for (int i = 0; i < MAX_PLAYERS; i++) {
643 if (MULTI_CONNECTED(Net_players[i]) && (Net_player != &Net_players[i]) ) {
651 void std_add_player(net_player *p)
653 std_update_connections();
655 // clear active player, client should reset if needed
656 Standalone_state.active_player.clear();
658 // check to see if this guy is the host
659 std_connect_set_host_connect_status();
662 int std_remove_player(net_player *p)
666 std_update_connections();
668 // clear active player, client should reset if needed
669 Standalone_state.active_player.clear();
671 // update the host connect count
672 std_connect_set_host_connect_status();
674 // update the currently connected players
675 count = std_connect_set_connect_count();
678 multi_quit_game(PROMPT_NONE);
685 void std_update_player_ping(net_player *p)
689 void std_pinfo_display_player_info(net_player *p)
697 pinfo.append(Ship_info[p->p_info.ship_class].name);
701 if (p->s_info.ping.ping_avg > 1000) {
702 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%s", XSTR("> 1 sec", 914));
704 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d%s", p->s_info.ping.ping_avg, XSTR(" ms", 915));
707 pinfo.append(sml_ping);
710 scoring_struct *ptr = &p->player->stats;
713 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->p_shots_fired);
714 pinfo.append(sml_ping);
716 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->p_shots_hit);
717 pinfo.append(sml_ping);
719 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->p_bonehead_hits);
720 pinfo.append(sml_ping);
722 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->p_shots_fired ? (int)(100.0f * ((float)ptr->p_shots_hit / (float)ptr->p_shots_fired)) : 0);
723 pinfo.append(sml_ping);
725 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->p_shots_fired ? (int)(100.0f * ((float)ptr->p_bonehead_hits / (float)ptr->p_shots_fired)) : 0);
726 pinfo.append(sml_ping);
728 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->s_shots_fired);
729 pinfo.append(sml_ping);
731 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->s_shots_hit);
732 pinfo.append(sml_ping);
734 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->s_bonehead_hits);
735 pinfo.append(sml_ping);
737 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->s_shots_fired ? (int)(100.0f * ((float)ptr->s_shots_hit / (float)ptr->s_shots_fired)) : 0);
738 pinfo.append(sml_ping);
740 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->s_shots_fired ? (int)(100.0f * ((float)ptr->s_bonehead_hits / (float)ptr->s_shots_fired)) : 0);
741 pinfo.append(sml_ping);
743 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->assists);
744 pinfo.append(sml_ping);
745 pinfo.append(";"); // <- end of block
748 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->mp_shots_fired);
749 pinfo.append(sml_ping);
751 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->mp_shots_hit);
752 pinfo.append(sml_ping);
754 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->mp_bonehead_hits);
755 pinfo.append(sml_ping);
757 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->mp_shots_fired ? (int)(100.0f * ((float)ptr->mp_shots_hit / (float)ptr->mp_shots_fired)) : 0);
758 pinfo.append(sml_ping);
760 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->mp_shots_fired ? (int)(100.0f * ((float)ptr->mp_bonehead_hits / (float)ptr->mp_shots_fired)) : 0);
761 pinfo.append(sml_ping);
763 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->ms_shots_fired);
764 pinfo.append(sml_ping);
766 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->ms_shots_hit);
767 pinfo.append(sml_ping);
769 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->ms_bonehead_hits);
770 pinfo.append(sml_ping);
772 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->ms_shots_fired ? (int)(100.0f * ((float)ptr->ms_shots_hit / (float)ptr->ms_shots_fired)) : 0);
773 pinfo.append(sml_ping);
775 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->ms_shots_fired ? (int)(100.0f * ((float)ptr->ms_bonehead_hits / (float)ptr->ms_shots_fired)) : 0);
776 pinfo.append(sml_ping);
778 SDL_snprintf(sml_ping, SDL_arraysize(sml_ping), "%d", ptr->m_assists);
779 pinfo.append(sml_ping);
781 std_add_ws_message("P:info ", pinfo.c_str());
784 void std_add_chat_text(const char *text, int player_index, int add_id)
789 if ( (player_index < 0) || (player_index >= MAX_PLAYERS) ) {
793 // format the chat text nicely
795 if ( MULTI_STANDALONE(Net_players[player_index]) ) {
796 SDL_snprintf(id, SDL_arraysize(id), XSTR("<SERVER> %s", 924), "");
798 SDL_snprintf(id, SDL_arraysize(id), "%s: ", Net_players[player_index].player->callsign);
807 std_add_ws_message("G:mesg ", msg.c_str());
810 void std_reset_timestamps()
812 // reset the stats update stamp
813 Standalone_stats_stamp = timestamp(STD_STATS_UPDATE_TIME);
815 // reset the netgame controls update timestamp
816 Standalone_ng_stamp = timestamp(STD_NG_UPDATE_TIME);
818 // reset the ping update stamp
819 Standalone_ping_stamp = timestamp(STD_PING_UPDATE_TIME);
821 // reset fps update stamp
822 Standalone_fps_stamp = timestamp(STD_FPS_UPDATE_TIME);
825 void std_add_ban(const char *name)
827 if ( (name == NULL) || !SDL_strlen(name) ) {
831 if (Standalone_ban_list.size() >= STANDALONE_MAX_BAN) {
835 Standalone_ban_list.push_back(name);
838 int std_player_is_banned(const char *name)
840 if ( Standalone_ban_list.empty() ) {
844 for (size_t i = 0; i < Standalone_ban_list.size(); i++) {
845 if ( !SDL_strcasecmp(name, Standalone_ban_list[i].c_str()) ) {
853 int std_is_host_passwd()
855 return (SDL_strlen(Multi_options_g.std_passwd) > 0) ? 1 : 0;
858 void std_multi_set_standalone_mission_name(const char *mission_name)
860 std_add_ws_message("M:name ", mission_name);
863 void std_multi_set_standalone_missiontime(float mission_time)
867 fix m_time = fl2f(mission_time);
869 // format the time string and set the text
870 game_format_time(m_time, timestr, SDL_arraysize(timestr));
871 SDL_snprintf(txt, SDL_arraysize(txt), "%s : %.1f", timestr, mission_time);
873 std_add_ws_message("M:time ", txt);
876 void std_multi_update_netgame_info_controls()
880 SDL_snprintf(nginfo, SDL_arraysize(nginfo), "%d,%d,%d,%d", Netgame.max_players, Netgame.options.max_observers, Netgame.security, Netgame.respawn);
882 std_add_ws_message("M:info ", nginfo);
885 void std_set_standalone_fps(float fps)
887 if ( (Standalone_fps_stamp == -1) || timestamp_elapsed(Standalone_fps_stamp) ) {
888 Standalone_fps_stamp = timestamp(STD_FPS_UPDATE_TIME);
892 SDL_snprintf(rfps, SDL_arraysize(rfps), "%.1f", fps);
894 std_add_ws_message("M:rfps ", rfps);
898 void std_multi_setup_goal_tree()
900 std::string mission_goals;
902 std::string secondary;
906 for (int i = 0; i < Num_goals; i++) {
907 switch (Mission_goals[i].satisfied) {
913 case GOAL_COMPLETE: {
918 case GOAL_INCOMPLETE:
925 switch (Mission_goals[i].type & GOAL_TYPE_MASK) {
927 primary.append(status);
928 primary.append(Mission_goals[i].name);
934 case SECONDARY_GOAL: {
935 secondary.append(status);
936 secondary.append(Mission_goals[i].name);
937 secondary.append(",");
943 bonus.append(status);
944 bonus.append(Mission_goals[i].name);
955 if ( primary.empty() ) {
956 mission_goals.append("i none");
958 mission_goals.append(primary.substr(0, primary.size()-1));
961 mission_goals.append(";");
963 if ( secondary.empty() ) {
964 mission_goals.append("i none");
966 mission_goals.append(secondary.substr(0, secondary.size()-1));
969 mission_goals.append(";");
971 if ( bonus.empty() ) {
972 mission_goals.append("i none");
974 mission_goals.append(bonus.substr(0, bonus.size()-1));
977 std_add_ws_message("M:goal ", mission_goals.c_str());
980 void std_multi_add_goals()
982 std_multi_setup_goal_tree();
985 void std_multi_update_goals()
987 std_multi_setup_goal_tree();
990 void std_reset_standalone_gui()
992 Standalone_send_buf.clear();
994 std_add_ws_message("reset", NULL);
996 std_add_ws_message("T:", Standalone_state.title.c_str());
997 std_add_ws_message("D:", Standalone_state.debug_txt.c_str());
999 std_add_ws_message("S:name ", Netgame.name);
1000 std_add_ws_message("S:pass ", Multi_options_g.std_passwd);
1002 std_update_connections();
1003 std_set_standalone_fps(0.0f);
1004 std_multi_set_standalone_missiontime(0.0f);
1005 std_multi_update_netgame_info_controls();
1007 Standalone_fps_stamp = -1;
1008 Standalone_ng_stamp = -1;
1009 Standalone_ping_stamp = -1;
1010 Standalone_stats_stamp = -1;
1012 Standalone_state.active_player.clear();
1016 void std_create_gen_dialog(const char *title)
1018 Standalone_state.popup_title = title;
1020 Standalone_state.popup_field1 = "";
1021 Standalone_state.popup_field2 = "";
1024 void std_destroy_gen_dialog()
1026 std_add_ws_message("popup ", NULL);
1029 void std_gen_set_text(const char *str, int field_num)
1031 std::string popup_str;
1033 switch (field_num) {
1035 Standalone_state.popup_title = str;
1039 Standalone_state.popup_field1 = str;
1043 Standalone_state.popup_field2 = str;
1050 popup_str.append(Standalone_state.popup_title);
1051 popup_str.append(";");
1052 popup_str.append(Standalone_state.popup_field1);
1053 popup_str.append(";");
1054 popup_str.append(Standalone_state.popup_field2);
1056 std_add_ws_message("popup ", popup_str.c_str());
1058 // force ws write since do_frame() may not happen until popup is done
1059 lws_callback_on_writable_all_protocol(stand_context, &stand_protocols[1]);
1060 lws_service(stand_context, 0);
1063 void std_tracker_notify_login_fail()
1068 void std_tracker_login()
1070 if ( !Multi_options_g.pxo ) {
1074 multi_fs_tracker_init();
1076 if ( !multi_fs_tracker_inited() ) {
1077 std_tracker_notify_login_fail();
1081 multi_fs_tracker_login_freespace();
1084 void std_connect_set_host_connect_status()
1090 void std_init_standalone(){}
1091 void std_do_gui_frame(){}
1092 void std_debug_set_standalone_state_string(const char *){}
1093 void std_connect_set_gamename(const char *){}
1094 int std_connect_set_connect_count(){return 0;}
1095 void std_add_player(net_player *){}
1096 int std_remove_player(net_player *){return 0;}
1097 void std_update_player_ping(net_player *){}
1098 void std_pinfo_display_player_info(net_player *){}
1099 void std_add_chat_text(const char *, int , int ){}
1100 void std_reset_timestamps(){}
1101 void std_add_ban(const char *){}
1102 int std_player_is_banned(const char *){return 0;}
1103 int std_is_host_passwd(){return 0;}
1104 void std_multi_set_standalone_mission_name(const char *){}
1105 void std_multi_set_standalone_missiontime(float mission_time){}
1106 void std_multi_update_netgame_info_controls(){}
1107 void std_set_standalone_fps(float ){}
1108 void std_multi_setup_goal_tree(){}
1109 void std_multi_add_goals(){}
1110 void std_multi_update_goals(){}
1111 void std_reset_standalone_gui(){}
1112 void std_create_gen_dialog(const char *){}
1113 void std_destroy_gen_dialog(){}
1114 void std_gen_set_text(const char *, int ){}
1115 void std_tracker_notify_login_fail(){}
1116 void std_tracker_login(){}
1117 void std_connect_set_host_connect_status(){}