27a59a0cbc
vulkan, wasapi, quake injector features added. irc, avplug, cef plugins/drivers reworked/updated/added openal reverb, doppler effects added. 'dir' console command now attempts to view clicked files. lots of warning fixes, should now only be deprecation warnings for most targets (depending on compiler version anyway...). SendEntity finally reworked to use flags properly. effectinfo improved, other smc-targetted fixes. mapcluster stuff now has support for linux. .basebone+.baseframe now exist in ssqc. qcc: -Fqccx supports qccx syntax, including qccx hacks. don't expect these to work in fteqw nor dp though. qcc: rewrote function call handling to use refs rather than defs. this makes struct passing more efficient and makes the __out keyword usable with fields etc. qccgui: can cope a little better with non-unicode files. can now represent most quake chars. qcc: suppressed warnings from *extensions.qc git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5000 fc73d0e0-1445-4013-8a0c-d673dee63da5
479 lines
11 KiB
C
479 lines
11 KiB
C
/*
|
|
Copyright (C) 1996-1997 Id Software, Inc.
|
|
|
|
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 2
|
|
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, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
//well, linux or cygwin (windows with posix emulation layer), anyway...
|
|
|
|
#include "quakedef.h"
|
|
|
|
#ifdef MULTITHREAD
|
|
#include <limits.h>
|
|
#include <pthread.h>
|
|
/* Thread creation calls */
|
|
typedef void *(*pfunction_t)(void *);
|
|
|
|
static pthread_t mainthread;
|
|
|
|
void Sys_ThreadsInit(void)
|
|
{
|
|
mainthread = pthread_self();
|
|
}
|
|
qboolean Sys_IsThread(void *thread)
|
|
{
|
|
return pthread_equal(pthread_self(), *(pthread_t*)thread);
|
|
}
|
|
qboolean Sys_IsMainThread(void)
|
|
{
|
|
return Sys_IsThread(&mainthread);
|
|
}
|
|
void Sys_ThreadAbort(void)
|
|
{
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
void *Sys_CreateThread(char *name, int (*func)(void *), void *args, int priority, int stacksize)
|
|
{
|
|
pthread_t *thread;
|
|
pthread_attr_t attr;
|
|
|
|
thread = (pthread_t *)malloc(sizeof(pthread_t));
|
|
if (!thread)
|
|
return NULL;
|
|
|
|
pthread_attr_init(&attr);
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
|
|
if (stacksize < PTHREAD_STACK_MIN*2)
|
|
stacksize = PTHREAD_STACK_MIN*2;
|
|
pthread_attr_setstacksize(&attr, stacksize);
|
|
if (pthread_create(thread, &attr, (pfunction_t)func, args))
|
|
{
|
|
free(thread);
|
|
thread = NULL;
|
|
}
|
|
pthread_attr_destroy(&attr);
|
|
|
|
return (void *)thread;
|
|
}
|
|
|
|
void Sys_WaitOnThread(void *thread)
|
|
{
|
|
int err;
|
|
err = pthread_join(*(pthread_t *)thread, NULL);
|
|
if (err)
|
|
printf("pthread_join(%p) failed, error %s\n", thread, strerror(err));
|
|
|
|
free(thread);
|
|
}
|
|
|
|
/* Mutex calls */
|
|
void *Sys_CreateMutex(void)
|
|
{
|
|
pthread_mutex_t *mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
|
|
|
if (mutex && !pthread_mutex_init(mutex, NULL))
|
|
return mutex;
|
|
return NULL;
|
|
}
|
|
|
|
qboolean Sys_TryLockMutex(void *mutex)
|
|
{
|
|
return !pthread_mutex_trylock(mutex);
|
|
}
|
|
|
|
qboolean Sys_LockMutex(void *mutex)
|
|
{
|
|
return !pthread_mutex_lock(mutex);
|
|
}
|
|
|
|
qboolean Sys_UnlockMutex(void *mutex)
|
|
{
|
|
return !pthread_mutex_unlock(mutex);
|
|
}
|
|
|
|
void Sys_DestroyMutex(void *mutex)
|
|
{
|
|
pthread_mutex_destroy(mutex);
|
|
free(mutex);
|
|
}
|
|
|
|
/* Conditional wait calls */
|
|
typedef struct condvar_s
|
|
{
|
|
pthread_mutex_t *mutex;
|
|
pthread_cond_t *cond;
|
|
} condvar_t;
|
|
|
|
void *Sys_CreateConditional(void)
|
|
{
|
|
condvar_t *condv;
|
|
pthread_mutex_t *mutex;
|
|
pthread_cond_t *cond;
|
|
|
|
condv = (condvar_t *)malloc(sizeof(condvar_t));
|
|
if (!condv)
|
|
return NULL;
|
|
|
|
mutex = (pthread_mutex_t *)malloc(sizeof(pthread_mutex_t));
|
|
if (!mutex)
|
|
return NULL;
|
|
|
|
cond = (pthread_cond_t *)malloc(sizeof(pthread_cond_t));
|
|
if (!cond)
|
|
return NULL;
|
|
|
|
if (!pthread_mutex_init(mutex, NULL))
|
|
{
|
|
if (!pthread_cond_init(cond, NULL))
|
|
{
|
|
condv->cond = cond;
|
|
condv->mutex = mutex;
|
|
|
|
return (void *)condv;
|
|
}
|
|
else
|
|
pthread_mutex_destroy(mutex);
|
|
}
|
|
|
|
free(cond);
|
|
free(mutex);
|
|
free(condv);
|
|
return NULL;
|
|
}
|
|
|
|
qboolean Sys_LockConditional(void *condv)
|
|
{
|
|
return !pthread_mutex_lock(((condvar_t *)condv)->mutex);
|
|
}
|
|
|
|
qboolean Sys_UnlockConditional(void *condv)
|
|
{
|
|
return !pthread_mutex_unlock(((condvar_t *)condv)->mutex);
|
|
}
|
|
|
|
qboolean Sys_ConditionWait(void *condv)
|
|
{
|
|
return !pthread_cond_wait(((condvar_t *)condv)->cond, ((condvar_t *)condv)->mutex);
|
|
}
|
|
|
|
qboolean Sys_ConditionSignal(void *condv)
|
|
{
|
|
return !pthread_cond_signal(((condvar_t *)condv)->cond);
|
|
}
|
|
|
|
qboolean Sys_ConditionBroadcast(void *condv)
|
|
{
|
|
return !pthread_cond_broadcast(((condvar_t *)condv)->cond);
|
|
}
|
|
|
|
void Sys_DestroyConditional(void *condv)
|
|
{
|
|
condvar_t *cv = (condvar_t *)condv;
|
|
|
|
pthread_cond_destroy(cv->cond);
|
|
pthread_mutex_destroy(cv->mutex);
|
|
free(cv->cond);
|
|
free(cv->mutex);
|
|
free(cv);
|
|
}
|
|
#endif
|
|
|
|
void Sys_Sleep (double seconds)
|
|
{
|
|
struct timespec ts;
|
|
|
|
ts.tv_sec = (time_t)seconds;
|
|
seconds -= ts.tv_sec;
|
|
ts.tv_nsec = seconds * 1000000000.0;
|
|
|
|
nanosleep(&ts, NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef SUBSERVERS
|
|
#include <spawn.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <errno.h>
|
|
#include <sys/wait.h>
|
|
#define STDIN 0
|
|
#define STDOUT 1
|
|
#define STDERR 2
|
|
|
|
typedef struct slaveserver_s
|
|
{
|
|
pubsubserver_t pub;
|
|
|
|
int inpipe;
|
|
int outpipe;
|
|
pid_t pid; //so we don't end up with zombie processes
|
|
|
|
qbyte inbuffer[2048];
|
|
int inbufsize;
|
|
} linsubserver_t;
|
|
|
|
static void Sys_InstructSlave(pubsubserver_t *ps, sizebuf_t *cmd)
|
|
{
|
|
//FIXME: this is blocking. this is bad if the target is also blocking while trying to write to us.
|
|
//FIXME: merge buffering logic with SSV_InstructMaster, and allow for failure if full
|
|
linsubserver_t *s = (linsubserver_t*)ps;
|
|
if (s->outpipe == -1)
|
|
return; //it already died.
|
|
cmd->data[0] = cmd->cursize & 0xff;
|
|
cmd->data[1] = (cmd->cursize>>8) & 0xff;
|
|
write(s->outpipe, cmd->data, cmd->cursize);
|
|
}
|
|
|
|
static int Sys_SubServerRead(pubsubserver_t *ps)
|
|
{
|
|
linsubserver_t *s = (linsubserver_t*)ps;
|
|
|
|
if (s->inbufsize < sizeof(s->inbuffer) && s->inpipe != -1)
|
|
{
|
|
ssize_t avail = read(s->inpipe, s->inbuffer+s->inbufsize, sizeof(s->inbuffer)-s->inbufsize);
|
|
if (!avail)
|
|
{ //eof
|
|
close(s->inpipe);
|
|
close(s->outpipe);
|
|
Con_Printf("%i:%s has died\n", s->pub.id, s->pub.name);
|
|
s->inpipe = -1;
|
|
s->outpipe = -1;
|
|
waitpid(s->pid, NULL, 0);
|
|
}
|
|
else if (avail < 0)
|
|
{
|
|
int e = errno;
|
|
if (e == EAGAIN || e == EWOULDBLOCK)
|
|
;
|
|
else
|
|
perror("subserver read");
|
|
}
|
|
else
|
|
s->inbufsize += avail;
|
|
}
|
|
|
|
if(s->inbufsize >= 2)
|
|
{
|
|
unsigned short len = s->inbuffer[0] | (s->inbuffer[1]<<8);
|
|
if (s->inbufsize >= len && len>=2)
|
|
{
|
|
memcpy(net_message.data, s->inbuffer+2, len-2);
|
|
net_message.cursize = len-2;
|
|
memmove(s->inbuffer, s->inbuffer+len, s->inbufsize - len);
|
|
s->inbufsize -= len;
|
|
MSG_BeginReading (msg_nullnetprim);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
else if (s->inpipe == -1)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
#ifdef SQL
|
|
#include "sv_sql.h"
|
|
#endif
|
|
|
|
pubsubserver_t *Sys_ForkServer(void)
|
|
{
|
|
#ifdef SERVERONLY
|
|
// extern jmp_buf host_abort;
|
|
|
|
int toslave[2];
|
|
int tomaster[2];
|
|
linsubserver_t *ctx;
|
|
pid_t pid;
|
|
|
|
//make sure we're fully synced, so that workers can't mess up
|
|
Cvar_Set(Cvar_FindVar("worker_count"), "0");
|
|
COM_WorkerFullSync();
|
|
DL_DeThread();
|
|
#ifdef SQL
|
|
SQL_KillServers(); //FIXME: this is bad...
|
|
#endif
|
|
//FIXME: we should probably use posix_atfork for those.
|
|
|
|
pipe(toslave);
|
|
pipe(tomaster);
|
|
|
|
//make the reads non-blocking.
|
|
fcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);
|
|
fcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);
|
|
|
|
pid = fork();
|
|
|
|
if (!pid)
|
|
{ //this is the child
|
|
dup2(toslave[0], STDIN);
|
|
close(toslave[1]);
|
|
close(toslave[0]);
|
|
dup2(tomaster[1], STDOUT);
|
|
|
|
isClusterSlave = true;
|
|
|
|
FS_UnloadPackFiles(); //these handles got wiped. make sure they're all properly wiped before loading new handles.
|
|
NET_Shutdown();
|
|
|
|
FS_ReloadPackFiles();
|
|
|
|
return NULL; //lets hope the caller can cope.
|
|
//jump out into the main work loop
|
|
// longjmp(host_abort, 1);
|
|
// exit(0); //err...
|
|
}
|
|
else
|
|
{ //this is the parent
|
|
close(toslave[0]);
|
|
close(tomaster[1]);
|
|
if (pid == -1)
|
|
{ //fork failed. make sure everything is destroyed.
|
|
close(toslave[1]);
|
|
close(tomaster[0]);
|
|
return NULL;
|
|
}
|
|
|
|
Con_DPrintf("Forked new server node\n");
|
|
ctx = Z_Malloc(sizeof(*ctx));
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
int toslave[2];
|
|
int tomaster[2];
|
|
char exename[MAX_OSPATH];
|
|
posix_spawn_file_actions_t action;
|
|
linsubserver_t *ctx;
|
|
char *argv[64];
|
|
int argc = 0;
|
|
|
|
argv[argc++] = exename;
|
|
argv[argc++] = "-clusterslave";
|
|
argc += FS_GetManifestArgv(argv+argc, countof(argv)-argc-1);
|
|
argv[argc++] = NULL;
|
|
|
|
#if 0
|
|
strcpy(exename, "/bin/ls");
|
|
args[1] = NULL;
|
|
#elif 0
|
|
strcpy(exename, "/tmp/ftedbg/fteqw.sv");
|
|
#else
|
|
memset(exename, 0, sizeof(exename)); //having problems with valgrind being stupid.
|
|
if (readlink("/proc/self/exe", exename, sizeof(exename)-1) <= 0)
|
|
return NULL;
|
|
#endif
|
|
Con_DPrintf("Execing %s\n", exename);
|
|
|
|
ctx = Z_Malloc(sizeof(*ctx));
|
|
|
|
pipe(toslave);
|
|
pipe(tomaster);
|
|
|
|
//make the reads non-blocking.
|
|
fcntl(toslave[1], F_SETFL, fcntl(toslave[1], F_GETFL, 0)|O_NONBLOCK);
|
|
fcntl(tomaster[0], F_SETFL, fcntl(tomaster[0], F_GETFL, 0)|O_NONBLOCK);
|
|
|
|
posix_spawn_file_actions_init(&action);
|
|
posix_spawn_file_actions_addclose(&action, toslave[1]);
|
|
posix_spawn_file_actions_addclose(&action, tomaster[0]);
|
|
|
|
posix_spawn_file_actions_adddup2(&action, toslave[0], STDIN);
|
|
posix_spawn_file_actions_adddup2(&action, tomaster[1], STDOUT);
|
|
// posix_spawn_file_actions_adddup2(&action, tomaster[1], STDERR);
|
|
|
|
posix_spawn_file_actions_addclose(&action, toslave[0]);
|
|
posix_spawn_file_actions_addclose(&action, tomaster[1]);
|
|
|
|
posix_spawn(&ctx->pid, exename, &action, NULL, argv, NULL);
|
|
#endif
|
|
|
|
ctx->inpipe = tomaster[0];
|
|
close(tomaster[1]);
|
|
close(toslave[0]);
|
|
ctx->outpipe = toslave[1];
|
|
|
|
ctx->pub.funcs.InstructSlave = Sys_InstructSlave;
|
|
ctx->pub.funcs.SubServerRead = Sys_SubServerRead;
|
|
return &ctx->pub;
|
|
}
|
|
|
|
void SSV_InstructMaster(sizebuf_t *cmd)
|
|
{
|
|
cmd->data[0] = cmd->cursize & 0xff;
|
|
cmd->data[1] = (cmd->cursize>>8) & 0xff;
|
|
write(STDOUT, cmd->data, cmd->cursize);
|
|
|
|
//FIXME: handle partial writes.
|
|
}
|
|
|
|
void SSV_CheckFromMaster(void)
|
|
{
|
|
static char inbuffer[1024];
|
|
static int inbufsize;
|
|
for(;;)
|
|
{
|
|
if(inbufsize >= 2)
|
|
{
|
|
unsigned short len = inbuffer[0] | (inbuffer[1]<<8);
|
|
if (inbufsize >= len && len>=2)
|
|
{
|
|
memcpy(net_message.data, inbuffer+2, len-2);
|
|
net_message.cursize = len-2;
|
|
memmove(inbuffer, inbuffer+len, inbufsize - len);
|
|
inbufsize -= len;
|
|
MSG_BeginReading (msg_nullnetprim);
|
|
|
|
SSV_ReadFromControlServer();
|
|
|
|
continue; //keep trying to handle it
|
|
}
|
|
}
|
|
|
|
if (inbufsize == sizeof(inbuffer))
|
|
{ //fatal: we can't easily recover from this.
|
|
SV_FinalMessage("Cluster message too large\n");
|
|
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
|
|
break;
|
|
}
|
|
|
|
{
|
|
ssize_t avail = read(STDIN, inbuffer+inbufsize, sizeof(inbuffer)-inbufsize);
|
|
if (!avail)
|
|
{ //eof
|
|
SV_FinalMessage("Cluster shut down\n");
|
|
Cmd_ExecuteString("quit force", RESTRICT_LOCAL);
|
|
break;
|
|
}
|
|
else if (avail < 0)
|
|
{
|
|
int e = errno;
|
|
if (e == EAGAIN || e == EWOULDBLOCK)
|
|
;
|
|
else
|
|
perror("master read");
|
|
break;
|
|
}
|
|
else
|
|
inbufsize += avail;
|
|
}
|
|
}
|
|
}
|
|
#endif
|