mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2025-01-17 22:50:51 +00:00
Added thread support to GIB and cleaned up escape characters yet again.
With any luck it should actually work this time. Added the getimpulse command so that GIB scripts can check if an impulse command is pending before sending their own. Fixed all the memory leaks I could find. QuakeC and GIB seem to be clean except for maybe one or two sneaky leaks I can't track down.
This commit is contained in:
parent
73e6cf062c
commit
3fffa26fb4
6 changed files with 226 additions and 109 deletions
|
@ -89,6 +89,12 @@ typedef struct cmd_buffer_s {
|
|||
struct cmd_buffer_s *prev, *next; // Neighboring buffers in stack
|
||||
} cmd_buffer_t;
|
||||
|
||||
typedef struct cmd_thread_s {
|
||||
struct cmd_buffer_s *cbuf;
|
||||
long int id;
|
||||
struct cmd_thread_s *prev, *next;
|
||||
} cmd_thread_t;
|
||||
|
||||
//===========================================================================
|
||||
|
||||
void escape (dstring_t * dstr, const char *clist);
|
||||
|
|
|
@ -122,6 +122,7 @@ menu_free (void *_m, void *unused)
|
|||
free ((char*)p->name);
|
||||
free (p);
|
||||
}
|
||||
free (m);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -225,7 +225,7 @@ bi_StringHash_Set (progs_t *pr)
|
|||
sh->elements = (str_hash_elem**) malloc(sizeof(str_hash_elem*));
|
||||
sh->cnt_elements = 0; // 0 because usage as index here
|
||||
} else {
|
||||
sh->elements = (str_hash_elem**) realloc(sh->elements,
|
||||
sh->elements = (str_hash_elem**) realloc(sh->elements,
|
||||
sizeof(str_hash_elem*) * (sh->cnt_elements+1));
|
||||
}
|
||||
sh->elements[sh->cnt_elements] = malloc(sizeof(str_hash_elem));
|
||||
|
@ -260,7 +260,7 @@ bi_StringHash_SetIdx (progs_t *pr)
|
|||
str_hash *sh = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL ||
|
||||
if(res->hashes == NULL ||
|
||||
(hash_id >= res->cnt_hashes || hash_id < 0) ||
|
||||
(val_id < 0 || val_id >= MAX_SH_VALUES))
|
||||
{
|
||||
|
@ -270,7 +270,7 @@ bi_StringHash_SetIdx (progs_t *pr)
|
|||
sh = res->hashes[hash_id];
|
||||
|
||||
if(idx < 0 || idx >= sh->cnt_elements || sh->elements[idx] == NULL) {
|
||||
if(sh->elements[idx] == NULL)
|
||||
if(sh->elements[idx] == NULL)
|
||||
PR_Error(pr, "NULL hash-element found -> not supposed!");
|
||||
|
||||
G_INT (pr, OFS_RETURN) = 0;
|
||||
|
@ -304,8 +304,8 @@ bi_StringHash_Get (progs_t *pr)
|
|||
const char *retstr = NULL;
|
||||
|
||||
// validate the hash ID
|
||||
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 ||
|
||||
val_id >= MAX_SH_VALUES)
|
||||
if(res->hashes == NULL || hash_id >= res->cnt_hashes || hash_id < 0 ||
|
||||
val_id >= MAX_SH_VALUES)
|
||||
{
|
||||
retstr = "";
|
||||
RETURN_STRING(pr, retstr);
|
||||
|
@ -375,8 +375,8 @@ bi_StringHash_GetIdx (progs_t *pr)
|
|||
}
|
||||
sh = res->hashes[hash_id];
|
||||
|
||||
if(idx < 0 || idx >= sh->cnt_elements ||
|
||||
(val_id < -1 || val_id >= MAX_SH_VALUES))
|
||||
if(idx < 0 || idx >= sh->cnt_elements ||
|
||||
(val_id < -1 || val_id >= MAX_SH_VALUES))
|
||||
{
|
||||
retstr = NULL;
|
||||
} else {
|
||||
|
@ -402,12 +402,16 @@ static void
|
|||
bi_strh_clear (progs_t *pr, void *data)
|
||||
{
|
||||
strh_resources_t *res = (strh_resources_t *)data;
|
||||
int i,d;
|
||||
int i,d,n;
|
||||
|
||||
for (i = 0; i < res->cnt_hashes; i++) {
|
||||
if (res->hashes[i]) {
|
||||
for(d = 0; d < res->hashes[i]->cnt_elements; d++) {
|
||||
free(res->hashes[i]->elements[d]);
|
||||
free(res->hashes[i]->elements[d]->key); // Free the key
|
||||
for (n = 0; n < MAX_SH_VALUES; n++) // Free all values
|
||||
if (res->hashes[i]->elements[d]->values[n])
|
||||
free(res->hashes[i]->elements[d]->values[n]);
|
||||
free(res->hashes[i]->elements[d]); // Free the element itself
|
||||
}
|
||||
free(res->hashes[i]->elements);
|
||||
free(res->hashes[i]);
|
||||
|
@ -443,10 +447,10 @@ StringHash_Progs_Init (progs_t *pr)
|
|||
PR_AddBuiltin (pr, "StringHash_GetIdx", bi_StringHash_GetIdx, -1);
|
||||
}
|
||||
|
||||
/*
|
||||
XXX NOTE by elmex:
|
||||
A file, decripted like this is what
|
||||
i want to see everywhere in qf-cvs =)
|
||||
/*
|
||||
XXX NOTE by elmex:
|
||||
A file, decripted like this is what
|
||||
i want to see everywhere in qf-cvs =)
|
||||
No excuse for undocumented code and design without
|
||||
a reason for it.
|
||||
We/I want to know why something was designed how it is.
|
||||
|
|
282
libs/util/cmd.c
282
libs/util/cmd.c
|
@ -63,19 +63,18 @@ typedef struct cmdalias_s {
|
|||
cmdalias_t *cmd_alias;
|
||||
cmd_source_t cmd_source;
|
||||
|
||||
/* FIXME: All these separate buffers are sort of hacky
|
||||
The command buffer interface should be generalized and
|
||||
each part of QF (console, stufftext, config files and scripts)
|
||||
that needs one should allocate and maintain its own.
|
||||
*/
|
||||
cmd_buffer_t *cmd_consolebuffer; // Console buffer
|
||||
cmd_buffer_t *cmd_consolebuffer; // Console buffer
|
||||
cmd_buffer_t *cmd_legacybuffer; // Server stuffcmd buffer with
|
||||
// absolute backwards-compatibility
|
||||
cmd_buffer_t *cmd_privatebuffer; // Buffer for internal command execution
|
||||
// absolute backwards-compatibility
|
||||
cmd_buffer_t *cmd_privatebuffer; // Buffer for internal command execution
|
||||
cmd_buffer_t *cmd_keybindbuffer; // Buffer for commands from bound keys
|
||||
cmd_buffer_t *cmd_activebuffer; // Buffer currently being executed
|
||||
|
||||
cmd_buffer_t *cmd_recycled; // Recycled buffers
|
||||
cmd_buffer_t *cmd_recycled; // Recycled buffers
|
||||
|
||||
|
||||
cmd_thread_t *cmd_threads; // Detached buffers running by themselves
|
||||
long int cmd_threadid; // The id of the last thread + 1
|
||||
|
||||
dstring_t *cmd_backtrace;
|
||||
|
||||
|
@ -89,7 +88,7 @@ hashtab_t *cmd_hash;
|
|||
|
||||
//=============================================================================
|
||||
|
||||
/* Local variable stuff */
|
||||
/* Structure management */
|
||||
|
||||
cmd_localvar_t *
|
||||
Cmd_NewLocal (const char *key, const char *value)
|
||||
|
@ -152,7 +151,7 @@ Cmd_LocalFree (void *ele, void *ptr)
|
|||
cmd_token_t *
|
||||
Cmd_NewToken (void) {
|
||||
cmd_token_t *new;
|
||||
|
||||
|
||||
new = calloc(1,sizeof(cmd_token_t));
|
||||
SYS_CHECKMEM (new);
|
||||
new->original = dstring_newstr();
|
||||
|
@ -190,7 +189,7 @@ Cmd_NewBuffer (qboolean ownvars)
|
|||
|
||||
/*
|
||||
Cmd_FreeBuffer
|
||||
|
||||
|
||||
Actually just "recycles" buffer for
|
||||
later use
|
||||
*/
|
||||
|
@ -203,7 +202,7 @@ Cmd_FreeBuffer (cmd_buffer_t *free) {
|
|||
dstring_clearstr (free->buffer);
|
||||
dstring_clearstr (free->line);
|
||||
dstring_clearstr (free->realline);
|
||||
dstring_clearstr (free->looptext);
|
||||
dstring_clearstr (free->looptext);
|
||||
dstring_clearstr (free->retval);
|
||||
free->locals = 0;
|
||||
free->next = cmd_recycled;
|
||||
|
@ -213,13 +212,52 @@ Cmd_FreeBuffer (cmd_buffer_t *free) {
|
|||
void
|
||||
Cmd_FreeStack (cmd_buffer_t *stack) {
|
||||
cmd_buffer_t *temp;
|
||||
|
||||
|
||||
for (;stack; stack = temp) {
|
||||
temp = stack->next;
|
||||
Cmd_FreeBuffer (stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
cmd_thread_t *
|
||||
Cmd_NewThread (long int id) {
|
||||
cmd_thread_t *new;
|
||||
|
||||
new = calloc (1, sizeof(cmd_thread_t));
|
||||
SYS_CHECKMEM (new);
|
||||
new->id = id;
|
||||
new->cbuf = Cmd_NewBuffer (true);
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
void
|
||||
Cmd_FreeThread (cmd_thread_t *thread) {
|
||||
Cmd_FreeBuffer (thread->cbuf);
|
||||
free (thread);
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
Cmd_AddThread (cmd_thread_t **list, cmd_thread_t *thread) {
|
||||
thread->next = *list;
|
||||
thread->prev = 0;
|
||||
if (*list)
|
||||
(*list)->prev = thread;
|
||||
*list = thread;
|
||||
}
|
||||
|
||||
void
|
||||
Cmd_RemoveThread (cmd_thread_t **list, cmd_thread_t *thread) {
|
||||
if (thread == *list)
|
||||
*list = thread->next;
|
||||
if (thread->next)
|
||||
thread->next->prev = thread->prev;
|
||||
if (thread->prev)
|
||||
thread->prev->next = thread->next;
|
||||
Cmd_FreeThread (thread);
|
||||
}
|
||||
|
||||
/*void
|
||||
Cmd_FreeBuffer (cmd_buffer_t *del)
|
||||
|
@ -345,6 +383,8 @@ Cbuf_Init (void)
|
|||
|
||||
cmd_activebuffer = cmd_consolebuffer;
|
||||
|
||||
cmd_threads = 0;
|
||||
cmd_threadid = 0;
|
||||
cmd_recycled = 0;
|
||||
|
||||
cmd_backtrace = dstring_newstr ();
|
||||
|
@ -480,7 +520,6 @@ Cbuf_ExecuteBuffer (cmd_buffer_t *buffer)
|
|||
Cbuf_ExtractLine (buffer->buffer, buf, buffer->legacy);
|
||||
if (!buf->str[0])
|
||||
continue;
|
||||
Sys_DPrintf("Cbuf_ExecuteBuffer: Executing line %s\n", buf->str);
|
||||
Cmd_TokenizeString (buf->str, cmd_activebuffer->legacy);
|
||||
if (cmd_error)
|
||||
break;
|
||||
|
@ -547,6 +586,16 @@ Cbuf_ExecuteStack (cmd_buffer_t *buffer)
|
|||
void
|
||||
Cbuf_Execute (void)
|
||||
{
|
||||
cmd_thread_t *t, *temp;
|
||||
|
||||
for (t = cmd_threads; t; t = temp) {
|
||||
temp = t->next;
|
||||
if (!t->cbuf->next && !t->cbuf->buffer->str[0]) {
|
||||
Cmd_RemoveThread (&cmd_threads, t);
|
||||
continue;
|
||||
}
|
||||
Cbuf_ExecuteStack (t->cbuf);
|
||||
}
|
||||
Cbuf_ExecuteStack (cmd_keybindbuffer);
|
||||
Cbuf_ExecuteStack (cmd_consolebuffer);
|
||||
Cbuf_ExecuteStack (cmd_legacybuffer);
|
||||
|
@ -1093,31 +1142,27 @@ Cmd_ProcessMath (dstring_t * dstr)
|
|||
statement = dstring_newstr ();
|
||||
|
||||
for (i = 0; i < strlen (dstr->str); i++) {
|
||||
if (dstr->str[i] == '#' && dstr->str[i + 1] == '{') {
|
||||
if (!escaped (dstr->str, i)) {
|
||||
i -= unescape (dstr, i);
|
||||
n = Cmd_EndBrace (dstr->str+i+1)+1;
|
||||
if (n < 0) {
|
||||
Cmd_Error ("Unmatched brace in math expression.\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
/* Copy text between parentheses into a buffer */
|
||||
dstring_clearstr (statement);
|
||||
dstring_insert (statement, dstr->str + i + 2, n - 2, 0);
|
||||
value = EXP_Evaluate (statement->str);
|
||||
if (EXP_ERROR == EXP_E_NORMAL) {
|
||||
temp = va ("%.10g", value);
|
||||
dstring_snip (dstr, i, n + 1); // Nuke the statement
|
||||
dstring_insertstr (dstr, temp, i); // Stick in the value
|
||||
i += strlen (temp) - 1;
|
||||
} else {
|
||||
ret = -2;
|
||||
Cmd_Error (va("Math error: invalid expression %s\n", statement->str));
|
||||
break; // Math evaluation error
|
||||
}
|
||||
} else
|
||||
i -= unescape (dstr, i);
|
||||
if (dstr->str[i] == '#' && dstr->str[i + 1] == '{' && !escaped (dstr->str, i)) {
|
||||
n = Cmd_EndBrace (dstr->str+i+1)+1;
|
||||
if (n < 0) {
|
||||
Cmd_Error ("Unmatched brace in math expression.\n");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
/* Copy text between parentheses into a buffer */
|
||||
dstring_clearstr (statement);
|
||||
dstring_insert (statement, dstr->str + i + 2, n - 2, 0);
|
||||
value = EXP_Evaluate (statement->str);
|
||||
if (EXP_ERROR == EXP_E_NORMAL) {
|
||||
temp = va ("%.10g", value);
|
||||
dstring_snip (dstr, i, n + 1); // Nuke the statement
|
||||
dstring_insertstr (dstr, temp, i); // Stick in the value
|
||||
i += strlen (temp) - 1;
|
||||
} else {
|
||||
ret = -2;
|
||||
Cmd_Error (va("Math error: invalid expression %s\n", statement->str));
|
||||
break; // Math evaluation error
|
||||
}
|
||||
}
|
||||
}
|
||||
dstring_delete (statement);
|
||||
|
@ -1183,18 +1228,14 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
|
|||
if (dstr->str[start+1] == '{')
|
||||
braces = 1;
|
||||
for (i = start + 1 + braces;; i++) {
|
||||
if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$')) {
|
||||
if (!escaped (dstr->str, i)) {
|
||||
i -= unescape (dstr, i);
|
||||
n = Cmd_ProcessVariablesRecursive (dstr, i);
|
||||
if (n < 0) {
|
||||
break;
|
||||
} else {
|
||||
i += n - 1;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
i -= unescape (dstr, i);
|
||||
if (dstr->str[i] == '$' && (braces || dstr->str[i-1] == '$') && !escaped (dstr->str, i)) {
|
||||
n = Cmd_ProcessVariablesRecursive (dstr, i);
|
||||
if (n < 0) {
|
||||
break;
|
||||
} else {
|
||||
i += n - 1;
|
||||
continue;
|
||||
}
|
||||
} else if (!dstr->str[i] && braces) { // No closing brace
|
||||
Cmd_Error ("Unmatched brace in variable substitution expression.\n");
|
||||
n = -1;
|
||||
|
@ -1214,6 +1255,8 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
|
|||
// Then cvars
|
||||
dstring_appendstr (copy, cvar->string);
|
||||
}
|
||||
n = 0;
|
||||
dstring_clearstr (varname); // Reuse variable
|
||||
if (dstr->str[start] == '[') {
|
||||
int val1, val2, res;
|
||||
res = Cmd_ProcessIndex (dstr, start, &val1, &val2);
|
||||
|
@ -1221,16 +1264,16 @@ Cmd_ProcessVariablesRecursive (dstring_t * dstr, int start)
|
|||
n = -1;
|
||||
else if (val1 >= strlen(copy->str) || val1 < 0)
|
||||
n = 0;
|
||||
else if (val2 < strlen(copy->str)) {
|
||||
dstring_insert (dstr, copy->str+val1, val2-val1+1, start);
|
||||
n = val2 - val1 + 1;
|
||||
} else {
|
||||
dstring_insert (dstr, copy->str+val1, strlen(copy->str)-val1, start);
|
||||
n = strlen(copy->str)-val1;
|
||||
}
|
||||
} else {
|
||||
dstring_insertstr (dstr, copy->str, start);
|
||||
n = strlen(copy->str);
|
||||
else if (val2 < strlen(copy->str))
|
||||
dstring_insert (varname, copy->str+val1, val2-val1+1, 0);
|
||||
else
|
||||
dstring_insert (varname, copy->str+val1, strlen(copy->str)-val1, 0);
|
||||
} else
|
||||
dstring_appendstr (varname, copy->str);
|
||||
if (n >= 0) {
|
||||
escape (varname, "\\");
|
||||
dstring_insertstr (dstr, varname->str, start);
|
||||
n = strlen(varname->str);
|
||||
}
|
||||
dstring_delete (copy);
|
||||
dstring_delete (varname);
|
||||
|
@ -1258,6 +1301,7 @@ Cmd_ProcessEmbeddedSingle (dstring_t * dstr, int start)
|
|||
return -1;
|
||||
}
|
||||
if (cmd_activebuffer->returned == cmd_returned) {
|
||||
escape (cmd_activebuffer->retval, "\\");
|
||||
dstring_snip(dstr, start, n+1);
|
||||
dstring_insertstr (dstr, cmd_activebuffer->retval->str, start);
|
||||
n = strlen(cmd_activebuffer->retval->str);
|
||||
|
@ -1283,20 +1327,16 @@ Cmd_ProcessEmbedded (cmd_token_t *tok, dstring_t * dstr)
|
|||
int i, n;
|
||||
|
||||
for (i = tok->pos; i < strlen(dstr->str); i++) {
|
||||
if (dstr->str[i] == '~' && dstr->str[i+1] == '{') {
|
||||
if (!escaped (dstr->str, i)) {
|
||||
i -= unescape (dstr, i);
|
||||
n = Cmd_ProcessEmbeddedSingle (dstr, i);
|
||||
if (n == -2) {
|
||||
tok->pos = i;
|
||||
return n;
|
||||
}
|
||||
if (n < 0)
|
||||
return n;
|
||||
else
|
||||
i += n-1;
|
||||
} else
|
||||
i -= unescape (dstr, i);
|
||||
if (dstr->str[i] == '~' && dstr->str[i+1] == '{' && !escaped (dstr->str, i)) {
|
||||
n = Cmd_ProcessEmbeddedSingle (dstr, i);
|
||||
if (n == -2) {
|
||||
tok->pos = i;
|
||||
return n;
|
||||
}
|
||||
if (n < 0)
|
||||
return n;
|
||||
else
|
||||
i += n-1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -1308,16 +1348,12 @@ Cmd_ProcessVariables (dstring_t * dstr)
|
|||
int i, n;
|
||||
|
||||
for (i = 0; i < strlen (dstr->str); i++) {
|
||||
if (dstr->str[i] == '$') {
|
||||
if (!escaped (dstr->str, i)) {
|
||||
i -= unescape (dstr, i);
|
||||
n = Cmd_ProcessVariablesRecursive (dstr, i);
|
||||
if (n < 0)
|
||||
return n;
|
||||
else
|
||||
i += n - 1;
|
||||
} else
|
||||
i -= unescape (dstr, i);
|
||||
if (dstr->str[i] == '$' && !escaped (dstr->str, i)) {
|
||||
n = Cmd_ProcessVariablesRecursive (dstr, i);
|
||||
if (n < 0)
|
||||
return n;
|
||||
else
|
||||
i += n - 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
@ -1335,27 +1371,24 @@ Cmd_ProcessVariables (dstring_t * dstr)
|
|||
tags, etc. can be escaped
|
||||
*/
|
||||
|
||||
void
|
||||
int
|
||||
Cmd_ProcessEscapes (dstring_t * dstr, const char *noprocess)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (strlen(dstr->str) == 1)
|
||||
return;
|
||||
for (i = 1; i <= strlen (dstr->str); i++) {
|
||||
if (!(dstr->str[i] && strchr (noprocess, dstr->str[i])) && dstr->str[i-1] == '\\' && dstr->str[i] != '\\') {
|
||||
if (dstr->str[i] == 'n' && escaped (dstr->str, i))
|
||||
dstr->str[i] = '\n';
|
||||
i -= unescape (dstr, i);
|
||||
return 0;
|
||||
for (i = 0; i < strlen (dstr->str); i++)
|
||||
if (dstr->str[i] == '\\') {
|
||||
dstring_snip (dstr, i, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
Cmd_ProcessToken (cmd_token_t *token)
|
||||
{
|
||||
int res;
|
||||
Cmd_ProcessEscapes (token->processed, "$#~");
|
||||
res = Cmd_ProcessEmbedded (token, token->processed);
|
||||
if (res < 0)
|
||||
return res;
|
||||
|
@ -1366,6 +1399,9 @@ Cmd_ProcessToken (cmd_token_t *token)
|
|||
if (res < 0)
|
||||
return res;
|
||||
Cmd_ProcessTags (token->processed);
|
||||
res = Cmd_ProcessEscapes (token->processed, "$#~");
|
||||
if (res < 0)
|
||||
return res;
|
||||
token->state = cmd_done;
|
||||
return 0;
|
||||
}
|
||||
|
@ -2004,6 +2040,10 @@ Cmd_For_f (void) {
|
|||
Cmd_ExecuteSubroutine (sub);
|
||||
} else
|
||||
Cmd_Error("Malformed for statement.\n");
|
||||
dstring_delete (arg1);
|
||||
dstring_delete (init);
|
||||
dstring_delete (cond);
|
||||
dstring_delete (inc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2096,6 +2136,53 @@ Cmd_Strlen_f (void)
|
|||
Cmd_Return (va("%ld", (long) strlen (Cmd_Argv(1))));
|
||||
}
|
||||
|
||||
void
|
||||
Cmd_Detach_f (void)
|
||||
{
|
||||
cmd_thread_t *thread;
|
||||
|
||||
if (Cmd_Argc () != 2) {
|
||||
Cmd_Error ("detach: invalid number of arguments.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
thread = Cmd_NewThread (cmd_threadid++);
|
||||
Cmd_AddThread (&cmd_threads, thread);
|
||||
Cbuf_AddTextTo (thread->cbuf, Cmd_Argv(1));
|
||||
Cbuf_ExecuteStack (thread->cbuf); // Execute it now
|
||||
cmd_error = false; // Don't let errors cross into this stack
|
||||
Cmd_Return (va("%li", thread->id));
|
||||
}
|
||||
|
||||
void
|
||||
Cmd_Killthread_f (void)
|
||||
{
|
||||
long int id;
|
||||
cmd_thread_t *t;
|
||||
if (Cmd_Argc() != 2)
|
||||
Cmd_Error ("killthread: invalid number of arguments.\n");
|
||||
id = atol (Cmd_Argv(1));
|
||||
for (t = cmd_threads; t; t = t->next)
|
||||
if (id == t->id) {
|
||||
if (t->cbuf->next)
|
||||
Cmd_FreeStack (t->cbuf->next);
|
||||
t->cbuf->next = 0;
|
||||
dstring_clearstr (t->cbuf->buffer);
|
||||
return;
|
||||
}
|
||||
Cmd_Error ("kill: invalid thread id\n");
|
||||
}
|
||||
void
|
||||
Cmd_Threadstats_f (void)
|
||||
{
|
||||
cmd_thread_t *t;
|
||||
|
||||
Sys_Printf ("Currently running threads:\n");
|
||||
for (t = cmd_threads; t; t = t->next)
|
||||
Sys_Printf("%li\n", t->id);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
Cmd_Hash_Stats_f (void)
|
||||
{
|
||||
|
@ -2179,6 +2266,9 @@ Cmd_Init (void)
|
|||
Cmd_AddCommand ("randint", Cmd_Randint_f, "Returns a random integer between $1 and $2");
|
||||
Cmd_AddCommand ("streq", Cmd_Streq_f, "Returns 1 if $1 and $2 are the same string, 0 otherwise");
|
||||
Cmd_AddCommand ("strlen", Cmd_Strlen_f, "Returns the length of $1");
|
||||
Cmd_AddCommand ("detach", Cmd_Detach_f, "Starts a thread with an initial program of $1");
|
||||
Cmd_AddCommand ("killthread", Cmd_Killthread_f, "Kills thread with id $1");
|
||||
Cmd_AddCommand ("threadstats", Cmd_Threadstats_f, "Shows statistics about threads");
|
||||
|
||||
//Cmd_AddCommand ("cmd_hash_stats", Cmd_Hash_Stats_f, "Display statistics "
|
||||
// "alias and command hash tables");
|
||||
|
|
|
@ -45,6 +45,7 @@ static const char rcsid[] =
|
|||
#include "QF/keys.h"
|
||||
#include "QF/msg.h"
|
||||
#include "QF/teamplay.h"
|
||||
#include "QF/va.h"
|
||||
|
||||
#include "cl_cam.h"
|
||||
#include "cl_demo.h"
|
||||
|
@ -365,6 +366,12 @@ IN_Impulse (void)
|
|||
Team_BestWeaponImpulse (); // HACK HACK HACK
|
||||
}
|
||||
|
||||
void
|
||||
IN_Getimpulse_f (void)
|
||||
{
|
||||
Cmd_Return(va("%i", in_impulse));
|
||||
}
|
||||
|
||||
/*
|
||||
CL_KeyState
|
||||
|
||||
|
@ -795,6 +802,8 @@ CL_Input_Init (void)
|
|||
"jumping");
|
||||
Cmd_AddCommand ("impulse", IN_Impulse, "Call a game function or QuakeC "
|
||||
"function.");
|
||||
Cmd_AddCommand ("getimpulse", IN_Getimpulse_f, "Returns the impulse that "
|
||||
"will be sent to the server this frame");
|
||||
Cmd_AddCommand ("+klook", IN_KLookDown, "When active, +forward and +back "
|
||||
"perform +lookup and +lookdown");
|
||||
Cmd_AddCommand ("-klook", IN_KLookUp, "When active, +forward and +back "
|
||||
|
|
|
@ -453,6 +453,7 @@ void
|
|||
CL_Disconnect (void)
|
||||
{
|
||||
byte final[10];
|
||||
int i;
|
||||
|
||||
connect_time = -1;
|
||||
|
||||
|
@ -494,6 +495,12 @@ CL_Disconnect (void)
|
|||
|
||||
Team_ResetTimers ();
|
||||
|
||||
// remove player info strings
|
||||
for (i = 0; i < MAX_CLIENTS; i++)
|
||||
if (cl.players[i].userinfo) {
|
||||
Info_Destroy (cl.players[i].userinfo);
|
||||
cl.players[i].userinfo = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
|
Loading…
Reference in a new issue