The code wrote the swept sector after calling shp_check_one_mines().
This failed to use up the mine that hit the minesweeper, and triggered
a seqno mismatch oops.
The code wrote the minesweeper after calling shp_check_one_mines().
This used freed memory when the minesweeper got sunk there.
Broken in 4.0.17. Fix by moving both calls before
shp_check_one_mines().
(cherry picked from commit b0644e822c)
This happened when fire command failed becayse the gun lacked shells,
and when other ways to fire failed because the gun was inefficient,
embarked, lacked mil, guns or shells.
Broken in commit b8bdc32b, hvy-metal-2.4.
ask_olist() let non-light land units board ships that can carry only
light units. If the board succeeds, the non-light unit move onto the
ship and then are stuck there.
(cherry picked from commit 6d38a04930)
Work percentage should have been documented in commit 233fce87,
v4.3.0.
Empire clock should have been documented in commit d3e0597f, v4.3.10.
(cherry picked from commit 4a67080656)
perform_mission() needs to know whether it is targeting ships or
something else, because the rules differ: submarines interdict only
ships, land units get their damage reduced when interdicting ships,
and different news are generated.
The old code assumed it was targeting ships when the target sector was
sea. Wrong when interdicting ships in harbors, bridges and such.
This has always been broken. Except when checking a submarine's
target: there it tested argument s, which is gross, but at least it
works. That code was added in v4.0.8.
Replace the broken test by the gross hack everywhere. This fixes news
and damage from land units when ships get interdicted in non-sea
sectors.
(cherry picked from commit 3e251b474f)
Commit 092a52f2 (v4.3.4) removed the code to estimate defense, because
the use of the estimate had been disabled since v4.0.0. This
accidentally removed the reporting of defending units, because
get_dlist() reported them when called for an estimate, and not when
called for real.
Fix by removing the unused estimate capability from get_dlist(). It
now reports defending units always.
(cherry picked from commit 64f44e9904)
Before s_commod() attempts to recursively supply a supply unit it
wants to use as supply source, it zaps the unit's load. When
actually_doit is false, it later restores the old load by overwriting
the change with a saved copy of the unit. That triggers a seqno
mismatch oops.
Avoid that by copying the new sequence number to the saved copy.
(cherry picked from commit aacd0fb754)
Change supply_commod() and try_supply_commod() not to call s_commod()
when zero units are wanted.
This isn't just for efficiency, it's also for limiting exposure to
supply bugs a bit.
(cherry picked from commit 7f17369491)
The value of diffx() had the wrong sign when the arguments differed by
WORLD_X / 2. Same for diffy() and WORLD_Y / 2. satmap() used them to
find the vector from map center to ship or land unit to put on the
map, and got incorrect values for ships and land units directly
opposite to the center in x or y. The bug made satmap() read a
pointer out bounds of its malloced radbuf[], and then write through
that with unpredictable consequences.
Broken in 4.2.12. The original bug was in Empire 1.1: it
miscalculated where to put ships on the map (no crash). An incomplete
fix for radmap() and satmap() appeared in Chainsaw 2 (still no crash).
radmap() got fixed correctly in Chainsaw 3, but satmap() was
forgotten. That one got "fixed" in 4.2.7, and again in 4.2.12, but
both "fixes" were flawed and could crash.
Fix by backing out the flawed fixes and adopting the fix from radmap()
instead.
(cherry picked from commit 0cc474bd6d)
Broken in commit 5f764285 (v4.3.12) for negative multiples of WORLD_X
and WORLD_Y, respectively.
This could theoretically lead to buffer overruns and other
unpleasantness. None have been reproduced, though.
(cherry picked from commit 7680acc39f)
load_land_ship() and load_land_land() automatically resupply the land
units they load. This can draw supplies from the sector where the
land units are. When load() and lload() later update the sector, they
wipe out the update made for drawing supplies, and we get a seqno
mismatch oops. Highly abusable.
navi() reads the ships into a list. When the ships get interdicted,
and lack flak shells, ac_shipflak() tries to get one and updates the
ship. When the ship in the list is later written back, e.g. to apply
interdiction damage, the flak shell is wiped out, and we get a seqno
mismatch oops.
Disable automatic flak shell supply in ac_ship_flak() for now. This
is related to commit f7d61817, which disabled automatic supply of
shells in ac_doflak() and shp_missile_defense().
Because supply_commod() updates supply sources it used, the caller
must not cache objects that could be supply sources across a supply
call. This is very easy to get wrong.
ac_doflak() supplies flak shells if the sector hasn't enough for its
guns. It caches the sector that receives them. If the sector has
some shells, but not enough, it supplies them to itself, causing it to
be updated from within supply_commod(). ac_doflak() then adds the
supplied shells to its cached sector, then writes that back. This
doubles shells already there, and triggers a a seqno mismatch oops.
shp_missile_defense() has similar problems, only for ships.
Disable ac_doflak() and shp_missile_defense() for now, to at least
reduce the oopsing to manageable levels.
Most likely other calls of supply_commod() are also wrong. Many of
them can't be just disabled, because supply is too relevant to
gameplay there.
Storing track in sectors is problematic, because we need to update
adjacent sectors when updating a sector in a way that changes its
capability to extend railway into its neighbors. This invalidates
cached adjacent sectors, and calling code may not be prepared for
that. Specifically, bridge building caches the bridge head, and
writes it back later, wiping out the track update.
Replace struct sctstr member sct_track by new sct_rail_track(). Make
selector track virtual. Remove the code to keep sct_track up-to-date:
set_railway(), update_railway().
Unfortunately, this causes cyclic dependencies between link libraries:
the virtual selector needs to be referenced from src/lib/global/nsc.c,
and it needs to reference stuff from src/lib/common/file.c. Hack
around it in Make.mk for now.
We don't want to starve tiny populations, because that would require
players to move trivial amounts of food after explore and such.
growfood() used to simply grow at least 1f when a sector was about to
starve. That food is almost never eaten by a tiny population, so we
effectively got some production without work. Fix by taking away that
free food after people ate, in do_feed().
This catches output dependency violations, e.g. two threads doing a
read-modify-write without synchronization.
New struct emptypedstr member seqno. Make sure all members of unit
empobj_storage share it. Set it in ef_blank() and ef_set_uid(), step
it in ef_write(). fairland and files don't use ef_set_uid(); need to
set it manually in files.c's main() and file_sct_init().
Factor do_read() out of fillcache() to make it available for
new get_seqno().
A sector type's terrain (struct dchrstr member d_terrain) is the
sector type of its underlying terrain. Sector types occuring in
d_terrain are terrain types, and must have their own type in
d_terrain. Players can change sector types only to those with the
same terrain.
The builtin configuration defines terrain types sea, mountain,
wasteland, wilderness and plains. It gives bridge span and tower
terrain sea, and everything else terrain wilderness. Hence, the stock
game remains unchanged.
Deities can use terrain to create sector types that can be developed
only in limited ways.
multifire() clobbered any changes to the target ship or sector made by
defend(). This let the target fire back for free.
multifire() retreated the target ship before reporting its location to
the player. This disclosed its new location.
Fix by damaging and retreating the target after calling defend().
multifire() drops depth charges if the target is a submarine, else it
fires guns. It fails if the target is out of range. But players
could still find out whether the target is a sub then, because depth
charge shell use differs from gun fire shell use. This loophole
existed before 4.0.6, and was reopened by commit a3ad623b (v4.3.12).
Change multifire() to always use guns if the target is out of range.
While there, treat failure from shp_dchrg() and shp_fire() the same,
so that the player can't distinguish the two cases. Failure there
should not happen.
Commit a3ad623b (v4.3.12) made depth charging fail when there's just
one shell. This let players find all submarine uids. It basically
reopened the loophole closed in commit aa26c53e (v4.2.20).
Fix by making shp_dchrg() with just one shell succeed and return
damage like fire from one gun.
multifire() failed to take into account that the firing firing sector,
ship or land unit can change while it is getting the target argument.
It thus clobbered any updates made to the firing object while it was
sleeping for the target argument. Abusable. Broken when Chainsaw
introduced MULTIFIRE.
The latter is necessary to interpret the journal correctly. The
former isn't, as it should always lead to a logout straight away, but
treating it just the same is simple and doesn't hurt.
Behavior differs for the following scenario: if, while the thread
sleeps in io_input() called from recvclient(), at least one line of
input arrives and the thread gets awakened by the update aborting
commands, then the old code throws away the first line of input, but
the new code doesn't.