/*
hm_gameserver - hearthmod gameserver
Copyright (C) 2016 Filip Pancik
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
struct ht_s **games = NULL;
struct conn_server_s *user_cs;
struct hm_log_s *lg;
struct ev_loop *loop = NULL;
static struct hm_log_s l;
static struct hm_pool_s *pool;
void set_result(const char *k, int r);
static void client_disconnected(void *data, const char *foreign_client_index, const char *hbs_id)
{
struct hs_holder_s *p = data;
struct game_s *g;
if(p == NULL) {
return;
}
struct packet_s *packet = net_game_over(p, p->entity_id == 2 ? 3 : 2);
struct ht_s *opponent = ht_get(async_clients, foreign_client_index, strlen(foreign_client_index));
if(opponent && opponent->n == sizeof(void *)) {
// loser
hm_log(LOG_DEBUG, lg, "Client disconnect callback, opponent exists");
net_send((void *)opponent->s, packet);
set_result(hbs_id, 0);
} else {
// winner
hm_log(LOG_DEBUG, lg, "Client disconnect callback, opponent doesn't exist, cleaning game");
set_result(hbs_id, 1);
g = p->game;
game_free(g);
}
packet_free(packet);
holder_free(p);
}
static void hm_recv(struct conn_client_s *cs, const char *buf, const int len)
{
hs(cs, buf, len);
}
static void server_shutdown()
{
shutdown_server(user_cs);
couchbase_deinit();
ht_free(games, pool);
ht_free(async_clients, pool);
}
int start_server()
{
struct conn_server_s *cs;
entities_init();
entities_init2();
lg = &l;
games = ht_init(pool);
async_clients = ht_init(pool);
cs = malloc(sizeof(*cs));
memset(cs, 0, sizeof(*cs));
cs->loop = loop;
cs->host = "0.0.0.0";
cs->port = "3724";
cs->log = &l;
cs->pool = pool;
cs->recv = hm_recv;
cs->client_dc = client_disconnected;
cs->shutdown = server_shutdown;
connector_server(cs);
user_cs = cs;
return 0;
}
void callback(struct instance_s *data)
{
struct instance_s *i;
if(data == NULL) {
couchbase_deinit();
hm_log(LOG_DEBUG, lg, "{Connector}: all destinations are offline");
return;
}
for(i = data; i != NULL; i = i->next) {
hm_log(LOG_DEBUG, lg, "{Connector}: connstr: [%s] online: %d group: %d bucket index: %d", i->connstr, i->status, i->group, i->index);
}
start_server();
}
static void sigh_terminate(int __attribute__ ((unused)) signo)
{
user_cs->shutdown();
}
static void initsignals()
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = SIG_IGN;
if(sigaction(SIGPIPE, &act, NULL) < 0) {
hm_log(LOG_CRIT, lg, "{Connector}: sigaction sigpipe");
}
/** We don't care if someone stops and starts a child process with kill (1) */
act.sa_flags = SA_NOCLDSTOP;
/* catch INT and TERM signals */
act.sa_flags = 0;
act.sa_handler = sigh_terminate;
if(sigaction(SIGINT, &act, NULL) < 0) {
hm_log(LOG_CRIT, lg, "{Connector}: Unable to register SIGINT signal handler: %s", strerror(errno));
exit(1);
}
if(sigaction(SIGTERM, &act, NULL) < 0) {
hm_log(LOG_CRIT, lg, "{Connector}: Unable to register SIGTERM signal handler: %s", strerror(errno));
exit(1);
}
}
void daemonize () {
if(chdir("/") != 0) {
hm_log(LOG_DEBUG, lg, "Unable change directory to /: %s", strerror(errno));
exit(1);
}
pid_t pid = fork();
if(pid < 0) {
hm_log(LOG_DEBUG, lg, "Unable to daemonize: fork failed: %s", strerror(errno));
exit(1);
}
if(pid != 0) {
hm_log(LOG_DEBUG, lg, "Daemonized as pid %d.", pid);
exit(0);
}
fclose(stdin);
fclose(stdout);
fclose(stderr);
#define NULL_DEV "/dev/null"
stdin = fopen(NULL_DEV, "r");
if(stdin == NULL) {
hm_log(LOG_EMERG, lg, "Unable to reopen stdin to %s: %s", NULL_DEV, strerror(errno));
exit(1);
}
stdout = fopen(NULL_DEV, "w");
if(stdout == NULL) {
hm_log(LOG_EMERG, lg, "Unable to reopen stdout to %s: %s", NULL_DEV, strerror(errno));
exit(1);
}
stderr = fopen(NULL_DEV, "w");
if(stderr == NULL) {
hm_log(LOG_EMERG, lg, "Unable to reopen stderr to %s: %s", NULL_DEV, strerror(errno));
exit(1);
}
pid_t s = setsid();
if(s < 0) {
hm_log(LOG_EMERG, lg, "Unable to create new session, setsid(2) failed: %s :: %d", strerror(errno), s);
exit(1);
}
hm_log(LOG_DEBUG, lg, "Successfully daemonized as pid %d.", getpid());
}
int main(int argc, char **argv)
{
struct couchbase_data_s cd;
const char *hosts[1] = { "localhost" };
const int groups[1] = { 0 }; // Must start with 0
const char *buckets[1] = { "hbs" };
const char *passwd[1] = { "aci" };
int i, daemon = 0;
memset(&l, 0, sizeof(l));
for(i = 1; i < argc; i++) {
if(strlen(argv[i]) > 6 && memcmp(argv[i], "--log=", 6) == 0) {
daemon = 1;
hm_log_open(&l, argv[i] + 6, LOG_DEBUG);
}
}
if(daemon == 0) {
hm_log_open(&l, NULL, LOG_DEBUG);
}
lg = &l;
loop = ev_default_loop(0);
pool = hm_create_pool(&l);
hm_log(LOG_DEBUG, lg, "\nhm_gameserver Copyright (C) 2016 Filip Pancik\n\
This program comes with ABSOLUTELY NO WARRANTY.\n\
This is free software, and you are welcome to redistribute it\n\
under certain conditions.");
initsignals();
if(daemon == 1) {
daemonize();
}
cd.loop = loop;
cd.log = &l;
cd.pool = pool;
cd.callback = callback;
cd.hosts = hosts;
cd.hgroups = groups;
cd.nhosts = 1;
cd.buckets = buckets;
cd.bpasswd = passwd;
cd.nbuckets = 1;
couchbase_init(&cd);
ev_run(loop, 0);
hm_destroy_pool(pool);
hm_log_close(&l);
ev_default_destroy();
return 0;
}