A level that fails to load at a level change or game start is unloaded completely and leads back to the main menu; the game loop restarted on the destroyed level before
The game loop leaves the frame as soon as no level is loaded any more (failed level change or savegame load inside the frame) instead of updating and rendering freed data
A game start that ends without a ship for the local player is cleaned up and returns to the main menu
A level without a start position or ship for the local player is refused with a log message and a network game is left; the player got an unrelated object as ship before
An invalid level number is rejected before the running level is unloaded; it was used as an index into the level name tables first
A secret level that cannot be loaded, or a failed return from it, ends in the main menu instead of continuing in a destroyed or half loaded level
A failed level or secret level start no longer leaves the network thread suspended
A secret level return segment outside the level is replaced by segment 0 instead of crashing on the return
The exit jump of the game (failed demo start, aborted network game start in the menu) no longer lands in a function that has already returned
After such a jump the open menus, automap, pause state and a suspended network thread are reset; the net game browser no longer doubles its entries after a failed join
Exit sequence: a missing, circular or one-way exit tunnel ends the level without the fly-through instead of crashing or hanging the game
Exit sequence: an .end file with fewer than 8 entries, an unusable level name or a bad terrain height map ends the level without the outside scene instead of crashing or exiting
A level file with an impossible segment or vertex count, vertex numbers outside the vertex table or side corners without a vertex is refused instead of ending the game while loading
A segment without any valid vertex no longer causes an endless loop when a random point in it is needed, and its centre is no longer divided by zero
An invalid segment owner in a level file is reset to "no owner"; in Entropy games it overwrote memory
A side without face that still has a neighbour segment: the neighbour's link back is closed now, not an unrelated side of the neighbour
Closing the level loading window with Escape no longer leaves the geometry half loaded; the remaining loading steps are finished
Particle and smoke emitters on sides of non-cubic segments take the side size from the side's own corners; random vertex numbers could end the level load
Wall and effect texture changes with texture, animation or frame numbers outside the game data tables are ignored or clamped instead of ending the game
The object, wall, trigger, door, reactor, robot maker and light counts of a level file are checked: a level with impossible counts is refused, surplus triggers are skipped
A level file that declares active doors no longer ends the game while loading
A level file with a negative model name count or an over-long level name, palette name or file name no longer overwrites memory
A level file with a bad section offset fails to load with an error message instead of ending the program
Level objects with a model number, animation clip or render type that does not fit their type are corrected; objects of unknown type or outside every segment are removed completely
Ship damage reset at level start, after a respawn and by the energy to shield converter acts on the player's own ship; the player number was used as object number
A team game on a level whose start positions all belong to the other team no longer picks the spawn point from an uninitialised table
The end of level score screen no longer divides by zero in a mission without secret levels
Multiplayer score table: players 9 to 16 get a valid colour and an invalid connection state shows a blank instead of reading outside the tables
The marker view of a cockpit window uses the same marker slots as the marker code (two per player)
A level load that fails for lack of memory for the light data no longer leaves the "between levels" state set
The frame timer is created without a memory allocation; a failed allocation ended the game
Linux: the frame limiter uses a monotonic clock; setting the system clock back froze the game for that long
Exit and secret exit triggers only request the level change, which is carried out at the end of the frame instead of inside trigger, collision or blast code that then used the freed level
Once an exit trigger has requested the level change, the players take no more damage until it is carried out; a player killed in the rest of that frame would have lost the exit
Descent 1 triggers and delayed or auto-play triggers no longer write to the trigger after it started a level change, and no further triggers are processed in that frame
A secret exit trigger in a mission without secret levels (also a level started from the command line) is ignored; before, it destroyed the running level while trying to load level 0
Object triggers of type shield damage or energy drain use their own value; before, it was looked up in the wall trigger table (crash, or the value of an unrelated wall trigger)
Trigger cascades through master triggers end at a depth of 16; the 17th level wrote behind the cascade stack
Triggers from savegames and the multiplayer join sync are validated like level triggers: at most 10 targets, none with an unknown segment, a bad side or an object where none is allowed
Triggers of old level formats get the same target check, and the reactor's target list is checked as a pure wall list (the old check read a trigger type a reactor list does not have)
The player number stored with a trigger in a savegame or join sync is limited to valid players
Teleport, speed boost, orientation and robot spawn triggers do nothing if the object that operated them no longer exists (e.g. a robot that died during the delay) instead of crashing
Texture numbers outside the texture table are refused: a change texture trigger leaves the side alone, a savegame keeps the level's texture; before, the next texture lookup ended the game
Wall records from savegames and multiplayer wall updates are sanitised: a place outside the level keeps the level's segment and side, an unknown animation or trigger number becomes "none"
At level load every wall takes its segment and side from the side that refers to it, for all level versions; a wall no side uses whose stored place lies outside the level is disabled
The wall count of a level is limited to the highest wall number the level format can address
Exploding walls: a record for a side without a wall or without an animation (savegame, network) is removed, and its time is limited to 0..1 - a huge time froze the game in the explosion loop
Active doors and cloaking walls from savegames or other players are checked: a missing wall removes the record, door parts are limited to 2, negative times become 0, frames stay in range
A triangular side (level version 25+) is tested for visibility with its three corners only; reading the missing fourth corner ended the level load with an exception
Corner bytes of a side in a level file are validated: values above 7 count as "no corner" and valid corners are packed to the front; a side without corners no longer divides by zero
A level with object triggers but no wall trigger no longer fails to load (their table was only created with wall triggers); object numbers of object triggers are limited to the object table
Wall animations in the game data: the frame count is limited to the table size and frame texture numbers outside the texture table are reset
Robot makers and equipment makers whose producer number lies outside the producer table (savegame) are skipped when they are triggered or their states are set
Animated textures: effect data from the game data is sanitised when loaded - negative frame times (endless loop), frame counts above 30, frame, texture and effect numbers outside their tables
After the reactor is destroyed, the critical animation of an effect with a hires override maps its frame to the clip's own frame list instead of reading behind it
A wall effect whose multi frame bitmap has no frames set up is skipped, and an effect that ends checks segment and side before it writes the destroyed texture
The list of walls a destroyed reactor opens is cleared before each level's list is read; a level without such a list no longer uses the segment numbers of the previous level
Segment object lists survive a wrong segment number: unlinking verifies the neighbours and finds the real list head, linking first unlinks an object that is still linked
Releasing a guided missile whose parent is not a player (robot, level object, stale number) no longer writes behind the guided missile table, which wiped the console and viewer pointers
An object with an unknown control type (level file, savegame, object sync) gets no control and is logged; before, the game ended with an error box
A weapon rejected as invalid in a multiplayer game no longer aborts the object update of the whole frame; only that weapon dies
Attached object lists (e.g. fireballs on an object) from savegames or object sync are followed with checks and a step limit; missing objects or circular links no longer crash or hang the game
Powerup ids outside the powerup table (network packet, level file) are refused by the multiplayer powerup counters and by object drops instead of writing outside the tables
A destroyed object with a negative contents count no longer drops objects until the object pool is full; counts above 127 are limited
Robot types beyond the robot table (level file, robot data, network packet) are refused: no such robot is created or gated in by a boss; before, data behind the table was used
An object whose position lies in no segment (non finite or far outside the mine) no longer crashes when its segment is updated or repaired after loading
Smoke emitters: a side number outside the segment's sides counts as no side, and speed or density values of 0 or less use the default; such level values could crash the level load
Setting a player's shield (savegame, network) copes with a ship object number that names no object
A local player number or ship object number outside the valid range (savegame, network sync) is replaced by 0 instead of producing a wild pointer to the player's ship
The object pool is limited to 32767 objects, the highest value the 16 bit object numbers can hold; levels with more than 16383 objects produced negative object numbers
Releasing an object number that lies inside the pool but names no object no longer crashes
Missing player or parent objects are tolerated by the spawn effect, the count of a player's mines, powerup drops, the death camera and fireballs or animations placed on an object
With a nearly full object pool, creating an object no longer deletes all dying objects on the spot (callers went on using released objects); that is left to the start of the next frame
After the death sequence the saved viewer is only restored if it is still the same object; otherwise the player's ship becomes the viewer instead of a released camera
Drive damage is read from the player's own ship and the cheats that reset ship damage reset that ship; before, the player number was used as an object number
A destroyed reactor whose wreck object no longer exists is skipped each frame instead of being dereferenced
Reactor data is bounded: at most 7 reactor types are stored from the game data, gun counts are limited to 8, and a reactor object with an id outside the table becomes type 0
A level with more than 50 reactors no longer overflows the reactor state table; the surplus reactors are ignored and reported in the log
Deleting a marker no longer crashes or removes another player's marker for player numbers above 0; the automap now selects markers by absolute slot, so every player can delete his own
Marker data from a savegame is checked: numbers that name no marker object are dropped and texts are terminated; rotating, deleting and teleporting to a marker verify the marker first
Hostages: an animation number outside the table is not drawn and a frame number outside the frame list shows frame 0 instead of crashing the renderer
Monsterball: the stored ball pointer is validated before use and cleared when the ball object is released, e.g. when another player removes it
Robot segment changes are only deferred during the parallel robot pass and applied right after it; changes queued elsewhere stayed pending, even into the next level (crash after an exit)
A deferred segment change is only applied to the object it was queued for, and a boss teleport no longer meets a pending change, which cross-linked the object lists of two segments
Claw attacks decided on AI worker threads hit the target (damage, energy drain, explosion) on the main thread right after the parallel pass instead of from several threads at once
Multiplayer: claiming a robot and sending its position or fire message from AI worker threads is serialised with a lock; two threads could write the shared message buffer at the same time
A negative difficulty level in a player profile is corrected after loading; it indexed all difficulty tables and could overwrite the stack when brain robots assigned squad roles
Savegames: segments in a robot's memory (last seen, goal, cloak records) that do not exist in the level become unknown instead of crashing the path finder
The route search rejects segments that do not exist: robots whose hide segment lies outside the level (Descent 2 levels 3 and 7, Descent 1 level 16) crashed the game when resuming their path
Path length queries reject unknown segments, and the sound portal router answers "no path" for an unknown listener segment instead of crashing or using an older listener position
Noises still queued at a level change are discarded at the next level start; robots of the new level reacted to them and could be sent to a segment that does not exist
When path points are moved away from the walls and one lies in no segment, only the points computed so far are copied; before, stale points of other paths ended up in the robot's path
Robots without a valid segment are skipped when noises are spread and awareness is assigned (table access out of range)
Spreading a noise through the mine ends even if a segment distance is not a number (segment with all vertices collapsed); before, it looped until memory ran out
Robot cloak tracking, the boss teleport and the claw attack cope with a missing local player ship
Robots scanning their neighbours (fighting neighbour, moving away from or around other robots) stop at a missing object in a segment's object list instead of crashing
Guide-bot and thief messages are no longer used as format strings; a % in a marker text or in the guide-bot's name crashed the game when the guide-bot spoke
The guide-bot's "ouch" message no longer overruns its buffer with longer texts (e.g. the German one)
Cheats: the value prompt (segment warp, level warp) no longer writes one byte behind its input buffer, and the multiplayer cheat penalty message is limited to its buffer
The cheat that kills everything skips wall records without a valid wall, segment or side instead of crashing
The collision test walks a segment's object list safely: it stops at a missing object or after as many steps as there are objects and skips slots that hold no valid object type
An object of an unexpected type (e.g. reactor, flare, marker, camera robot) that moves into a wall no longer ends the game with an error box; it simply gets no wall reaction
A model number beyond the model table (level file, savegame, object sync) counts as "no model" for the collision code instead of being read behind the table
Thrust on an object with mass 0 (robot data) no longer turns its velocity and orientation into NaN; such an object is not accelerated
Flying into a marker whose id lies outside the marker table (level file, savegame) no longer reads behind the marker messages and the player table
The sphere against walls test stops at 1000 visited segments and answers "intersects"; before, it wrote behind its list and restarted it, which removed the limit of the search
Hit box collision tests use one buffer per thread; robots thinking in parallel overwrote and reallocated a shared buffer
The transparent pixel test of wall textures is guarded by a lock and checks the texture number; it can load textures and use the shared RLE cache from AI worker threads
Multiplayer: an omega blob whose firing object is already gone no longer crashes the omega damage check
The chase camera path copes with a missing local player ship
Earthshaker and seismic shakes only rock the guide-bot if it still exists, and its stored object number is cleared when it is released; after its death the shake wrote to a missing or foreign object
Gun points: the clamped gun number is handed back to the caller and the model number is checked; the missile lock of gun 7 weapons and remote missile fire read behind the gun point table
Hoard and entropy orbs created by firing a proximity mine are only added to the network create list while it has room (40 entries); further entries overwrote memory
Selecting a weapon number outside the weapon range (savegame) selects weapon 0, and an unknown weapon in the auto-select order no longer ends the game with an error box
The options homing update rate, missile start speed and omega ramp are limited to their tables at every use; unchecked values from a profile or the host indexed behind the tables
A laser rendered object with an invalid weapon type or render type is not drawn; before, the game ended with an error box as soon as it came into view
Creating a shot for a weapon with an invalid render type or without weapon data fails without ending the game; an invalid render type is reported once when the weapon table is loaded
A weapon with a fire delay of 0 or less no longer hangs the primary fire loop (minimum 0.04 s), and a fire count of 0 no longer breaks the energy calculation
Weapon and powerup tables: game data announcing more than 70 weapon or 50 powerup types no longer writes behind the tables; surplus records are skipped and reported
Weapon data: flash, robot hit and wall hit animation numbers outside the animation table become "none"; the weapon's own animation and a powerup's animation number become 0
Powerup animations: a clip without frames no longer divides by zero, a frame number outside the clip is reset, an invalid add-on bitmap number is ignored, an out of range frame draws the first frame
A remote player firing an invalid weapon number no longer resets the local player's selected primary weapon
A message box opened while a packet was handled (e.g. the kick message) re-entered packet processing and crashed or hung; packets now leave the queue first, no lock is held
Packets already fetched for processing are discarded when the network thread starts or stops, so none is handled after its game has ended
The network thread is stopped at program exit; quitting while it ran (e.g. Alt+F4 in the game browser or in a network game) ended in a crash report
A rejected datagram (too short, wrong signature, own address, source 0.0.0.0) no longer ends the read pass; the datagrams behind it are read, up to 64 rejects per pass
The log line for an oversized datagram is written once instead of once per datagram
A datagram flood can no longer keep the receive loop busy and block the game: at most 1000 datagrams are read per pass and at most 10000 packets are queued
The sender tables are limited (1000 tracked senders, 1024 known clients); packets of further senders are still delivered, only their loss statistics are skipped
Without background listening a received packet now sets the address used for the answer; answers could go to the sender of an older packet
Life signs, player timeout checks and keep alive pings are skipped while the local player number is not valid
Multicast games: a socket that cannot be opened is reported in the log; the message box that was shown while the socket was locked is gone
Multicast games: the socket is non blocking on Windows and Linux, so a read can no longer block with the socket locked and hold up all sends
Multicast games: when the aux data of a game cannot be used (e.g. protocol version mismatch), the browser returns to the game list after the message instead of joining anyway
Multicast games: leaving a game without a group address is skipped instead of asserting
Windows multicast: the Linux only format '%m' was removed from an error message
Linux: a socket that is closed (or whose descriptor is too large for select) is no longer polled
Linux: clients are told apart by their full address; the first clients of a Linux host were matched by subnet and port and could overwrite each other
Aborting the host's 'waiting for players' box no longer jumps into a function that has already returned; game browser and game loop save and restore the exit point
A sync packet that does not list the local player (client or host) is rejected before player data is overwritten; the local player number no longer becomes -1
Leaving a game, player timeouts, pings, frame packets, end level packets and the net game help are guarded against an invalid local player number
Two players joining a running game at the same time could get the same player number; a number held by a running sync is not handed out again, 'new player' is kept per joiner
A join request that maps to the host's own player number is dropped instead of syncing the joiner into the host's slot
The join test and the search for the local player clamp the player count of the game info before they walk the player list
Game browser: a game that reports more players or player slots than the game supports is refused
Game browser: a game whose level number is out of range is refused with a message instead of crashing in the level name table (this includes games that are in a secret level)
Game browser: key U on a row without a game no longer requests the player names of a game that is not there
Game browser: shortening a long game or mission name no longer writes behind the text buffer when '...' is appended
Net game info (key I): an invalid game index is ignored in all builds, the 'game running' state is kept, and the browsing flag is restored after the player names box
Player ranks received from a host are clipped to the valid range (game browser, net game help, sync packet); an invalid rank crashed the net game help
Lobby: a network state changed by a foreign extra game info packet is set back to 'starting' (was an assertion; join requests were filtered out)
Object sync packets are only accepted while this machine waits for its join sync; during play such a packet reset the object table of the running game
Object sync: an owner outside the player range aborts the sync (start marker) or counts as 'no owner' instead of becoming the local player number
Object sync: objects with an invalid type, player id, robot type or powerup id are dropped; attach links are cleared and robot owners outside the player range are reset
Object sync: a failed object ends the pass instead of parsing on in the middle of that object
After the object sync the way point table is rebuilt, as after loading a savegame
Monitor sync on join: sides whose overlay texture or effect clip lies outside the tables are skipped by host and joiner alike (the host terminated on such a texture)
Multicast games: the rejoin sync fills only the 8 player rows that exist; 16 rows destroyed the host's check sum, score goal and play time
Restricted games: a join request with an unterminated callsign no longer overflows the 'wants to join' message and the stored player name
Restricted games: accepting a joiner for whom no player number is free no longer runs the team assignment with a negative number
Banning a player whose callsign is not terminated no longer overflows the ban list entry
A server address of 22 or more characters (e.g. -ng_server) no longer overruns the buffer of the address parser
Frame packets are built without dereferencing a missing local ship object
Mission download: one connection attempt per request instead of a retry loop that froze the game for up to 30 seconds
Mission download: an incomplete packet no longer blocks the game in a receive loop; the download waits for the rest and its timeout ends it when the host is gone
Mission download: a long or unterminated mission name no longer overflows the title of the download dialog
Mission download: upload threads left from an earlier hosted game no longer overwrite the packet that is being received
Mission upload: the host only joins upload threads that have finished; a stuck upload or a lost cancel request froze the host. Uploads have their own cancel flag now
Mission upload: a cancelled upload opens no further file and sends no further data
Mission upload: a running upload keeps its own copy of the listening socket, which a mission download on the same machine cleared under it
Mission upload: running uploads are cancelled and awaited (at most 250 ms) at program exit and before the next game starts; unjoined upload threads aborted the game at exit
Mission upload: the listening socket is closed when the last upload slot is released at game start; it was dropped without being closed
netMsgCreateRobotPowerups is 60 bytes long in UDP games (the length table said 59 and cut the last byte of the 15th object number); the multiplayer protocol version is 12
Being kicked: handling of the kick packet ends once the game has been left instead of going on in the player list
Kick / dump packets with an unknown reason are ignored; the reason text is no longer used as a format string and the kick message is length limited
Extra game info is checked for its version and (outside menu and game browser) the game's security number before it is copied; a foreign version no longer ends a running game
Extra game info: settings used as table index or divisor (homing turn speed, missile start speed, omega ramp, recharge speed, coop penalty, speed scale and others) are clamped
Extra game info for an unknown game no longer clears memory behind the game list when the list is full
Player list and sync packets with a wrong security number are dropped before they are copied
A sync packet arriving during play is only applied when its check sum matches and it lists the local player exactly once
'Add player', end of level and player data packets naming the local player are ignored; they overwrote the own slot, connection state, score or ship position
Callsigns, game, mission and team names are terminated where packets arrive (player data, player list, sync, game info, lite info); the net game help prints them length limited
Game info updates during play: the player counts are clamped to the maximum
Player data with a non finite position, velocity or rotation leaves the ship where it is; robot fire and robot drop messages with such values are dropped
Robot maker and boss gate messages naming an unknown robot type are ignored before any effect is created
A robot drop message whose contents are neither a powerup nor a known robot type is ignored
Sending a robot position checks the robot's control slot number, which can come from a synced object, before it indexes the control tables
Guided missile updates check segment and vectors and relink the missile to its segment; setting the segment alone corrupted the segment object lists
Player explosion messages apply position and segment only when valid and relink the ship instead of only setting its segment
A trigger message naming an object that does not exist on this machine is ignored in UDP games instead of crashing in the teleport code
Trigger state messages: the targets are validated, player and object numbers outside their range are reset to 'none'
Wall status messages only update hit points, type, flags, state, keys and cloak value; segment, side, linked wall, trigger and clip remain those of the level
Door open messages only open doors (besides blastable walls) and take over only the 'opened' and 'locked' flags
Exploding wall messages need a finite time of at least 0 and a side whose wall has an animation; cloaking wall messages a finite time of at least 0
Light messages: a texture number outside the texture tables skips that side
A 'reactor destroyed' message must name a reactor; it could blow up any object, the own ship included
Messages naming the local player as their subject (quit, escape, explode, kill, fire, score and 14 more types) are ignored; a forged quit left the player stuck as a ghost
Fire messages are only accepted for player ships, valid primary weapons and laser levels; an invalid weapon number reset the local primary weapon
Kill messages whose killer player has no ship object are ignored
Position messages check the player number, the object type and all values before anything is applied
Weapon state messages with an invalid primary or secondary weapon are ignored
Create weapon messages only accept proximity and smart mines, with validated vectors and an existing local object
Secondary ammo counts from weapon and player stats messages are limited to twice the maximum (orb and virus counts in hoard and entropy games excepted)
Sound messages with a sound number outside the sound table or a non finite volume are ignored
Non finite numbers (positions, velocities, times, energy, shield) in countdown, powerup, marker, monsterball, shield, heartbeat, seismic, fusion and other messages are ignored
Marker drop messages are ignored while the local ship does not exist; no game message is processed while the local player number is invalid
Chat: the 'move:' and ping commands end the message input on every path; after a refused 'move:' every further key wrote past the 35 byte message buffer
Chat: characters are only appended while the buffer has room, and the line wrap copies overlapping text correctly
Chat: the handicap message shows the shield value (a float was passed to an integer format)
Team change message: built with a length limit, it overflowed the chat buffer; the German text has a valid format now
Kill messages are built with a length limit (unterminated callsigns or team names overflowed the stack); the marker owner gets a terminated copy of the callsign
The 'only player' HUD message is built with a length limit and no longer used as a format string
Confirming a message no longer overwrites the message buffer: capture bonus, orb bonus and play by play now count on the scorer's own machine for every player number
A new game clears the whole score matrix; in non UDP games only four of its rows were cleared
The kill list is sorted and copied for at most 16 players, also when the player count read from a demo is higher
Powerup respawn: a pass stops at the first powerup that cannot be dropped instead of trying up to 65535 times per type (frame stall)
Removing excess powerups only takes powerup objects; it could delete a marker, hostage or effect object with the same id
Entropy: segment owners above 2 from a level file are skipped in the winner check and the texture change (stack overwrite every frame)
Monsterball sync: the position is read and validated first, so the ball is created where the message says and not at a stale position
A failed multiplayer savegame restore loads no level resources, and the rest of that packet is not processed
Level start: viewer and console object are only set when the local player's ship exists
Changing the local player number ignores numbers outside 0..15