|
Post by MaxED on Jul 31, 2017 15:46:48 GMT -5
void train_children_think(edict_t *self) { if (!self || self->enemy) return; //mxd. Shouldn't it check for !self->enemy?
if(self->enemy->spawnflags & TRAIN_ROTATE) { // The the train was changed from TRAIN_ROTATE_CONSTANT to TRAIN_ROTATE // by a target_change... get da hell outta here. self->think = train_yaw; self->think(self); return; }
if(self->enemy->movewith_next && (self->enemy->movewith_next->movewith_ent == self->enemy)) { set_child_movement(self->enemy); self->nextthink = level.time + FRAMETIME; } else if(level.time < 2) self->nextthink = level.time + FRAMETIME; }
|
|
|
Post by MaxED on Aug 1, 2017 7:15:19 GMT -5
"models\objects\grenade" model in kmquake2_extras.pk3 contains a lot of duplicate polygons. It can be reduced from 136 to 58 triangles without any visual changes.
|
|
|
Post by knightmare on Aug 5, 2017 13:17:20 GMT -5
I've considered putting up the stable branch of KMQ2 in a Bitbucket repo. However, the hassles I've had using such a repo with the Daikatana patch project have made me realize it's not worth it unless I'm actively collaborating with someone else on the project.
I got those bugfixes merged in. The issues found in KMQ2 outside the game DLL were in code adapted from Quake2Max, and had already been fixed in the development branch. That monster movement bug is also now fixed.
Could you possibly re-create that crane test map using an editor that outputs .map files compatible with Radiant? I'll need to play around with the map to figure out that disappearing cable issue.
As far as the grenade model goes, I didn't create it, but the extra polygons don't really matter performance-wise. The whole thing gets drawn at once with a single glDrawRangeElements call.
|
|
|
Post by MaxED on Aug 5, 2017 15:55:51 GMT -5
I've considered putting up the stable branch of KMQ2 in a Bitbucket repo. However, the hassles I've had using such a repo with the Daikatana patch project have made me realize it's not worth it unless I'm actively collaborating with someone else on the project. Don't know about Bitbucket, but GitHub + TortoiseGit combo works fine for me... Also, here's the crane test map in conventional map format.
|
|
|
Post by knightmare on Aug 6, 2017 0:52:50 GMT -5
Thanks for the .map file.
I was right about the entity order- putting the crane pieces in the order of beam, hoist, hook in the .map file fixes the movement issue.
I also added the Lazarus crane control panel texture to the crane_control entity. It turns out that with the way you built the crane, with the beam spanning north-south and moving east-west, the forward/backward and left/right controls are reversed. The crane_beam needs to span east-west and move north-south for the controls to work correctly.
I'm still trying to figure out why the crane cable is not appearing.
|
|
|
Post by knightmare on Aug 10, 2017 1:21:56 GMT -5
OK, I finally figured out why the crane cable wasn't appearing. It turns out that you need to have air space above the crane for the cable's origin point to spawn in. If the area right above the crane is a solid brush, the cable won't be visible to the client. Here's the fixed .map: markshan.com/knightmare/misc/crane_movement_bug_fixed.zip
|
|
|
Post by MaxED on Apr 19, 2018 5:02:53 GMT -5
Here are 2 bugs I ran into recently: 1. Mod_LoadMarksurfaces loads leafface indices as shorts, while they are stored as unsigned shorts in BSP, which can result in an overflow. 2. Fixing the first bug resulted in Hunk_Alloc overflow while loading the map. Increasing hunk size for IDBSPHEADER in Mod_ForName to 0x2000000 fixed the issue for me. Here's the .bsp, which triggers these issues. Here are the resources used by it, if you need them.
|
|
|
Post by knightmare on Apr 19, 2018 18:19:38 GMT -5
Changing the "*in" variable in Mod_LoadMarksurfaces() to unsigned short alone doesn't fix this. Did you also change LittleShort() to unsigned as well?
EDIT: OK, just adding a cast to unsigned short for LittleShort() did it:
void Mod_LoadMarksurfaces (lump_t *l) { int i, j, count; unsigned short *in; // FIXME: change to unsigned short? msurface_t **out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) VID_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = Hunk_Alloc ( count*sizeof(*out));
loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count;
for ( i=0 ; i<count ; i++) { j = (unsigned short)LittleShort(in[i]); if (j < 0 || j >= loadmodel->numsurfaces) VID_Error (ERR_DROP, "Mod_ParseMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } }
|
|
|
Post by MaxED on Jun 11, 2018 7:02:18 GMT -5
Here are some more bugs I ran into: #1. com_token overflow can happen in COM_Parse when parsing a quoted string longer than 128 chars:
// handle quoted strings specially if (c == '\"') { data++; while (1) { c = *data++; if (c=='\"' || !c) { com_token[len] = 0; //mxd. If data is longer than 128 chars, this will try to set com_token[128] to 0 *data_p = data; return com_token; } if (len < MAX_TOKEN_CHARS) //mxd. This stops adding chars when len exceeds MAX_TOKEN_CHARS limit and sets len to 128. On my end I changed this to "if (len < MAX_TOKEN_CHARS - 1)". Not sure how normal a more-than-128-chars quoted string is though... { com_token[len] = c; len++; } } }
|
|
|
Post by MaxED on Jun 11, 2018 7:13:46 GMT -5
#2. Mod_LoadSubmodels uses a fixed-size array to load models into. This turns into a problem when a map has more than 512 submodels, because:
1. There is no numsubmodels overflow check in Mod_LoadSubmodels, so this triggers CTD when the code in Mod_LoadBrushModel tries to set up the submodels using unrelated data. 2. If you add said check and use VID_Error() to report it, you'll still get a CTD in R_MarkLeaves, because it tries to access r_worldmodel->vis while r_worldmodel is NULL because of 1.
Here's the bsp, which triggers these issues. Also, can MAX_MOD_KNOWN be increased to 1024 (or even 2048) for the next release?..
|
|
|
Post by knightmare on Jun 12, 2018 22:34:35 GMT -5
Here's my fix for this COM_Parse() overflow:
// handle quoted strings specially if (c == '\"') { data++; while (1) { c = *data++; if (c=='\"' || !c) { if (len == MAX_TOKEN_CHARS) // Knightmare- discard if > MAX_TOKEN_CHARS-1 len = 0;
com_token[len] = 0; *data_p = data; return com_token; } if (len < MAX_TOKEN_CHARS) { com_token[len] = c; len++; } } }
I just discard the quoted string if it exceeds MAX_TOKEN_CHARS-1. COM_Parse() already does this at the end of the function for all other tokens:
if (len == MAX_TOKEN_CHARS) { // Com_Printf ("Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); len = 0; } com_token[len] = 0;
And here's my fix for the submodel overflow:
In r_model.c->Mod_LoadSubmodels():
void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; mmodel_t *out; int i, j, count;
in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) VID_Error (ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = Hunk_Alloc ( count*sizeof(*out));
// Knightmare- catch submodel overflow if (count >= MAX_MOD_KNOWN) VID_Error (ERR_DROP, "MOD_LoadBmodel: too many submodels (%i >= %i) in %s", count, MAX_MOD_KNOWN, loadmodel->name);
loadmodel->submodels = out; loadmodel->numsubmodels = count;
In r_surface.c->R_MarkLeaves() I already had a catch for a null r_worldmodel in my development version:
void R_MarkLeaves (void) { byte *vis; byte fatvis[MAX_MAP_LEAFS/8]; mnode_t *node; int i, c; mleaf_t *leaf; int cluster;
if (r_oldviewcluster == r_viewcluster && r_oldviewcluster2 == r_viewcluster2 && !r_novis->value && r_viewcluster != -1) return;
// development aid to let you run around and see exactly where // the pvs ends if (r_lockpvs->value) return;
if (!r_worldmodel) // Knightmare- potential crash fix return;
r_visframecount++; r_oldviewcluster = r_viewcluster; r_oldviewcluster2 = r_viewcluster2;
MAX_MOD_KNOWN will be increased to 8192 in the next update. Thanks for finding this, as it was one engine limit I missed!
|
|
|
Post by MaxED on Jun 15, 2018 8:51:34 GMT -5
Well, here's a fun bug: you've increased MAX_STATS from 32 to 256, so now the logic tries to read/store 256 bit flags from/to a 32-bit integer (see statbits in SV_WritePlayerstateToClient and CL_ParsePlayerstate).
|
|
|
Post by MaxED on Jun 15, 2018 9:29:06 GMT -5
Found in CL_ParseServerData:
// set gamedir if ( ( (*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)) ) //mxd. I guess this means "if fs_gamedirvar->string is not null OR it's not an empty string". && !cl.attractloop ) // Knightmare- don't allow demos to change this Cvar_Set("game", str);
|
|
|
Post by knightmare on Jun 15, 2018 11:16:05 GMT -5
The MAX_STATS thing has already been fixed in my development branch. But since it requires a protocol change, it won't be fixed in any 0.20 update.
However, the extended stats do work without this fix, as
1<<i
always evaluates to the same value if i > 31, at least when compiled with Visual Studio 2008. Thus ALL of the extended status slots get sent to the client.
In SV_WritePlayerstateToClient():
void SV_WritePlayerstateToClient (client_frame_t *from, client_frame_t *to, sizebuf_t *msg) { int i, j; int pflags; player_state_t *ps, *ops; player_state_t dummy; int statbitarray[(MAX_STATS+31)>>5]; // derived from MAX_STATS . . . . // send stats for (j = 0; j < (MAX_STATS+31)>>5; j++) statbitarray[j] = 0; for (i=0; i<MAX_STATS; i++) if (ps->stats[i] != ops->stats[i]) statbitarray[i>>5] |= 1<<(i&31); for (j = 0; j < (MAX_STATS+31)>>5; j++) MSG_WriteLong (msg, statbitarray[j]); for (i=0; i<MAX_STATS; i++) if ( statbitarray[i>>5] & (1<<(i&31)) ) MSG_WriteShort (msg, ps->stats[i]);
In CL_ParsePlayerstate():
void CL_ParsePlayerstate (frame_t *oldframe, frame_t *newframe) { int flags; player_state_t *state; int i, j; int statbits; // still used by legacy protocol int statbitarray[(MAX_STATS+31)>>5]; // derived from MAX_STATS . . . . // parse stats for (j = 0; j < (MAX_STATS+31)>>5; j++) statbitarray[j] = MSG_ReadLong (&net_message); for (i = 0; i < MAX_STATS; i++) if ( statbitarray[i>>5] & (1<<(i&31)) ) state->stats[i] = MSG_ReadShort(&net_message); } // end new CL_ParsePlayerstate code
Here's the original Q2 v3.21 code for that gamedir thing:
// set gamedir if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
So that's an original Q2 minor logic bug.
|
|
|
Post by MaxED on Jun 18, 2018 8:54:28 GMT -5
#1. It's possible to cause CTD in S_IssuePlaysound by performing rather specific actions: 1. Start the game. As soon as the loading plaque shows up, press Esc. 2. Wait for several seconds. 3. Press Esc again, wait for the demo to start loading, then press Esc again.
#2. Pasting via Shift-Insert doesn't work in Field_Key.
|
|