diff --git a/prvm_edict.c b/prvm_edict.c index c6fadd166..5f96e19c7 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -54,6 +54,7 @@ cvar_t prvm_garbagecollection_scan_limit = {CF_CLIENT | CF_SERVER, "prvm_garbage cvar_t prvm_garbagecollection_strings = {CF_CLIENT | CF_SERVER, "prvm_garbagecollection_strings", "1", "automatically call strunzone() on strings that are not referenced"}; cvar_t prvm_stringdebug = {CF_CLIENT | CF_SERVER, "prvm_stringdebug", "0", "Print debug and warning messages related to strings"}; cvar_t sv_entfields_noescapes = {CF_SERVER, "sv_entfields_noescapes", "wad", "Space-separated list of fields in which backslashes won't be parsed as escapes when loading entities from .bsp or .ent files. This is a workaround for buggy maps with unescaped backslashes used as path separators (only forward slashes are allowed in Quake VFS paths)."}; +cvar_t prvm_gameplayfix_div0is0 = {CF_SERVER, "prvm_gameplayfix_div0is0", "0", "When set to 1, floating point division by 0 will return zero instead of returning the IEEE standardized result (likely nan or inf). Other ways of getting non-finite values are not affected, and the warning will still print."}; static double prvm_reuseedicts_always_allow = 0; qbool prvm_runawaycheck = true; @@ -3238,6 +3239,7 @@ void PRVM_Init (void) Cvar_RegisterVariable (&prvm_garbagecollection_strings); Cvar_RegisterVariable (&prvm_stringdebug); Cvar_RegisterVariable (&sv_entfields_noescapes); + Cvar_RegisterVariable (&prvm_gameplayfix_div0is0); // COMMANDLINEOPTION: PRVM: -norunaway disables the runaway loop check (it might be impossible to exit DarkPlaces if used!) prvm_runawaycheck = !Sys_CheckParm("-norunaway"); diff --git a/prvm_exec.c b/prvm_exec.c index fc1e6b207..75b31440b 100644 --- a/prvm_exec.c +++ b/prvm_exec.c @@ -946,6 +946,7 @@ static void PRVM_StatementCoverageEvent(prvm_prog_t *prog, mfunction_t *func, in extern cvar_t prvm_traceqc; extern cvar_t prvm_statementprofiling; extern qbool prvm_runawaycheck; +extern cvar_t prvm_gameplayfix_div0is0; #define PRVM_GLOBALSBASE 0x80000000 diff --git a/prvm_execprogram.h b/prvm_execprogram.h index 2eb0c2987..92206704c 100644 --- a/prvm_execprogram.h +++ b/prvm_execprogram.h @@ -394,16 +394,17 @@ prvm_eval_t *src; OPC->vector[2] = tempfloat * OPA->vector[2]; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_F): - if( OPB->_float != 0.0f ) - { - OPC->_float = OPA->_float / OPB->_float; - } - else + if( OPB->_float == 0.0f ) { PRE_ERROR(); VM_Warning(prog, "Attempted division of %f by zero\n", OPA->_float); - OPC->_float = 0.0f; + if (prvm_gameplayfix_div0is0.integer) + { + OPC->_float = 0; + DISPATCH_OPCODE(); + } } + OPC->_float = OPA->_float / OPB->_float; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_BITAND_F): OPC->_float = (prvm_int_t)OPA->_float & (prvm_int_t)OPB->_float; @@ -884,21 +885,22 @@ prvm_eval_t *src; OPC->vector[2] = (prvm_vec_t) OPB->_int * OPA->vector[2]; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_VF): - if( OPB->_float != 0.0f ) - { - float temp = 1.0f / OPB->_float; - OPC->vector[0] = temp * OPA->vector[0]; - OPC->vector[1] = temp * OPA->vector[1]; - OPC->vector[2] = temp * OPA->vector[2]; - } - else + if( OPB->_float == 0.0f ) { PRE_ERROR(); VM_Warning(prog, "Attempted division of '%f %f %f' by zero\n", OPA->vector[0], OPA->vector[1], OPA->vector[2]); - OPC->vector[0] = 0.0f; - OPC->vector[1] = 0.0f; - OPC->vector[2] = 0.0f; + if (prvm_gameplayfix_div0is0.integer) + { + OPC->vector[0] = 0; + OPC->vector[1] = 0; + OPC->vector[2] = 0; + DISPATCH_OPCODE(); + } } + tempfloat = OPB->_float; + OPC->vector[0] = OPA->vector[0] / tempfloat; + OPC->vector[1] = OPA->vector[1] / tempfloat; + OPC->vector[2] = OPA->vector[2] / tempfloat; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_I): // NOTE: This also catches the second kind of division that can trap, namely, -2147483648 / -1, @@ -915,28 +917,30 @@ prvm_eval_t *src; } DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_IF): - if( OPB->_float != 0.0f ) - { - OPC->_float = ((prvm_vec_t) OPA->_int) / OPB->_float; - } - else + if( OPB->_float == 0.0f ) { PRE_ERROR(); VM_Warning(prog, "Attempted division of %"PRVM_PRIi" by zero\n", OPA->_int); - OPC->_float = 0; + if (prvm_gameplayfix_div0is0.integer) + { + OPC->_float = 0; + DISPATCH_OPCODE(); + } } + OPC->_float = ((prvm_vec_t) OPA->_int) / OPB->_float; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_DIV_FI): - if( OPB->_int != 0 ) - { - OPC->_float = OPA->_float / (prvm_vec_t) OPB->_int; - } - else + if( OPB->_int == 0 ) { PRE_ERROR(); VM_Warning(prog, "Attempted division of %f by zero\n", OPA->_float); - OPC->_float = 0; + if (prvm_gameplayfix_div0is0.integer) + { + OPC->_float = 0; + DISPATCH_OPCODE(); + } } + OPC->_float = OPA->_float / (prvm_vec_t) OPB->_int; DISPATCH_OPCODE(); HANDLE_OPCODE(OP_CONV_ITOF): OPC->_float = OPA->_int;