*.o
*.i
*.s
+*.obj
+*.tlog
ChangeLog
darkplaces-agl
darkplaces-glx
+++ /dev/null
-PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.29.30037:VCServicingVersionATL=14.29.30038:VCServicingVersionCrtHeaders=14.29.30040:VCServicingVersionCompilers=14.29.30040:TargetPlatformVersion=10.0.19041.0:\r
-Debug|Win32|C:\Users\havoc\Source\Repos\darkplaces\|\r
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project>\r
- <ProjectOutputs>\r
- <ProjectOutput>\r
- <FullPath>C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe</FullPath>\r
- </ProjectOutput>\r
- </ProjectOutputs>\r
- <ContentFiles />\r
- <SatelliteDlls />\r
- <NonRecipeFileRefs />\r
-</Project>
\ No newline at end of file
+++ /dev/null
- darkplaces-sdl2-vs2019.vcxproj -> C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe\r
+++ /dev/null
-PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.29.30037:VCServicingVersionATL=14.29.30038:VCServicingVersionCrtHeaders=14.29.30040:VCServicingVersionCompilers=14.29.30040:TargetPlatformVersion=10.0.19041.0:\r
-Debug|x64|C:\Users\havoc\Source\Repos\darkplaces\|\r
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project>\r
- <ProjectOutputs>\r
- <ProjectOutput>\r
- <FullPath>C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe</FullPath>\r
- </ProjectOutput>\r
- </ProjectOutputs>\r
- <ContentFiles />\r
- <SatelliteDlls />\r
- <NonRecipeFileRefs />\r
-</Project>
\ No newline at end of file
+++ /dev/null
-cl : command line warning D9035: option 'Gm' has been deprecated and will be removed in a future release\r
- convex.c\r
- darkplaces-sdl2-vs2019.vcxproj -> C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe\r
+++ /dev/null
-PlatformToolSet=v142:VCToolArchitecture=Native32Bit:VCToolsVersion=14.29.30037:VCServicingVersionATL=14.29.30038:VCServicingVersionCrtHeaders=14.29.30040:VCServicingVersionCompilers=14.29.30040:TargetPlatformVersion=10.0.19041.0:\r
-Release|x64|C:\Users\havoc\Source\Repos\darkplaces\|\r
+++ /dev/null
-<?xml version="1.0" encoding="utf-8"?>\r
-<Project>\r
- <ProjectOutputs>\r
- <ProjectOutput>\r
- <FullPath>C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe</FullPath>\r
- </ProjectOutput>\r
- </ProjectOutputs>\r
- <ContentFiles />\r
- <SatelliteDlls />\r
- <NonRecipeFileRefs />\r
-</Project>
\ No newline at end of file
+++ /dev/null
- bih.c\r
- builddate.c\r
- cap_avi.c\r
- cap_ogg.c\r
- cd_shared.c\r
- cl_cmd.c\r
- cl_collision.c\r
- cl_demo.c\r
- cl_ents.c\r
- cl_ents4.c\r
- cl_ents5.c\r
- cl_ents_nq.c\r
- cl_ents_qw.c\r
- cl_input.c\r
- cl_main.c\r
- cl_parse.c\r
- cl_particles.c\r
- cl_screen.c\r
- cl_video.c\r
- cl_video_libavw.c\r
- clvm_cmds.c\r
- cmd.c\r
- collision.c\r
- com_crc16.c\r
- com_ents.c\r
- com_ents4.c\r
- com_game.c\r
- com_infostring.c\r
- com_msg.c\r
- common.c\r
- console.c\r
- csprogs.c\r
- curves.c\r
- cvar.c\r
- dpvsimpledecode.c\r
- filematch.c\r
- fractalnoise.c\r
- fs.c\r
- ft2.c\r
- gl_backend.c\r
- gl_draw.c\r
- gl_rmain.c\r
- gl_rsurf.c\r
- gl_textures.c\r
- hmac.c\r
- host.c\r
- image.c\r
- image_png.c\r
- jpeg.c\r
- keys.c\r
- lhnet.c\r
- libcurl.c\r
- mathlib.c\r
- matrixlib.c\r
- mdfour.c\r
- menu.c\r
- meshqueue.c\r
- mod_skeletal_animatevertices_generic.c\r
- mod_skeletal_animatevertices_sse.c\r
- model_alias.c\r
- model_brush.c\r
- model_shared.c\r
- model_sprite.c\r
- mvm_cmds.c\r
- netconn.c\r
- palette.c\r
- polygon.c\r
- portals.c\r
- protocol.c\r
- prvm_cmds.c\r
- prvm_edict.c\r
- prvm_exec.c\r
- r_explosion.c\r
- r_lightning.c\r
- r_modules.c\r
- r_shadow.c\r
- r_stats.c\r
- r_sky.c\r
- r_sprites.c\r
- sbar.c\r
- snd_main.c\r
- snd_mem.c\r
- snd_mix.c\r
- snd_ogg.c\r
- snd_sdl.c\r
- snd_wav.c\r
- snd_xmp.c\r
- sv_ccmds.c\r
- sv_demo.c\r
- sv_ents.c\r
- sv_ents4.c\r
- sv_ents5.c\r
- sv_ents_nq.c\r
- sv_ents_csqc.c\r
- sv_main.c\r
- sv_move.c\r
- sv_phys.c\r
- sv_save.c\r
- sv_send.c\r
- sv_user.c\r
- svbsp.c\r
- svvm_cmds.c\r
- sys_sdl.c\r
- sys_shared.c\r
- taskqueue.c\r
- thread_sdl.c\r
- utf8lib.c\r
- vid_sdl.c\r
- vid_shared.c\r
- view.c\r
- wad.c\r
- world.c\r
- zone.c\r
- crypto.c\r
- Creating library C:\Users\havoc\Source\Repos\darkplaces\\darkplaces-sdl2-vs2019.lib and object C:\Users\havoc\Source\Repos\darkplaces\\darkplaces-sdl2-vs2019.exp\r
- Generating code\r
- Previous IPDB not found, fall back to full compilation.\r
- All 3374 functions were compiled because no usable IPDB/IOBJ from previous compilation was found.\r
- Finished generating code\r
- darkplaces-sdl2-vs2019.vcxproj -> C:\Users\havoc\Source\Repos\darkplaces\darkplaces-sdl2-vs2019.exe\r
#include "cl_collision.h"
-cvar_t cl_name = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "name", "player", "player name"};
+cvar_t cl_name = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "_cl_name", "player", "player name"};
cvar_t cl_rate = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate", "20000", "connection speed"};
cvar_t cl_rate_burstsize = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "rate_burstsize", "1024", "rate control burst size"};
cvar_t cl_topcolor = {CF_CLIENT | CF_ARCHIVE | CF_USERINFO, "topcolor", "0", "color of your shirt"};
}
}
+/*
+==================
+CL_Name_f
+
+The logic from div0-stable's Host_Name_f() is now in SV_Name_f().
+==================
+*/
+static void CL_Name_f(cmd_state_t *cmd)
+{
+ char *newNameSource;
+
+ if (Cmd_Argc(cmd) == 1)
+ {
+ Con_Printf("name: \"%s^7\"\n", cl_name.string);
+ return;
+ }
+
+ // in the single-arg case any enclosing quotes shall be stripped
+ newNameSource = (char *)(Cmd_Argc(cmd) == 2 ? Cmd_Argv(cmd, 1) : Cmd_Args(cmd));
+
+ if (strlen(newNameSource) >= MAX_SCOREBOARDNAME) // may as well truncate before networking
+ newNameSource[MAX_SCOREBOARDNAME - 1] = '\0'; // this is fine (cbuf stores length)
+
+ Cvar_SetQuick(&cl_name, newNameSource);
+}
+
/*
==================
CL_Color_f
{
dpsnprintf(cls.userinfo, sizeof(cls.userinfo), "\\name\\player\\team\\none\\topcolor\\0\\bottomcolor\\0\\rate\\10000\\msg\\1\\noaim\\1\\*ver\\dp");
+ /* In Quake `name` is a command that concatenates its arguments (quotes unnecessary)
+ * which is expected in most DP-based games.
+ * In QuakeWorld it's a cvar which requires quotes if spaces are used.
+ */
Cvar_RegisterVariable(&cl_name);
- Cvar_RegisterVirtual(&cl_name, "_cl_name");
+ if ((0)) // TODO: if (gamemode == GAME_QUAKEWORLD)
+ Cvar_RegisterVirtual(&cl_name, "name");
+ else
+ Cmd_AddCommand(CF_CLIENT, "name", CL_Name_f, "change your player name");
+
Cvar_RegisterVariable(&cl_rate);
Cvar_RegisterVirtual(&cl_rate, "_cl_rate");
Cvar_RegisterVariable(&cl_rate_burstsize);
CL_VM_PreventInformationLeaks();
- // get new key events
- Key_EventQueue_Unblock();
- SndSys_SendKeyEvents();
- Sys_SendKeyEvents();
-
/*
* If the accumulator hasn't become positive, don't
* run the frame. Everything that happens before this
Cause a command to be executed after a delay.
============
*/
-static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing);
+static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending);
+static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize);
static void Cmd_Defer_f (cmd_state_t *cmd)
{
cmd_input_t *current;
cmd_buf_t *cbuf = cmd->cbuf;
+ unsigned int cmdsize;
if(Cmd_Argc(cmd) == 1)
{
else if(Cmd_Argc(cmd) == 2 && !strcasecmp("clear", Cmd_Argv(cmd, 1)))
{
while(!List_Is_Empty(&cbuf->deferred))
+ {
+ cbuf->size -= List_Entry(cbuf->deferred.next, cmd_input_t, list)->length;
List_Move_Tail(cbuf->deferred.next, &cbuf->free);
+ }
}
- else if(Cmd_Argc(cmd) == 3)
+ else if(Cmd_Argc(cmd) == 3 && (cmdsize = strlen(Cmd_Argv(cmd, 2))) )
{
- const char *text = Cmd_Argv(cmd, 2);
- current = Cbuf_LinkGet(cbuf, NULL);
- current->length = strlen(text);
- current->source = cmd;
- current->delay = atof(Cmd_Argv(cmd, 1));
-
- if(current->size < current->length)
- {
- current->text = (char *)Mem_Realloc(cbuf_mempool, current->text, current->length + 1);
- current->size = current->length;
- }
+ Cbuf_Lock(cbuf);
- strlcpy(current->text, text, current->length + 1);
+ Cbuf_LinkString(cmd, &cbuf->deferred, NULL, Cmd_Argv(cmd, 2), false, cmdsize);
+ List_Entry(cbuf->deferred.prev, cmd_input_t, list)->delay = atof(Cmd_Argv(cmd, 1));
- List_Move_Tail(¤t->list, &cbuf->deferred);
+ Cbuf_Unlock(cbuf);
}
else
{
COMMAND BUFFER
+ * The Quake command-line is super basic. It can be entered in the console
+ * or in config files. A semicolon is used to terminate a command and chain
+ * them together. Otherwise, a newline delineates command input.
+ *
+ * In most engines, the Quake command-line is a simple linear text buffer that
+ * is parsed when it executes. In Darkplaces, we use a linked list of command
+ * input and parse the input on the spot.
+ *
+ * This was done because Darkplaces allows multiple command interpreters on the
+ * same thread. Previously, each interpreter maintained its own buffer and this
+ * caused problems related to execution order, and maintaining a single simple
+ * buffer for all interpreters makes it non-trivial to keep track of which
+ * command should execute on which interpreter.
+
=============================================================================
*/
-static cmd_input_t *Cbuf_LinkGet(cmd_buf_t *cbuf, cmd_input_t *existing)
+/*
+============
+Cbuf_NodeGet
+
+Returns an existing buffer node for appending or reuse, or allocates a new one
+============
+*/
+static cmd_input_t *Cbuf_NodeGet(cmd_buf_t *cbuf, cmd_input_t *existing)
{
- cmd_input_t *ret = NULL;
+ cmd_input_t *node;
if(existing && existing->pending)
- ret = existing;
+ node = existing;
else if(!List_Is_Empty(&cbuf->free))
{
- ret = List_Entry(cbuf->free.next, cmd_input_t, list);
- ret->length = 0;
- ret->pending = false;
+ node = List_Entry(cbuf->free.next, cmd_input_t, list);
+ node->length = node->pending = 0;
+ }
+ else
+ {
+ node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
+ node->list.prev = node->list.next = &node->list;
+ node->size = node->length = node->pending = 0;
}
- return ret;
-}
-
-static cmd_input_t *Cmd_AllocInputNode(void)
-{
- cmd_input_t *node = (cmd_input_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_input_t));
- node->list.prev = node->list.next = &node->list;
- node->size = node->length = node->pending = 0;
return node;
}
+/*
+============
+Cbuf_LinkString
-// Cloudwalk FIXME: The entire design of this thing is overly complicated.
-// We could very much safely have one input node per line whether or not
-// the command was terminated. We don't need to split up input nodes per command
-// executed.
-static size_t Cmd_ParseInput (cmd_input_t **output, char **input)
+Copies a command string into a buffer node
+============
+*/
+static void Cbuf_LinkString(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool leavepending, unsigned int cmdsize)
{
- size_t pos, cmdsize = 0, start = 0;
- qbool command = false, lookahead = false;
- qbool quotes = false, comment = false;
- qbool escaped = false;
-
- /*
- * The Quake command-line is super basic. It can be entered in the console
- * or in config files. A semicolon is used to terminate a command and chain
- * them together. Otherwise, a newline delineates command input.
- *
- * In most engines, the Quake command-line is a simple linear text buffer that
- * is parsed when it executes. In Darkplaces, we use a linked list of command
- * input and parse the input on the spot.
- *
- * This was done because Darkplaces allows multiple command interpreters on the
- * same thread. Previously, each interpreter maintained its own buffer and this
- * caused problems related to execution order, and maintaining a single simple
- * buffer for all interpreters makes it non-trivial to keep track of which
- * command should execute on which interpreter.
- */
-
- // Run until command and lookahead are both true, or until we run out of input.
- for (pos = 0; (*input)[pos]; pos++)
- {
- // Look for newlines and semicolons. Ignore semicolons in quotes.
- switch((*input)[pos])
- {
- case '\r':
- case '\n':
- command = false;
- comment = false;
- break;
- default:
- if(!comment) // Not a newline so far. Still not a valid command yet.
- {
- if(!quotes && (*input)[pos] == ';') // Ignore semicolons in quotes.
- command = false;
- else if (ISCOMMENT((*input), pos)) // Comments
- {
- comment = true;
- command = false;
- }
- else
- {
- command = true;
- if(!lookahead)
- {
- if(!cmdsize)
- start = pos;
- cmdsize++;
- }
-
- switch((*input)[pos])
- {
- case '"':
- if (!escaped)
- quotes = !quotes;
- else
- escaped = false;
- break;
- case '\\':
- if (!escaped && quotes)
- escaped = true;
- else if (escaped)
- escaped = false;
- break;
- }
- }
- }
- }
- if(cmdsize && !command)
- lookahead = true;
+ cmd_buf_t *cbuf = cmd->cbuf;
+ cmd_input_t *node = Cbuf_NodeGet(cbuf, existing);
+ unsigned int offset = node->length; // > 0 if(pending)
- if(command && lookahead)
- break;
+ // node will match existing if its text was pending continuation
+ if(node != existing)
+ {
+ node->source = cmd;
+ List_Move_Tail(&node->list, head);
}
- if(cmdsize)
+ node->length += cmdsize;
+ if(node->size < node->length)
{
- size_t offset = 0;
-
- if(!*output)
- *output = Cmd_AllocInputNode();
-
- // Append, since this input line hasn't closed yet.
- if((*output)->pending)
- offset = (*output)->length;
-
- (*output)->length += cmdsize;
-
- if((*output)->size < (*output)->length)
- {
- (*output)->text = (char *)Mem_Realloc(cbuf_mempool, (*output)->text, (*output)->length + 1);
- (*output)->size = (*output)->length;
- }
-
- strlcpy(&(*output)->text[offset], &(*input)[start], cmdsize + 1);
-
- /*
- * If we were still looking ahead by the time we broke from the loop, the command input
- * hasn't terminated yet and we're still expecting more, so keep this node open for appending later.
- */
- (*output)->pending = !lookahead;
+ node->text = (char *)Mem_Realloc(cbuf_mempool, node->text, node->length + 1);
+ node->size = node->length;
}
+ cbuf->size += cmdsize;
- // Set input to its new position. Can be NULL.
- *input = &(*input)[pos];
-
- return cmdsize;
+ strlcpy(&node->text[offset], text, cmdsize + 1); // always sets the last char to \0
+ //Con_Printf("^5Cbuf_LinkString(): %s `^7%s^5`\n", node->pending ? "append" : "new", &node->text[offset]);
+ node->pending = leavepending;
}
-// Cloudwalk: Not happy with this, but it works.
-static void Cbuf_LinkCreate(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text)
+/*
+============
+Cbuf_ParseText
+
+Parses text to isolate command strings for linking into the buffer
+separators: \n \r or unquoted and uncommented ';'
+============
+*/
+static void Cbuf_ParseText(cmd_state_t *cmd, llist_t *head, cmd_input_t *existing, const char *text, qbool allowpending)
{
- char *in = (char *)&text[0];
- cmd_buf_t *cbuf = cmd->cbuf;
- size_t totalsize = 0, newsize = 0;
- cmd_input_t *current = NULL;
+ unsigned int cmdsize = 0, start = 0, pos;
+ qbool quotes = false, comment = false;
- // Slide the pointer down until we reach the end
- while(*in)
+ for (pos = 0; text[pos]; ++pos)
{
- // Check if the current node is still accepting input (input line hasn't terminated)
- current = Cbuf_LinkGet(cbuf, existing);
- newsize = Cmd_ParseInput(¤t, &in);
-
- // Valid command
- if(newsize)
+ switch(text[pos])
{
- // current will match existing if the input line hasn't terminated yet
- if(current != existing)
- {
- current->source = cmd;
- List_Move_Tail(¤t->list, head);
- }
+ case ';':
+ if (comment || quotes)
+ break;
+ case '\r':
+ case '\n':
+ comment = false;
+ quotes = false; // matches div0-stable
+ if (cmdsize)
+ {
+ Cbuf_LinkString(cmd, head, existing, &text[start], false, cmdsize);
+ cmdsize = 0;
+ }
+ else if (existing && existing->pending) // all I got was this lousy \n
+ existing->pending = false;
+ continue; // don't increment cmdsize
+
+ case '/':
+ if (!quotes && text[pos + 1] == '/' && (pos == 0 || ISWHITESPACE(text[pos - 1])))
+ comment = true;
+ break;
+ case '"':
+ if (!comment && (pos == 0 || text[pos - 1] != '\\'))
+ quotes = !quotes;
+ break;
+ }
- totalsize += newsize;
+ if (!comment)
+ {
+ if (!cmdsize)
+ start = pos;
+ ++cmdsize;
}
- else if (current == existing && !totalsize)
- current->pending = false;
- current = NULL;
}
- cbuf->size += totalsize;
+ if (cmdsize) // the line didn't end yet but we do have a string
+ Cbuf_LinkString(cmd, head, existing, &text[start], allowpending, cmdsize);
}
/*
Con_Print("Cbuf_AddText: overflow\n");
else
{
- Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text);
- if(!List_Is_Empty(&llist))
- List_Splice_Tail(&llist, &cbuf->start);
+ // If the string terminates but the (last) line doesn't, the node will be left in the pending state (to be continued).
+ Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.prev, cmd_input_t, list)), text, true);
+ List_Splice_Tail(&llist, &cbuf->start);
}
Cbuf_Unlock(cbuf);
}
Cbuf_InsertText
Adds command text immediately after the current command
-FIXME: actually change the command buffer to do less copying
============
*/
void Cbuf_InsertText (cmd_state_t *cmd, const char *text)
Cbuf_Lock(cbuf);
- // we need to memmove the existing text and stuff this in before it...
if (cbuf->size + l >= cbuf->maxsize)
Con_Print("Cbuf_InsertText: overflow\n");
else
{
- Cbuf_LinkCreate(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text);
- if(!List_Is_Empty(&llist))
- List_Splice(&llist, &cbuf->start);
+ // bones_was_here assertion: when prepending to the buffer it never makes sense to leave node(s) in the `pending` state,
+ // it would have been impossible to append to such text later in the old raw text buffer,
+ // and allowing it causes bugs when .cfg files lack \n at EOF (see: https://gitlab.com/xonotic/darkplaces/-/issues/378).
+ Cbuf_ParseText(cmd, &llist, (List_Is_Empty(&cbuf->start) ? NULL : List_Entry(cbuf->start.next, cmd_input_t, list)), text, false);
+ List_Splice(&llist, &cbuf->start);
}
Cbuf_Unlock(cbuf);
*/
static void Cbuf_Execute_Deferred (cmd_buf_t *cbuf)
{
- cmd_input_t *current;
- double eat;
+ cmd_input_t *current, *n;
+ vec_t eat;
if (host.realtime - cbuf->deferred_oldtime < 0 || host.realtime - cbuf->deferred_oldtime > 1800)
cbuf->deferred_oldtime = host.realtime;
eat = host.realtime - cbuf->deferred_oldtime;
- if (eat < (1.0 / 120.0))
+ if (eat < 1/128)
return;
cbuf->deferred_oldtime = host.realtime;
- List_For_Each_Entry(current, &cbuf->deferred, cmd_input_t, list)
+ List_For_Each_Entry_Safe(current, n, &cbuf->deferred, cmd_input_t, list)
{
current->delay -= eat;
if(current->delay <= 0)
{
- cbuf->size += current->length;
- List_Move(¤t->list, &cbuf->start);
- // We must return and come back next frame or the engine will freeze. Fragile... like glass :3
- return;
+ Cbuf_AddText(current->source, current->text); // parse deferred string and append its cmdstring(s)
+ List_Entry(cbuf->start.prev, cmd_input_t, list)->pending = false; // faster than div0-stable's Cbuf_AddText(";\n");
+ List_Move_Tail(¤t->list, &cbuf->free); // make deferred string memory available for reuse
+ cbuf->size -= current->length;
}
}
}
Cbuf_Execute
============
*/
+extern qbool prvm_runawaycheck;
static qbool Cmd_PreprocessString(cmd_state_t *cmd, const char *intext, char *outtext, unsigned maxoutlen, cmd_alias_t *alias );
void Cbuf_Execute (cmd_buf_t *cbuf)
{
cmd_input_t *current;
char preprocessed[MAX_INPUTLINE];
char *firstchar;
+ unsigned int i = 0;
// LadyHavoc: making sure the tokenizebuffer doesn't get filled up by repeated crashes
cbuf->tokenizebufferpos = 0;
cbuf->wait = false;
break;
}
+
+ if (++i == 1000000 && prvm_runawaycheck)
+ {
+ Con_Printf(CON_WARN "Cbuf_Execute: runaway loop counter hit limit of %d commands, clearing command buffers!\n", i);
+ while (!List_Is_Empty(&cbuf->start))
+ List_Move_Tail(cbuf->start.next, &cbuf->free);
+ while (!List_Is_Empty(&cbuf->deferred))
+ List_Move_Tail(cbuf->deferred.next, &cbuf->free);
+ cbuf->size = 0;
+ }
}
}
cmd_buf_t *cbuf;
cbuf_mempool = Mem_AllocPool("Command buffer", 0, NULL);
cbuf = (cmd_buf_t *)Mem_Alloc(cbuf_mempool, sizeof(cmd_buf_t));
- cbuf->maxsize = 655360;
+ cbuf->maxsize = CMDBUFSIZE;
cbuf->lock = Thread_CreateMutex();
cbuf->wait = false;
host.cbuf = cbuf;
Cmd_Argc
============
*/
-int Cmd_Argc (cmd_state_t *cmd)
+inline int Cmd_Argc (cmd_state_t *cmd)
{
return cmd->argc;
}
Cmd_Argv
============
*/
-const char *Cmd_Argv(cmd_state_t *cmd, int arg)
+inline const char *Cmd_Argv(cmd_state_t *cmd, int arg)
{
if (arg >= cmd->argc )
return cmd->null_string;
Cmd_Args
============
*/
-const char *Cmd_Args (cmd_state_t *cmd)
+inline const char *Cmd_Args (cmd_state_t *cmd)
{
return cmd->args;
}
{
llist_t list;
cmd_state_t *source;
- double delay;
+ vec_t delay;
size_t size;
size_t length;
char *text;
{
// Aliases are purple, cvars are yellow
if (strcmp(cvar->name, name))
- Con_Printf("^6");
+ Con_Printf("^6%s^7 (alias of ^3%s^7)", name, cvar->name);
else
- Con_Printf("^3");
- Con_Printf("%s^7 is \"%s\" [\"%s\"]", name, ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
- if (strcmp(cvar->name, name))
- Con_Printf(" (also ^3%s^7)", cvar->name);
+ Con_Printf("^3%s^7", name);
+ Con_Printf(" is \"%s^7\" [\"%s^7\"]", ((cvar->flags & CF_PRIVATE) ? "********"/*hunter2*/ : cvar->string), cvar->defstring);
if (full)
Con_Printf(" %s", cvar->description);
- Con_Printf("\n");
+ Con_Print("\n");
}
// written by LadyHavoc
#include "mathlib.h"
#include "matrixlib.h"
-extern cvar_t developer;
-extern cvar_t developer_entityparsing;
-extern cvar_t developer_extra;
-extern cvar_t developer_insane;
-extern cvar_t developer_loadfile;
-extern cvar_t developer_loading;
-extern cvar_t host_isclient;
-extern cvar_t sessionid;
-
#endif
float(string s) isfunction = #607;
void(entity e, string s) parseentitydata = #608;
+//DP_QC_FINDCHAIN_TOFIELD
+//see: dpextensions.qc
+entity(vector org, float rad, .entity tofield) findradius_tofield = #22;
+entity(.string fld, string match, .entity tofield) findchain_tofield = #402;
+entity(.float fld, float match, .entity tofield) findchainflags_tofield = #450;
+entity(.float fld, float match, .entity tofield) findchainfloat_tofield = #403;
+
//DP_COVERAGE
//idea: divVerent
//darkplaces implementation: divVerent
}
#endif
+
+/* This code seems to have originally been written with the assumption that
+ * read(..., n) returns n on success. This is not the case (refer to
+ * <https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html>).
+ * Ditto for write.
+ */
+
+/*
+====================
+ReadAll
+
+Read exactly length bytes from fd into buf. If end of file is reached,
+the number of bytes read is returned. If an error occurred, that error
+is returned. Note that if an error is returned, any previously read
+data is lost.
+====================
+*/
+static fs_offset_t ReadAll(const filedesc_t fd, void *const buf, const size_t length)
+{
+ char *const p = (char *)buf;
+ size_t cursor = 0;
+ do
+ {
+ const fs_offset_t result = FILEDESC_READ(fd, p + cursor, length - cursor);
+ if (result < 0) // Error
+ return result;
+ if (result == 0) // EOF
+ break;
+ cursor += result;
+ } while (cursor < length);
+ return cursor;
+}
+
+/*
+====================
+WriteAll
+
+Write exactly length bytes to fd from buf.
+If an error occurred, that error is returned.
+====================
+*/
+static fs_offset_t WriteAll(const filedesc_t fd, const void *const buf, const size_t length)
+{
+ const char *const p = (const char *)buf;
+ size_t cursor = 0;
+ do
+ {
+ const fs_offset_t result = FILEDESC_WRITE(fd, p + cursor, length - cursor);
+ if (result < 0) // Error
+ return result;
+ cursor += result;
+ } while (cursor < length);
+ return cursor;
+}
+
+#undef FILEDESC_READ
+#define FILEDESC_READ ReadAll
+#undef FILEDESC_WRITE
+#define FILEDESC_WRITE WriteAll
+
/** \page fs File System
All of Quake's data access is through a hierchal file system, but the contents
};
static dllhandle_t shfolder_dll = NULL;
-const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
+const GUID qFOLDERID_SavedGames = {0x4C5C32FF, 0xBB9D, 0x43b0, {0xB5, 0xB4, 0x2D, 0x72, 0xE5, 0x4E, 0xAA, 0xA4}};
#define qREFKNOWNFOLDERID const GUID *
#define qKF_FLAG_CREATE 0x8000
#define qKF_FLAG_NO_ALIAS 0x1000
ret = FS_SysCheckGameDir(va(vabuf, sizeof(vabuf), "%s%s/", fs_basedir, gamedir), buf, sizeof(buf));
if(ret)
return ret;
-
+
return fs_checkgamedir_missing;
}
continue;
if(!*info)
continue;
- stringlistappend(&list2, list.strings[i]);
+ stringlistappend(&list2, list.strings[i]);
}
stringlistfreecontents(&list);
====================
FS_SanitizePath
-Sanitize path (replace non-portable characters
+Sanitize path (replace non-portable characters
with portable ones in-place, etc)
====================
*/
if(count < 0)
return NULL;
linkbuf[count] = 0;
-
+
// Now combine the paths...
mergeslash = strrchr(filename, '/');
mergestart = linkbuf;
{
Con_Printf("usage:\n%s <file>\n", Cmd_Argv(cmd, 0));
return;
- }
+ }
filename = Cmd_Argv(cmd, 1);
sp = FS_FindFile(filename, &index, true);
if (!sp) {
Mem_Free(tmp);
return NULL;
}
-
+
if(qz_deflateEnd(&strm) != Z_OK)
{
Con_Printf("FS_Deflate: deflateEnd failed\n");
memcpy(out, tmp, strm.total_out);
Mem_Free(tmp);
-
+
return out;
}
case Z_STREAM_END:
case Z_OK:
break;
-
+
case Z_STREAM_ERROR:
Con_Print("FS_Inflate: stream error!\n");
break;
default:
Con_Print("FS_Inflate: unknown error!\n");
break;
-
+
}
if(ret != Z_OK && ret != Z_STREAM_END)
{
Mem_Free(outbuf.data);
*inflated_size = (size_t)outbuf.cursize;
-
+
return out;
}
// Run any downloads
Curl_Frame();
+ // get new SDL events and add commands from keybindings to the cbuf
+ Sys_SendKeyEvents();
+
// process console commands
Cbuf_Frame(host.cbuf);
#include "qtypes.h"
#include "qdefs.h"
#include "cmd.h"
+#include "cvar.h"
+
+extern cvar_t developer;
+extern cvar_t developer_entityparsing;
+extern cvar_t developer_extra;
+extern cvar_t developer_insane;
+extern cvar_t developer_loadfile;
+extern cvar_t developer_loading;
+extern cvar_t host_isclient;
+extern cvar_t sessionid;
struct cmd_state_s;
{
// button commands add keynum as a parm
if (bind[0] == '+')
- Cbuf_AddText (cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
+ Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
else
- {
- Cbuf_AddText (cmd, bind);
- Cbuf_AddText (cmd, "\n");
- }
- } else if(bind[0] == '+' && !down && keydown[key] == 0)
- Cbuf_AddText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
+ Cbuf_InsertText(cmd, bind);
+ }
+ else if(bind[0] == '+' && !down && keydown[key] == 0)
+ Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
}
return;
}
{
if (down && con_closeontoggleconsole.integer && bind && !strncmp(bind, "toggleconsole", strlen("toggleconsole")) && ascii != STRING_COLOR_TAG)
{
- Cbuf_AddText(cmd, "toggleconsole\n"); // Deferred to next frame so we're not sending the text event to the console.
+ Cbuf_InsertText(cmd, "toggleconsole\n"); // Deferred to next frame so we're not sending the text event to the console.
tbl_keydest[key] = key_void; // key release should go nowhere (especially not to key_menu or key_game)
return;
}
{
// button commands add keynum as a parm
if (bind[0] == '+')
- Cbuf_AddText (cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
+ Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "%s %i\n", bind, key));
else
- {
- Cbuf_AddText (cmd, bind);
- Cbuf_AddText (cmd, "\n");
- }
- } else if(bind[0] == '+' && !down && keydown[key] == 0)
- Cbuf_AddText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
+ Cbuf_InsertText(cmd, bind);
+ }
+ else if(bind[0] == '+' && !down && keydown[key] == 0)
+ Cbuf_InsertText(cmd, va(vabuf, sizeof(vabuf), "-%s %i\n", bind + 1, key));
}
break;
default:
cvar_t mod_q2bsp_littransparentsurfaces = {CF_CLIENT, "mod_q2bsp_littransparentsurfaces", "0", "allows lighting on rain in 3v3gloom3 and other cases of transparent surfaces that have lightmaps that were ignored by quake2"};
cvar_t mod_q1bsp_polygoncollisions = {CF_CLIENT | CF_SERVER, "mod_q1bsp_polygoncollisions", "0", "disables use of precomputed cliphulls and instead collides with polygons (uses Bounding Interval Hierarchy optimizations)"};
-cvar_t mod_q1bsp_zero_hullsize_cutoff = {CF_CLIENT | CF_SERVER, "mod_q1bsp_zero_hullsize_cutoff", "3", "bboxes with an X dimension smaller than this will use the smallest cliphull (0x0x0) instead of being rounded up to the player's cliphull (32x32x56)"};
+cvar_t mod_q1bsp_zero_hullsize_cutoff = {CF_CLIENT | CF_SERVER, "mod_q1bsp_zero_hullsize_cutoff", "3", "bboxes with an X dimension smaller than this will use the smallest cliphull (0x0x0) instead of being rounded up to the player cliphull (32x32x56) in Q1BSP, or crouching player (32x32x36) in HLBSP"};
cvar_t mod_bsp_portalize = {CF_CLIENT, "mod_bsp_portalize", "1", "enables portal generation from BSP tree (may take several seconds per map), used by r_drawportals, r_useportalculling, r_shadow_realtime_dlight_portalculling, r_shadow_realtime_world_compileportalculling"};
cvar_t mod_recalculatenodeboxes = {CF_CLIENT | CF_SERVER, "mod_recalculatenodeboxes", "1", "enables use of generated node bounding boxes based on BSP tree portal reconstruction, rather than the node boxes supplied by the map compiler"};
const hull_t *hull;
VectorSubtract(inmaxs, inmins, size);
- if (cmodel->brush.ishlbsp)
+ if (size[0] < mod_q1bsp_zero_hullsize_cutoff.value)
+ hull = &cmodel->brushq1.hulls[0]; // 0x0x0
+ else if (cmodel->brush.ishlbsp)
{
- if (size[0] < mod_q1bsp_zero_hullsize_cutoff.value)
- hull = &cmodel->brushq1.hulls[0]; // 0x0x0
- else if (size[0] <= 32)
+ if (size[0] <= 32)
{
if (size[2] < 54) // pick the nearest of 36 or 72
hull = &cmodel->brushq1.hulls[3]; // 32x32x36
}
else
{
- if (size[0] < 3)
- hull = &cmodel->brushq1.hulls[0]; // 0x0x0
- else if (size[0] <= 32)
+ if (size[0] <= 32)
hull = &cmodel->brushq1.hulls[1]; // 32x32x56
else
hull = &cmodel->brushq1.hulls[2]; // 64x64x88
PRVM_ED_Free
Marks the edict as free
+
FIXME: walk all entities and NULL out references to this entity
+bones_was_here: do not want, that would break chains immediately!
+Currently chains aren't broken by removing an entity, at least with prvm_reuseedicts_neverinsameframe 1
+which is very handy and some QC code will depend on it.
=================
*/
void PRVM_ED_Free(prvm_prog_t *prog, prvm_edict_t *ed)
cvar_t sv_gameplayfix_multiplethinksperframe = {CF_SERVER, "sv_gameplayfix_multiplethinksperframe", "1", "allows entities to think more often than the server framerate, primarily useful for very high fire rate weapons"};
cvar_t sv_gameplayfix_noairborncorpse = {CF_SERVER, "sv_gameplayfix_noairborncorpse", "1", "causes entities (corpses, items, etc) sitting ontop of moving entities (players) to fall when the moving entity (player) is no longer supporting them"};
cvar_t sv_gameplayfix_noairborncorpse_allowsuspendeditems = {CF_SERVER, "sv_gameplayfix_noairborncorpse_allowsuspendeditems", "1", "causes entities sitting ontop of objects that are instantaneously remove to float in midair (special hack to allow a common level design trick for floating items)"};
-cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "0", "attempts to fix physics errors (where an object ended up in solid for some reason)"};
+cvar_t sv_gameplayfix_nudgeoutofsolid = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid", "1", "attempts to fix physics errors where an object ended up in solid for some reason, supersedes sv_gameplayfix_unstickentities"};
cvar_t sv_gameplayfix_nudgeoutofsolid_separation = {CF_SERVER, "sv_gameplayfix_nudgeoutofsolid_separation", "0.03125", "keep objects this distance apart to prevent collision issues on seams"};
cvar_t sv_gameplayfix_q2airaccelerate = {CF_SERVER, "sv_gameplayfix_q2airaccelerate", "0", "Quake2-style air acceleration"};
cvar_t sv_gameplayfix_nogravityonground = {CF_SERVER, "sv_gameplayfix_nogravityonground", "0", "turn off gravity when on ground (to get rid of sliding)"};
cvar_t sv_gameplayfix_upwardvelocityclearsongroundflag = {CF_SERVER, "sv_gameplayfix_upwardvelocityclearsongroundflag", "1", "prevents monsters, items, and most other objects from being stuck to the floor when pushed around by damage, and other situations in mods"};
cvar_t sv_gameplayfix_downtracesupportsongroundflag = {CF_SERVER, "sv_gameplayfix_downtracesupportsongroundflag", "1", "prevents very short moves from clearing onground (which may make the player stick to the floor at high netfps), fixes groundentity not being set when walking onto a mover with sv_gameplayfix_nogravityonground"};
cvar_t sv_gameplayfix_q1bsptracelinereportstexture = {CF_SERVER, "sv_gameplayfix_q1bsptracelinereportstexture", "1", "enables mods to get accurate trace_texture results on q1bsp by using a surface-hitting traceline implementation rather than the standard solidbsp method, q3bsp always reports texture accurately"};
-cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "1", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
-cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position"};
+cvar_t sv_gameplayfix_unstickplayers = {CF_SERVER, "sv_gameplayfix_unstickplayers", "0", "big hack to try and fix the rare case of MOVETYPE_WALK entities getting stuck in the world clipping hull."};
+cvar_t sv_gameplayfix_unstickentities = {CF_SERVER, "sv_gameplayfix_unstickentities", "1", "hack to check if entities are crossing world collision hull and try to move them to the right position, superseded by sv_gameplayfix_nudgeoutofsolid"};
cvar_t sv_gameplayfix_fixedcheckwatertransition = {CF_SERVER, "sv_gameplayfix_fixedcheckwatertransition", "1", "fix two very stupid bugs in SV_CheckWaterTransition when watertype is CONTENTS_EMPTY (the bugs causes waterlevel to be 1 on first frame, -1 on second frame - the fix makes it 0 on both frames)"};
cvar_t sv_gameplayfix_customstats = {CF_SERVER, "sv_gameplayfix_customstats", "0", "Disable stats higher than 220, for use by certain games such as Xonotic"};
cvar_t sv_gravity = {CF_SERVER | CF_NOTIFY, "sv_gravity","800", "how fast you fall (512 = roughly earth gravity)"};
{
sv.perf_offset_max = sv.perf_acc_offset_max;
sv.perf_offset_avg = sv.perf_acc_offset / sv.perf_acc_offset_samples;
- sv.perf_offset_sdev = sqrt(sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg);
+ sv.perf_offset_sdev = sv.perf_acc_offset_squared / sv.perf_acc_offset_samples - sv.perf_offset_avg * sv.perf_offset_avg;
+ sv.perf_offset_sdev = sv.perf_offset_sdev > 0 ? sqrt(sv.perf_offset_sdev) : 0;
}
if (sv.perf_lost > 0 && reporting)
============
*/
static float SV_Gravity (prvm_edict_t *ent);
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink);
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck);
#define MAX_CLIP_PLANES 5
static int SV_FlyMove (prvm_edict_t *ent, float time, qbool applygravity, float *stepnormal, int hitsupercontentsmask, int skipsupercontentsmask, int skipmaterialflagsmask, float stepheight)
{
break;
VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
- if(!SV_PushEntity(&trace, ent, push, false))
+ if(!SV_PushEntity(&trace, ent, push, false, true))
{
// we got teleported by a touch function
// let's abort the move
// this code is used by MOVETYPE_WALK and MOVETYPE_STEP and SV_UnstickEntity
// abort move if we're stuck in the world (and didn't make it out)
- if (trace.worldstartsolid && trace.allsolid)
+ if (trace.worldstartsolid && trace.allsolid && trace.startdepth < 0)
{
VectorCopy(restore_velocity, PRVM_serveredictvector(ent, velocity));
return 3;
VectorSet(steppush, 0, 0, stepheight);
VectorScale(PRVM_serveredictvector(ent, velocity), time_left, push);
VectorCopy(PRVM_serveredictvector(ent, origin), org);
- if(!SV_PushEntity(&steptrace, ent, steppush, false))
+ if(!SV_PushEntity(&steptrace, ent, steppush, false, true))
{
blocked |= 8;
break;
}
//Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
- if(!SV_PushEntity(&steptrace2, ent, push, false))
+ if(!SV_PushEntity(&steptrace2, ent, push, false, true))
{
blocked |= 8;
break;
}
//Con_Printf("%f %f %f : ", PRVM_serveredictvector(ent, origin)[0], PRVM_serveredictvector(ent, origin)[1], PRVM_serveredictvector(ent, origin)[2]);
VectorSet(steppush, 0, 0, org[2] - PRVM_serveredictvector(ent, origin)[2]);
- if(!SV_PushEntity(&steptrace3, ent, steppush, false))
+ if(!SV_PushEntity(&steptrace3, ent, steppush, false, true))
{
blocked |= 8;
break;
if (stepnormal)
VectorCopy(trace.plane.normal, stepnormal);
}
+
+ // Unlike some other movetypes Quake's SV_FlyMove calls SV_Impact only after setting ONGROUND which id1 fiends rely on.
+ // If we stepped up (sv_gameplayfix_stepmultipletimes) this will impact the steptrace2 plane instead of the original.
+ if (PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace.ent)
+ SV_Impact(ent, &trace);
+ if (ent->free)
+ return blocked; // removed by the impact function
+
if (trace.fraction >= 0.001)
{
// actually covered some distance
Returns true if the push did not result in the entity being teleported by QC code.
============
*/
-static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink)
+static qbool SV_PushEntity (trace_t *trace, prvm_edict_t *ent, vec3_t push, qbool dolink, qbool checkstuck)
{
prvm_prog_t *prog = SVVM_prog;
int solid;
VectorCopy(PRVM_serveredictvector(ent, mins), mins);
VectorCopy(PRVM_serveredictvector(ent, maxs), maxs);
- // move start position out of solids
- if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
- {
- PHYS_NudgeOutOfSolid(prog, ent);
- }
-
VectorCopy(PRVM_serveredictvector(ent, origin), start);
VectorAdd(start, push, end);
type = MOVE_NORMAL;
*trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
- // fail the move if stuck in world
- if (trace->worldstartsolid)
- return true;
+ // abort move if we're stuck in the world (and didn't make it out)
+ if (trace->worldstartsolid && trace->allsolid && trace->startdepth < 0 && checkstuck)
+ {
+ // checking startdepth eliminates many false positives on Q1BSP with mod_q1bsp_polygoncollisions 0
+ // but it's still not guaranteed that we're stuck in a bmodel at this point
+ if (sv_gameplayfix_nudgeoutofsolid.integer && sv_gameplayfix_nudgeoutofsolid_separation.value >= 0)
+ {
+ switch (PHYS_NudgeOutOfSolid(prog, ent))
+ {
+ case 0:
+ Con_Printf(CON_WARN "NudgeOutOfSolid couldn't fix stuck entity %i (classname \"%s\").\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)));
+ return true; // definitely stuck in a bmodel
+ case 1:
+ Con_DPrintf("NudgeOutOfSolid fixed stuck entity %i (classname \"%s\") with offset %f %f %f.\n", (int)PRVM_EDICT_TO_PROG(ent), PRVM_GetString(prog, PRVM_serveredictstring(ent, classname)), PRVM_serveredictvector(ent, origin)[0] - start[0], PRVM_serveredictvector(ent, origin)[1] - start[1], PRVM_serveredictvector(ent, origin)[2] - start[2]);
+ VectorCopy(PRVM_serveredictvector(ent, origin), start);
+ VectorAdd(start, push, end);
+ *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+
+ // definitely not stuck in a bmodel, move may proceed
+ }
+ }
+ else if (sv_gameplayfix_unstickentities.integer && SV_UnstickEntity(ent))
+ {
+ // bones_was_here: pretty sure we can deprecate sv_gameplayfix_unstickentities, sv_gameplayfix_nudgeoutofsolid is much nicer
+ VectorCopy(PRVM_serveredictvector(ent, origin), start);
+ VectorAdd(start, push, end);
+ *trace = SV_TraceBox(start, mins, maxs, end, type, ent, SV_GenericHitSuperContentsMask(ent), 0, 0, collision_extendmovelength.value);
+ }
+ else
+ return true; // assuming stuck, bones_was_here TODO: always use PHYS_NudgeOutOfSolid (remove sv_gameplayfix_nudgeoutofsolid)?
+ }
VectorCopy(trace->endpos, PRVM_serveredictvector(ent, origin));
#endif
if (dolink)
+ {
SV_LinkEdict_TouchAreaGrid(ent);
- if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
- SV_Impact (ent, trace);
+ if((PRVM_serveredictfloat(ent, solid) >= SOLID_TRIGGER && trace->ent && (!((int)PRVM_serveredictfloat(ent, flags) & FL_ONGROUND) || PRVM_serveredictedict(ent, groundentity) != PRVM_EDICT_TO_PROG(trace->ent))))
+ SV_Impact (ent, trace);
+ }
if(ent->priv.required->mark == PRVM_EDICT_MARK_SETORIGIN_CAUGHT)
{
// try moving the contacted entity
PRVM_serveredictfloat(pusher, solid) = SOLID_NOT;
- if(!SV_PushEntity (&trace, check, move, true))
+ if(!SV_PushEntity(&trace, check, move, true, true))
{
// entity "check" got teleported
PRVM_serveredictvector(check, angles)[1] += trace.fraction * moveangle[1];
{
// hack to invoke all necessary movement triggers
VectorClear(move2);
- if(!SV_PushEntity(&trace2, check, move2, true))
+ if(!SV_PushEntity(&trace2, check, move2, true, true))
{
// entity "check" got teleported
continue;
// move up
VectorClear (upmove);
upmove[2] = sv_stepheight.value;
- if(!SV_PushEntity(&trace, ent, upmove, true))
+ if(!SV_PushEntity(&trace, ent, upmove, true, true))
{
// we got teleported when upstepping... must abort the move
return;
// move down
VectorClear (downmove);
downmove[2] = -sv_stepheight.value + start_velocity[2]*sv.frametime;
- if(!SV_PushEntity (&downtrace, ent, downmove, true))
+ if(!SV_PushEntity(&downtrace, ent, downmove, true, true))
{
// we got teleported when downstepping... must abort the move
return;
{
// move origin
VectorScale(PRVM_serveredictvector(ent, velocity), movetime, move);
- if(!SV_PushEntity(&trace, ent, move, true))
+ // The buzzsaw traps in r2m6 and r2m7 use MOVETYPE_FLY and rely on moving while stuck in the world.
+ // Quake movetypes checked allsolid only in SV_FlyMove().
+ if(!SV_PushEntity(&trace, ent, move, true, PRVM_serveredictfloat(ent, movetype) != MOVETYPE_FLY))
return; // teleported
if (ent->free)
return;
- if (trace.bmodelstartsolid && sv_gameplayfix_unstickentities.integer)
- {
- // try to unstick the entity
- SV_UnstickEntity(ent);
- if(!SV_PushEntity(&trace, ent, move, true))
- return; // teleported
- if (ent->free)
- return;
- }
if (trace.fraction == 1)
break;
movetime *= 1 - min(1, trace.fraction);
}
}
+// asynchronous path
void SV_Physics_ClientMove(void)
{
prvm_prog_t *prog = SVVM_prog;
0 optimize darkplaces renderer: get rid of attenuation texture on lights because math is faster, add fastpath for no normalmap (Lava_Croft)
1 bug darkplaces WGL client: figure out why for some people GDI input has stuttering problems with gl_finish 0 mode (Kinn, Urre, romi, Spike, Black)
1 bug darkplaces WGL/GLX/SDL client bug: if sound is unavailable (causing a freeze waiting for it to become available), the config is reset (SavageX)
-1 bug darkplaces bsd filesystem: read() is failing (not returning the requested amount) on freebsd when reading files, whether actual files or in a pk3 - somehow it is still able to read the pk3 zip directory though (suminigashi, Elric)
1 bug darkplaces collisions: curve collisions sometimes catch on the lip of the edge, pushing into the curved back wall around certain jumppads in Nexuiz for example consistently gets stuck just below the ledge (HReaper)
1 bug darkplaces command: "rate", "playermodel", "playerskin", "pmodel" commands can spam server console with usage statements (Spike)
1 bug darkplaces console: when logging using log_file and log_sync 0, setting log_file back to "" does not close the file until another message is posted?
d revelation: reduce damage from weapons (romi)
d sv_user.qc: figure out why looking up/down slows movement and fix it (Vermeulen)
d zmodel: fix scale and origin commands (Vermeulen)
+-d bug darkplaces bsd filesystem: read() is failing (not returning the requested amount) on freebsd when reading files, whether actual files or in a pk3 - somehow it is still able to read the pk3 zip directory though (suminigashi, Elric)
f LordHavoc: examine .mb (Maya Binary) file from Electro and learn its format (Electro)
f bug darkplaces capturevideo: cl_capturevideo 1 with sound off is not locking the framerate of a server (Vermeulen)
f bug darkplaces client: decals are not sticking to submodels