When the lot being bid for changes while the player is at the "How
much" prompt, we report "Commodity #%d has changed!", and continue
with the changed lot.
If continuing is okay, we should keep quiet. We did that until commit
40b11c098 "Fix buy not to wipe out concurrent updates", v4.3.27. Okay
when only the lot's price changed.
However, the lot could have gone away, or even be reused for something
else. Failing the command seems safest for the player, so do that.
It's how we use the check_FOO_ok() elsewhere.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
To find out whether a lot is in use, some places check for zero
trd_owner, others for negative trd_unitid. The former is reliable,
the latter is not: set() fails to change trd_unitid when it takes a
lot off the market. The next trade-related command then runs
check_trade(), which logs "Something weird" and cleans up the mess.
Broken in commit e16e38dfab (v4.2.18).
Replace the unreliable checks by reliable ones.
Clean up set() not to implictly rely on unused lots having negative
trd_unitid.
The trd_unitid = -1 are unnecessary now, so drop them.
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
Lots stay on the market until there's a bid and bidding time expires.
When the highest bidder changes, and less than five minutes of bidding
time are left, it gets extended by five minutes (since 4.0.7, actually
works since 4.0.9).
Normally, this ensures that the competition has at least five minutes
to react. Except when this is the first bid, bidding time may have
expired already. If it expired less than five minutes ago, the
competition still gets time to react, just less than it should. If it
expired earlier, the sale is executed immediately for units. For
commodities, the bidding time is set to expire in five minutes (since
4.2.0).
Instead of extending bidding time by five minutes, set it to expire in
five minutes, both for commodities and for units.
buy() reads the lot, prompts for input, then writes back the lot,
triggering a generation oops. Any updates made by other threads while
buy() waits for input are wiped out, triggering a seqno mismatch oops.
Since commodities are taken from the seller when he puts them on the
market, and given to the buyer when the trade executes, the wiped out
lot's seller loses his goods without compensation, the other seller
gets to keep his goods, and the buyer receives their duplicates.
This can be abused by two conspiring countries to duplicate
commodities. The seller puts them on the market (say 100 gold bars).
The buyer starts a buy command, and waits at its last prompt for the
lot to be replaced. The seller takes them off the market (possible,
since there's no bid, yet), and sells something else (say one food)
quickly enough to get the same lot number assigned. The buyer then
completes the buy command. The seller loses one food, the buyer gains
100 gold bars.
Replaces a partial fix from v4.0.1, which only caught lots gone away,
not lots replaced by new ones.
Why upgrade? I'm not a lawyer, but here's my take on the differences
to version 2:
* Software patents: better protection against abuse of patents to
prevent users from exercising the rights under the GPL. I doubt
we'll get hit with a patent suit, but it's a good move just on
general principles.
* License compatibility: compatible with more free licenses, i.e. can
"steal" more free software for use in Empire. I don't expect to steal
much, but it's nice to have the option.
* Definition of "source code": modernization of some details for today's
networked world, to make it easier to distribute the software. Not
really relevant to us now, as we normally distribute full source code.
* Tivoization: this is about putting GPL-licensed software in hardware,
then make the hardware refuse to run modified software. "Neat" trick
to effectively deny its users their rights under the GPL. Abuse was
"pioneered" by TiVo (popular digital video recorders). GPLv3 forbids
it. Unlikely to become a problem for us.
* Internationalization: more careful wording, to harden the license
outside the US. The lawyers tell us it better be done that way.
* License violations: friendlier way to deal with license violations.
This has come out of past experience enforcing the GPL.
* Additional permissions: Probably not relevant to us.
Also include myself in the list of principal authors.
other. Ensure headers in include/ can be included in any order
(except for econfig-spec.h, which is special). New header types.h to
help avoid inclusion cycles. Sort include directives. Remove some
superflous includes.
undocumented feature: When a buyer didn't have enough money at the
time of the trade, the seller *automatically* granted a high-interest,
medium term loan for up to 90% of the price. This is just a silly
trap for unwary sellers and buyers alike.
(N_FIN_TROUBLE, N_CREDIT_JUNK): Unused, remove.
(rpt): Update accordingly.
I_BAR, I_FOOD, I_OIL, I_LCM, I_HCM, I_UW, I_RAD, I_MAX): Turn macros
into enumeration constants.
(i_type): New. Use where appropriate. No functional changes, except
that I_NONE is now catched properly in a few places.
member com_type from mnemo character to item type.
(whichitem): Unused, remove.
(check_market): Use full item name instead of mnemo in telegrams.
(display_mark): Separate arguments for item type and cheapest only.
Cheapest only of specific item type is not implemented.
(rese): Guard against bad com_type. Delete some code that had no
effect.
display_mark() to callers. buy() no longer accepts "all". It used to
display all lots, but didn't let you buy any. Similar nonsense
happened for "" if buy() prompted for it.
(display_mark): Fix size of cheapest_items[].
(player_coms): Document mark accepting "all".
them. From Marc Olzheim.
Type modifier 'l' was missing in many places, probably rendering the
server useless on 64-bit hosts.
(ef_flush, ef_write, ef_extend, lwpCreate, lwpDestroy): Use conversion
specifier 'p' to print pointers.
(check_market): Fix display of loan amount.
(doland): Fix unescaped '%' (undefined behavior).
(ldump, ndump, pdump, sdump): Don't use flag '0' with conversion
specifier 's' (undefined behavior).
(dump, ldump, lost, ndump, pdump, sdump, empth_create, update_sched):
Cast time_t and pthread_t to long for printing.
(lwpStackCheck, lwpStackCheckUsed, finish_sects): Insert cast to fix
argument type on all platforms.
(prod): Remove extra argument.
(perform_mission, airdamage, retreat_land1, lwpReschedule): Format
string missed arguments.
(give, deliver_it): Use it instead of 9990.
(load_comm_ship, load_comm_land, rese): Use it instead of 9999.
(thre): Use it instead of 10000.
(check_market, explore, move, pln_dropoff): Use it instead of 32767.
(unload_it): Use it instead of 99999 (which couldn't possibly work,
but what do you expect from the autonav code).
To save space, the ancients invented `variables': a collection of
key-value pairs, missing means zero value, space for `enough' keys.
This complicates the code, as assigning to a `variable' can fail for
lack of space. Over time, `enough' increased, and for quite some time
now `variables' have been *wasting* space. This changeset replaces
them, except in struct mchrstr, struct lchrstr and struct pchrstr,
where they are read-only, and will be replaced later. It is only a
first step; further cleanup is required. To simplify and minimize
this necessarily huge changeset, the new item[] arrays have an unused
slot 0, and the old variable types V_CIVIL, ... are still defined, but
must have the same values as the item types I_CIVIL, ...
(genitem_ca, ship_ca, plane_ca, land_ca): Remove selector "sell".
(comstr, trdstr): Members trd_price, com_price have no effect. Use
them instead of trd_maxprice, com_maxprice and remove the latter.
(commodity_ca, trade_ca): Remove selector "maxprice".
instead of declaring them all over the place. This uncovered type
errors:
(s_p_etu, adj_update): Defined long, sometimes declared int. Kills
big endian machines where sizeof(long) != sizeof(int). Change to
int.
(set_option, delete_option, optstrset, intset, floatset, doubleset,
longset, optionset, optiondel, worldxset): Change linkage to static.