mirror of
https://github.com/ondra-novak/gates_of_skeldal.git
synced 2025-07-20 05:04:53 -04:00
sse client to listen commands (from mapedit)
This commit is contained in:
parent
13f6c05c60
commit
bb5be10adc
10 changed files with 336 additions and 29 deletions
|
@ -39,7 +39,7 @@
|
||||||
#define E_MENU_SELECT 36
|
#define E_MENU_SELECT 36
|
||||||
#define E_CLOSE_MAP 37
|
#define E_CLOSE_MAP 37
|
||||||
#define E_CLOSE_GEN 38
|
#define E_CLOSE_GEN 38
|
||||||
#define E_HACKER 39
|
#define E_EXTERNAL_MSG 40 //external message arrived from sse_listener
|
||||||
|
|
||||||
//side flags
|
//side flags
|
||||||
|
|
||||||
|
|
|
@ -280,6 +280,10 @@ INIS sinit[]=
|
||||||
int last_ms_cursor=-1;
|
int last_ms_cursor=-1;
|
||||||
int vmode=2;
|
int vmode=2;
|
||||||
|
|
||||||
|
#include <platform/sse_receiver.h>
|
||||||
|
|
||||||
|
static SSE_RECEIVER *sse_receiver = NULL;
|
||||||
|
static MTQUEUE *mtqueue = NULL;
|
||||||
|
|
||||||
void purge_temps(char _) {
|
void purge_temps(char _) {
|
||||||
temp_storage_clear();
|
temp_storage_clear();
|
||||||
|
@ -741,7 +745,8 @@ void done_skeldal(void)
|
||||||
cur_config = NULL;
|
cur_config = NULL;
|
||||||
}
|
}
|
||||||
kill_timer();
|
kill_timer();
|
||||||
|
if (sse_receiver) sse_receiver_stop(sse_receiver);
|
||||||
|
if (mtqueue) mtqueue_destroy(mtqueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -980,6 +985,38 @@ void show_loading_picture(char *filename)
|
||||||
ablock_free(p);
|
ablock_free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sse_listener_watch(EVENT_MSG *msg, void **userdata) {
|
||||||
|
if (msg->msg == E_WATCH) {
|
||||||
|
char *s = mtqueue_pop(mtqueue);
|
||||||
|
if (s) {
|
||||||
|
send_message(E_EXTERNAL_MSG, s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void sse_listener_init(const char *hostport) {
|
||||||
|
|
||||||
|
char *host = local_strdup(hostport);
|
||||||
|
char *port = strrchr(host,':');
|
||||||
|
if (port == NULL) {
|
||||||
|
port = local_strdup("80");
|
||||||
|
} else {
|
||||||
|
*port = 0;
|
||||||
|
++port;
|
||||||
|
}
|
||||||
|
|
||||||
|
MTQUEUE *q = mtqueue_create();
|
||||||
|
SSE_RECEIVER *rcv = sse_receiver_install(q, host, port);
|
||||||
|
|
||||||
|
if (rcv == NULL) {
|
||||||
|
mtqueue_destroy(q);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mtqueue = q;
|
||||||
|
sse_receiver = rcv;
|
||||||
|
send_message(E_ADD, E_WATCH, sse_listener_watch);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void init_skeldal(const INI_CONFIG *cfg)
|
void init_skeldal(const INI_CONFIG *cfg)
|
||||||
{
|
{
|
||||||
|
@ -993,6 +1030,7 @@ void init_skeldal(const INI_CONFIG *cfg)
|
||||||
|
|
||||||
steam_init();
|
steam_init();
|
||||||
|
|
||||||
|
|
||||||
char verr = game_display_init(ini_section_open(cfg, "video"), "Skeldal");
|
char verr = game_display_init(ini_section_open(cfg, "video"), "Skeldal");
|
||||||
if (!verr)
|
if (!verr)
|
||||||
{
|
{
|
||||||
|
@ -1120,15 +1158,21 @@ char doNotLoadMapState=0;
|
||||||
static int reload_map_handler(EVENT_MSG *msg,void **usr)
|
static int reload_map_handler(EVENT_MSG *msg,void **usr)
|
||||||
{
|
{
|
||||||
extern char running_battle;
|
extern char running_battle;
|
||||||
if (msg->msg==E_RELOADMAP)
|
if (msg->msg==E_EXTERNAL_MSG)
|
||||||
{
|
{
|
||||||
int i;
|
const char *m = va_arg(msg->data, const char *);
|
||||||
ReloadMapInfo *minfo=va_arg(msg->data, ReloadMapInfo *);
|
char fname[13];
|
||||||
const char *fname=minfo->fname;
|
int sector;
|
||||||
int sektor=minfo->sektor;
|
int i;
|
||||||
strcopy_n(loadlevel.name,fname,sizeof(loadlevel.name));
|
|
||||||
loadlevel.start_pos=sektor;
|
if (sscanf(m, "RELOAD %12s %d", fname, §or) != 2) return 0;
|
||||||
for(i=0;i<POCET_POSTAV;i++)postavy[i].sektor=loadlevel.start_pos;
|
|
||||||
|
strcopy_n(loadlevel.name,fname,sizeof(loadlevel.name));
|
||||||
|
loadlevel.start_pos=sector;
|
||||||
|
for(i=0;i<POCET_POSTAV;i++) {
|
||||||
|
postavy[i].sektor=loadlevel.start_pos;
|
||||||
|
postavy[i].groupnum = 1;
|
||||||
|
}
|
||||||
SEND_LOG("(WIZARD) Load map '%s' %d",loadlevel.name,loadlevel.start_pos);
|
SEND_LOG("(WIZARD) Load map '%s' %d",loadlevel.name,loadlevel.start_pos);
|
||||||
unwire_proc();
|
unwire_proc();
|
||||||
if (battle) konec_kola();
|
if (battle) konec_kola();
|
||||||
|
@ -1751,6 +1795,10 @@ int skeldal_entry_point(const SKELDAL_CONFIG *start_cfg)
|
||||||
enable_achievements(1);
|
enable_achievements(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (start_cfg->sse_hostport) {
|
||||||
|
sse_listener_init(start_cfg->sse_hostport);
|
||||||
|
}
|
||||||
|
|
||||||
start_check();
|
start_check();
|
||||||
purge_temps(1);
|
purge_temps(1);
|
||||||
clrscr();
|
clrscr();
|
||||||
|
|
|
@ -13,6 +13,7 @@ typedef struct {
|
||||||
const char *config_path;
|
const char *config_path;
|
||||||
const char *lang_path;
|
const char *lang_path;
|
||||||
|
|
||||||
|
const char *sse_hostport;
|
||||||
|
|
||||||
} SKELDAL_CONFIG;
|
} SKELDAL_CONFIG;
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ target_sources(skeldal_platform PRIVATE
|
||||||
timer.cpp
|
timer.cpp
|
||||||
getopt.c
|
getopt.c
|
||||||
achievements.cpp
|
achievements.cpp
|
||||||
|
mtqueue.cpp
|
||||||
|
sse_receiver.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(all_libs
|
set(all_libs
|
||||||
|
|
|
@ -19,6 +19,7 @@ void show_help(const char *arg0) {
|
||||||
"-a <adv> path for adventure file (.adv)\n"
|
"-a <adv> path for adventure file (.adv)\n"
|
||||||
"-l <lang> set language (cz|en)\n"
|
"-l <lang> set language (cz|en)\n"
|
||||||
"-s <directory> generate string-tables (for localization) and exit\n"
|
"-s <directory> generate string-tables (for localization) and exit\n"
|
||||||
|
"-L <host:port> connect to host:port to listen commands (mapedit)\n"
|
||||||
"-h this help\n");
|
"-h this help\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
@ -33,13 +34,15 @@ int main(int argc, char **argv) {
|
||||||
std::string adv_config_file;
|
std::string adv_config_file;
|
||||||
std::string gen_stringtable_path;
|
std::string gen_stringtable_path;
|
||||||
std::string lang;
|
std::string lang;
|
||||||
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:")) != -1; ) {
|
std::string sse_hostport;
|
||||||
|
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:L:")) != -1; ) {
|
||||||
switch (optchr) {
|
switch (optchr) {
|
||||||
case 'f': config_name = optarg;break;
|
case 'f': config_name = optarg;break;
|
||||||
case 'a': adv_config_file = optarg;break;
|
case 'a': adv_config_file = optarg;break;
|
||||||
case 'h': show_help(argv[0]);break;
|
case 'h': show_help(argv[0]);break;
|
||||||
case 'l': lang = optarg;break;
|
case 'l': lang = optarg;break;
|
||||||
case 's': gen_stringtable_path = optarg;break;
|
case 's': gen_stringtable_path = optarg;break;
|
||||||
|
case 'L': sse_hostport = optarg;break;
|
||||||
default: show_help_short();
|
default: show_help_short();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -53,6 +56,7 @@ int main(int argc, char **argv) {
|
||||||
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
|
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
|
||||||
cfg.config_path = config_name.c_str();
|
cfg.config_path = config_name.c_str();
|
||||||
cfg.lang_path = lang.empty()?NULL:lang.c_str();
|
cfg.lang_path = lang.empty()?NULL:lang.c_str();
|
||||||
|
cfg.sse_hostport = sse_hostport.c_str();
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (!gen_stringtable_path.empty()) {
|
if (!gen_stringtable_path.empty()) {
|
||||||
|
|
44
platform/mtqueue.cpp
Normal file
44
platform/mtqueue.cpp
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
#include "mtqueue.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
struct StringDeleter {
|
||||||
|
void operator()(char *x) {
|
||||||
|
free(x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<char, StringDeleter> alloc_string(const char *x) {
|
||||||
|
return std::unique_ptr<char, StringDeleter>(strdup(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tag_mtqueue {
|
||||||
|
std::queue<std::unique_ptr<char, StringDeleter> > _q;
|
||||||
|
std::mutex _mx;
|
||||||
|
|
||||||
|
|
||||||
|
} MTQUEUE;
|
||||||
|
|
||||||
|
MTQUEUE *mtqueue_create() {
|
||||||
|
return new MTQUEUE();
|
||||||
|
}
|
||||||
|
void mtqueue_push(MTQUEUE *q, const char *message) {
|
||||||
|
std::lock_guard _(q->_mx);
|
||||||
|
q->_q.push(alloc_string(message));
|
||||||
|
}
|
||||||
|
char *mtqueue_pop(MTQUEUE *q) {
|
||||||
|
std::lock_guard _(q->_mx);
|
||||||
|
if (q->_q.empty()) return NULL;
|
||||||
|
else {
|
||||||
|
char *c = q->_q.front().release();
|
||||||
|
q->_q.pop();
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void mtqueue_destroy(MTQUEUE *q) {
|
||||||
|
delete q;
|
||||||
|
}
|
32
platform/mtqueue.h
Normal file
32
platform/mtqueue.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tag_mtqueue MTQUEUE;
|
||||||
|
///Create multithread queue
|
||||||
|
MTQUEUE *mtqueue_create();
|
||||||
|
///push to queue (string is copied)
|
||||||
|
/**
|
||||||
|
* @param q queue
|
||||||
|
* @param message message (string is copied)
|
||||||
|
*/
|
||||||
|
void mtqueue_push(MTQUEUE *q, const char *message);
|
||||||
|
///pop from the queue
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param q queue
|
||||||
|
* @return NULL, if queue is empty, or string. You have to release
|
||||||
|
* string by calling free() when you finish.
|
||||||
|
*/
|
||||||
|
char *mtqueue_pop(MTQUEUE *q);
|
||||||
|
|
||||||
|
///destroy the queue
|
||||||
|
void mtqueue_destroy(MTQUEUE *q);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
142
platform/sse_receiver.cpp
Normal file
142
platform/sse_receiver.cpp
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
#include "sse_receiver.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <ws2tcpip.h>
|
||||||
|
#pragma comment(lib, "ws2_32.lib")
|
||||||
|
typedef SOCKET sock_t;
|
||||||
|
#define CLOSESOCK closesocket
|
||||||
|
#define sock_init() { WSADATA wsa; WSAStartup(MAKEWORD(2,2), &wsa); }
|
||||||
|
#define sock_cleanup() WSACleanup()
|
||||||
|
#define SHUT_RD SD_RECEIVE
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
typedef int sock_t;
|
||||||
|
#define INVALID_SOCKET -1
|
||||||
|
#define CLOSESOCK close
|
||||||
|
#define sock_init()
|
||||||
|
#define sock_cleanup()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define BUFFER_SIZE 1024
|
||||||
|
|
||||||
|
|
||||||
|
void sse_client_loop(const char *host, const char *port, std::function<void(const char *)> callback, std::stop_token tkn) {
|
||||||
|
sock_init();
|
||||||
|
|
||||||
|
struct addrinfo hints = {}, *res = NULL;
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
if (getaddrinfo(host, port, &hints, &res) != 0) {
|
||||||
|
perror("getaddrinfo");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sock_t sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
||||||
|
if (sock == INVALID_SOCKET) {
|
||||||
|
perror("socket");
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (connect(sock, res->ai_addr, res->ai_addrlen) != 0) {
|
||||||
|
perror("connect");
|
||||||
|
CLOSESOCK(sock);
|
||||||
|
freeaddrinfo(res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
freeaddrinfo(res);
|
||||||
|
|
||||||
|
{
|
||||||
|
std::stop_callback _cb(tkn, [&]{
|
||||||
|
shutdown(sock, SHUT_RD);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send HTTP GET request
|
||||||
|
char req[512];
|
||||||
|
snprintf(req, sizeof(req),
|
||||||
|
"GET /command HTTP/1.1\r\n"
|
||||||
|
"Host: %s\r\n"
|
||||||
|
"Accept: text/event-stream\r\n"
|
||||||
|
"Connection: keep-alive\r\n\r\n", host);
|
||||||
|
send(sock, req, strlen(req), 0);
|
||||||
|
|
||||||
|
// Read response and extract "data: " lines
|
||||||
|
char buffer[BUFFER_SIZE];
|
||||||
|
int buf_len = 0;
|
||||||
|
|
||||||
|
while (!tkn.stop_requested()) {
|
||||||
|
int n = recv(sock, buffer + buf_len, BUFFER_SIZE - buf_len - 1, 0);
|
||||||
|
if (n <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_len += n;
|
||||||
|
buffer[buf_len] = '\0';
|
||||||
|
|
||||||
|
char *line_start = buffer;
|
||||||
|
while (1) {
|
||||||
|
char *newline = strstr(line_start, "\n");
|
||||||
|
if (!newline) break;
|
||||||
|
|
||||||
|
*newline = '\0';
|
||||||
|
|
||||||
|
if (strncmp(line_start, "data: ", 6) == 0) {
|
||||||
|
callback(line_start + 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_start = newline + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move leftover data to start
|
||||||
|
buf_len = strlen(line_start);
|
||||||
|
memmove(buffer, line_start, buf_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
CLOSESOCK(sock);
|
||||||
|
sock_cleanup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct tag_sse_receiver {
|
||||||
|
std::jthread thr;
|
||||||
|
}
|
||||||
|
SSE_RECEIVER;
|
||||||
|
|
||||||
|
SSE_RECEIVER *sse_receiver_install(MTQUEUE *q, const char *host, const char *port) {
|
||||||
|
SSE_RECEIVER *sse = new SSE_RECEIVER;
|
||||||
|
sse->thr = std::jthread([sse, q, host = std::string(host), port = std::string(port)](std::stop_token tkn){
|
||||||
|
while (!tkn.stop_requested()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(2));
|
||||||
|
sse_client_loop(host.c_str(), port.c_str(), [q](const char *msg){
|
||||||
|
mtqueue_push(q, msg);
|
||||||
|
}, tkn);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sse;
|
||||||
|
}
|
||||||
|
void sse_receiver_stop(SSE_RECEIVER *inst) {
|
||||||
|
delete inst;
|
||||||
|
}
|
||||||
|
|
30
platform/sse_receiver.h
Normal file
30
platform/sse_receiver.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mtqueue.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct tag_sse_receiver SSE_RECEIVER;
|
||||||
|
|
||||||
|
///Install sse receiver
|
||||||
|
/**
|
||||||
|
* @param q mtqueue, which receives messages received by the receiver
|
||||||
|
* @param host host
|
||||||
|
* @param port port
|
||||||
|
* @return pointer to instance of receiver
|
||||||
|
*/
|
||||||
|
SSE_RECEIVER *sse_receiver_install(MTQUEUE *q, const char *host, const char *port);
|
||||||
|
|
||||||
|
///Stops the receiver
|
||||||
|
/**
|
||||||
|
* @param inst instance of receiver
|
||||||
|
* @note the associated queue is not destroyed
|
||||||
|
*/
|
||||||
|
void sse_receiver_stop(SSE_RECEIVER *inst);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -22,6 +22,7 @@ void show_help(std::ostream &out, const char *arg0) {
|
||||||
"-a <adv> path for adventure file (.adv)\n"
|
"-a <adv> path for adventure file (.adv)\n"
|
||||||
"-l <lang> set language (cz|en)\n"
|
"-l <lang> set language (cz|en)\n"
|
||||||
"-s <directory> generate string-tables (for localization) and exit\n"
|
"-s <directory> generate string-tables (for localization) and exit\n"
|
||||||
|
"-L <host:port> connect to host:port to listen commands (mapedit)\n"
|
||||||
"-h this help\n";
|
"-h this help\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,14 +36,16 @@ int main(int argc, char **argv) {
|
||||||
std::string adv_config_file;
|
std::string adv_config_file;
|
||||||
std::string gen_stringtable_path;
|
std::string gen_stringtable_path;
|
||||||
std::string lang;
|
std::string lang;
|
||||||
|
std::string sse_hostport;
|
||||||
std::ostringstream console;
|
std::ostringstream console;
|
||||||
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:")) != -1; ) {
|
for (int optchr = -1; (optchr = getopt(argc, argv, "hf:a:s:l:L:")) != -1; ) {
|
||||||
switch (optchr) {
|
switch (optchr) {
|
||||||
case 'f': config_name = optarg;break;
|
case 'f': config_name = optarg;break;
|
||||||
case 'a': adv_config_file = optarg;break;
|
case 'a': adv_config_file = optarg;break;
|
||||||
case 'h': show_help(console, argv[0]);break;
|
case 'h': show_help(console, argv[0]);break;
|
||||||
case 'l': lang = optarg;break;
|
case 'l': lang = optarg;break;
|
||||||
case 's': gen_stringtable_path = optarg;break;
|
case 's': gen_stringtable_path = optarg;break;
|
||||||
|
case 'L': sse_hostport = optarg;break;
|
||||||
default: show_help_short(console);break;
|
default: show_help_short(console);break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +55,7 @@ int main(int argc, char **argv) {
|
||||||
show_help(console, argv[0]);
|
show_help(console, argv[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
SKELDAL_CONFIG cfg;
|
SKELDAL_CONFIG cfg = {};
|
||||||
cfg.short_help = []{};
|
cfg.short_help = []{};
|
||||||
cfg.show_error = [](const char *txt) {
|
cfg.show_error = [](const char *txt) {
|
||||||
char buff[MAX_PATH];
|
char buff[MAX_PATH];
|
||||||
|
@ -63,6 +66,7 @@ int main(int argc, char **argv) {
|
||||||
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
|
cfg.adventure_path = adv_config_file.empty()?NULL:adv_config_file.c_str();
|
||||||
cfg.config_path = config_name.c_str();
|
cfg.config_path = config_name.c_str();
|
||||||
cfg.lang_path = lang.empty()?NULL:lang.c_str();
|
cfg.lang_path = lang.empty()?NULL:lang.c_str();
|
||||||
|
cfg.sse_hostport = sse_hostport.c_str();
|
||||||
|
|
||||||
{
|
{
|
||||||
std::string msg = console.str();
|
std::string msg = console.str();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue