The various bombing functions silently skipped planes not carrying
bombs. This sanity check was wrong: it checked capabilities "tactical
or not cargo" instead of "tactical or bomber", and failed for
non-tactical cargo bombers. No such planes exist in the stock game.
The broken check comes from Chainsaw; it replaced an equally wrong
"not cargo" check.
Because pln_sel() lets only suitable planes go on a bombing run, the
broken sanity check is unnecessary. Drop it.
There are three ways to fly cargo: transport (fly command with a
commodity argument), cargo drops (drop command that isn't a sea mine
drop), and paradrops.
A cargo flight can be either an airlift or an airdrop. Airlifts carry
more cargo than airdrops. A cargo drop or paradrop with a non-VTOL
plane is an airdrop. Anything else is an airlift.
Before, paradrop always behaved like an airdrop, regardless of VTOL,
and drop always like an airlift. This made little sense.
Effect of the change on the stock game: paradrop with tc carries twice
the punch, and np/tr/jt can drop less than they can fly. In
particular, tr can't drop guns anymore, and jt can drop only one
instead of three.
Simplify plane selection for drop, fly, recon and sweep
Plane flying commands first select the planes to fly the mission and
their escorts, then equip them. They all fail when no planes to fly
the mission can be equipped.
Unlike bomb and paradrop, commands drop, fly, recon and sweep had an
additional check that made them fail when no planes to fly the mission
could be selected. Because "none selected" implies "none equipped",
the additional check is redundant. Remove it.
While there, break lines in calls of pln_sel() more tastefully.
Before, bomb selected any plane, but planes with zero load could not
be equipped. Cargo planes could be equipped fine, and they flew bombs
to the target, where they silently vanished.
Initially, paradrop capability was implied both by capability cargo
and by capability VTOL. Chainsaw changed para() to require cargo, and
added compile-time option PARAFLAG to additionally require new
capability para. The optional PARAFLAG rule became mandatory in
Empire 2.
Chainsaw left the old tests for "cargo or VTOL" in place. Because
para() checked "cargo and para" first, the old tests for "cargo or
VTOL" always passed, so they had no effect.
Make passing paradrop & mine cargo to pln_arm() & friends optional
These missions imply the cargo type, just like bombing missions. Use
the implied type instead of cargo type parameter ip there. Parameter
ip is now optional except for missions 't' (transport) and 'd' (drop).
Simplify para() not to pass the optional cargo type. Leave drop()
alone, because always passing the type is simpler there.
pln_arm(), pln_equip(), mission_pln_arm() mission_pln_equip() took a
mission parameter encoding the kind of sortie (strategic bomb,
pinpoint bomb, transport, ...), a flag parameter to further specify
the plane's role, and a parameter ip to specify the load.
The flags argument was always either P_F (intercept), P_F | P_ESC
(escort), or zero (any other role).
With non-zero flags, mission and ip argument were not used in any way.
Use mission 'e' and null load for escorts, and remove flags.
Intercept can still be identified by mission zero.
Also change pln_mobcost() to take a mission parameter instead of
flags, so that pln_arm() and mission_pln_arm() can simply pass on
their mission.
Check the true load instead of the ip parameter. Makes a difference
only when callers pass a bogus ip that isn't actually used. Happens
for escorts, but then the call can only be reached for deities,
because for mortals the transports fail before the escorts.
Don't print hit chance for nuclear warheads, it's always 100%.
Instead, print "Arming nuclear warheads" there. That message is now
printed after interception instead of before.
Before, it took a sector argument, and targeted the lowest-numbered
satellite there. Rather inconvenient when your own satellite masks
one of the enemy's.
Moreover, the command could be abused to find all sectors with
satellites. Now it can "only" be abused to find satellite ids, and
whether they're in range. Still not ideal, but tolerable.
Fix value of detonate() when target sector takes no damage
This can happen when hitting a sanctuary, or for ridiculously impotent
nukes: groundburst for dam < 5, airburst for dam < 7. detonate()
returned unpredictable value then.
This screwed up interdiction damage, except for missiles interdicting
ships or land units. Since nothing can move into a sanctuary, the bug
bit only with ridiculously impotent nukes.
Fix prod() to limit level production to resource contents
This could make production command mispredict resource-depleting level
production. Stock game is not affected. In fact, such a product
would be highly unusual.
Fix use of invalid pointer when depleting resource "none"
prod() and produce() dereferenced resource uninitialized for products
depleting resource "none" (p_nrdep != 0 && p_nrndx == 0). The latter
even wrote to it.
Depleting "none" makes no sense, and the depletion is now ignored.
Before, it could conceivably crash the server or corrupt the game.
Commit 1bca66c0 added show news and commit 71bbd642 show product
without updating the help text. Fix that. The prompt is now too
long, so add option '?' to show it, and change the prompt to refer to
that.
Our nightly build test cases rely on the PRN sequence generated by GNU
libc's random(). We used to have such a PRNG in our tree (derived
from an old version of GNU libc's), for use under Windows. Thus we
got the same PRN sequence on both our nightly test systems. Commit 3ec807e9 switched to -liberty under Windows and removed our random().
Now we got a different sequence there, breaking the test cases.
Test cases still don't work on non-Windows systems where random()
doesn't match GNU libc's. We should switch to a PRNG that produces
the same sequence everywhere.
Move Windows socket stuff out off posixio.c into w32sockets.c
This is so we can avoid linking utilities with socket libraries (see
commit 8b778634).
When using sockets, we need to replace close(), because Windows'
close() can't cope with socket file descriptors. But replacing it
always would pull in the socket stuff again. Define close() to call
function pointer w32_close_function, which is initially _close.
Rename posix_close() to w32_close_maybe_socket(). Make new
w32_socket_init() put it in w32_close_function.
Same for read() and write(): define read(), write() to call function
pointers w32_read_function, w32_write_function, initially _read(),
_write(); rename posix_read(), posix_write() to
w32_read_maybe_socket(), w32_write_maybe_socket(), and put them into
w32_read_function, w32_write_function in w32_socket_init().
Also call WSAStartup() there, and use that from loc_NTInit().
WSACleanup() now belongs next to w32_socket_init(). Don't bother,
just drop it, along with loc_NTTerm().
Put the Windows code into new open_locked() instead. It's ugly having
that in file.c, but the fakes are ugly too, and somewhat brittle.
Remove posix_open(), F_SETLK, F_RDLCK, F_WRLCK, struct flock, and
simplify fcntl().
Unlike POSIX sockets, Windows sockets are not file descriptors, but
"OS handles", with a completely separate set of functions.
However, Windows can create a file descriptor for a socket, and return
a file descriptor's underlying handle. Use that instead of wrapping
our own file descriptors around Windows file descriptors and sockets.
Remove FILE_FUNCTION(), posix_fstat(), posix_lseek(),
SHARED_FUNCTION(), and fileno(), because the system's functions now
work fine.
posix_fsync() is used only #ifdef _WIN32, remove it, and call
_commit() directly.
The old code stuffed WSA error codes into errno, which doesn't work.
Use new w32_set_winsock_errno() to retrieve, convert & stuff into
errno. Adapt inet_ntop() to set the WSA error code instead of errno,
so it can use w32_set_winsock_errno().
Move EWOULDBLOCK from sys/socket.h to w32misc.h, and drop unused
ENOTSOCK, EAFNOSUPPORT.
Use SOCKET rather than int in Windows-specific code.
Unlike POSIX sockets, Windows sockets are not file descriptors, but
"OS handles", with a completely separate set of functions.
However, Windows can create a file descriptor for a socket, and return
a file descriptor's underlying handle. Use that instead of our gross
hacks to keep up the illusion that sockets are file descriptors.
Slightly dirty: we put file descriptors into fd_set. Works because
both boil down to int. Change w32_select(), w32_socket(),
w32_connect(), w32_recv(), w32_writev_socket(), w32_send() to take and
return only file descriptors, and map to sockets internally. Replace
w32_close_socket() by w32_close(), and drop the close() macro hackery
that made tcp_connect(), host_connect() use w32_close_socket(). New
fd_is_socket().
Windows provides select()-like functions only for handles. Because of
that, the client used a handle for reading script files, and stored it
in file descriptor input_fd. Drop this dirty hack, use a file
descriptor instead. Works because we can get its underlying handle.
Remove the dirty macro hackery that made play(), ring_from_file() and
doexecute() unwittingly work with a handle. Remove w32_openhandle()
and w32_close_handle(). Replace w32_readv_handle() by w32_readv_fd().
Update w32_select().
Remove w32_openfd(), it's not really needed.
The old code stuffed WSA error codes into errno, which doesn't work.
Use new w32_set_winsock_errno() to convert & stuff.
Fix signed vs. unsigned warnings in Windows client.
Move the struct sigaction replacement next to the sigaction()
replacement.
Rename sysdep_init() to w32_sysdep_init() for consistency.
When select() gets interrupted by SIGINT while a handler is active
without SA_RESTART, it returns immediately with EINTR. w32_select()
did that only while it waited for standard input to become ready for
reading. This isn't the case when:
* The client has already received EOF on standard input. But then the
action is SIG_DFL, so there was no problem.
* Reading standard input is suspended until the server drains the
input buffer. Then reaction to Ctrl-C got delayed until the socket
got ready, and w32_select() returned normally. Harmless, because
the reaction merely appends to the input buffer.
Change w32_select() to match select()'s behavior anyway.
Don't log out player when update aborts a command under Windows
pthread.c's empth_select() returned 1 instead of 0 when empth_wakeup()
interrupted select(). This made io_input() attempt to read input,
which failed with WSAEWOULDBLOCK. The failure then got propagated all
the way up, and the player got logged out. Fix by returning 0 in that
case.
start_server() creates the thread running player_accept() before it
calls update_init(). However, update_init() initializes stuff used to
player threads: update_time[] and play_lock. In theory, a player
thread could start before that, and crash when taking the
uninitialized play_lock.
Delay starting that tread until after update_init().
A player thread may sleep on input or output, except:
(1) While it is executing a C_MOD command, it may only sleep on input.
(2) While it is being aborted by the update or shutdown, it may not
sleep at all.
To find out whether a player thread may sleep on input, code has to
check condition (2). It needs do to that in recvclient().
To find out whether it may sleep on output, it has to check both
conditions. It needs to do that in pr_player() and upr_player().
The code tracked condition (1) in global variable play_lock_wanted.
It checked condition (2) by examining struct player member command.
Replace all that by new struct player member may_sleep. Initialize it
in player_new(), update it in dispatch(), shutdwn() and update_run().
This makes the tests in recvclient(), pr_player() and upr_player()
obvious. play_wrlock_wanted() is now unused, remove it.
Player threads may only sleep under certain conditions. In
particular, they must not sleep while a command is being aborted by
the update or shutdown.
io.c should not know about that. Yet io_output_all() does, because it
needs to give up when update or shutdown interrupt it. The function
was introduced in Empire 2, but it didn't give up then. Fixed in
commit a7fa7dee, v4.2.22. The fix dragged unwanted knowledge of
command abortion into io.c.
To clean up this mess, io_output_all() has to go.
First user is io_write(). io_write() automatically flushes the queue.
In wait-mode, it calls io_output_all() when the queue is longer than
the bufsize, to attempt flushing the queue completely. In
no-wait-mode, it calls io_output() every bufsize bytes. Except the
test for that is screwy, so it actually misses some of the flush
conditions.
The automatic flush makes io_write() differ from io_gets(), which is
ugly. It wasn't present in BSD Empire 1.1. Remove it again, dropping
io_write()'s last argument.
Flush the queue in its callers pr_player() and upr_player() instead.
Provide new io_output_if_queue_long() for them. Requires new struct
iop member last_out to keep track of queue growth. pr_player() and
upr_player() call repeatedly until it makes no more progress. This
flushes a bit less eagerly in wait-mode, and a bit more eagerly in
non-wait mode.
Second user is recvclient(). It needs to flush the queue before
potentially sleeping in io_input(). Do that with a simple loop around
io_output(). No functional change there.
Return number of bytes written on success, -1 on error. In
particular, return zero when nothing was written because the queue was
empty, or because the write slept and got woken up, or because the
write refused to sleep.
Before, it instead returned the number of bytes remaining to be
written when empth_select() failed, when woken up from sleep, or
refusing to sleep. You couldn't tell from the return value whether
the call made progress writing out the queue.
The current callers don't actually notice the change.
Don't set IO_EOF when writev() returns zero. I don't think this could
happen, but it's wrong anyway, because a short write should not stop
future reads.
The blocking I/O option makes no sense in the server, because it
blocks the server process instead of the thread. In fact, it's been
unused since Empire 2, except for one place, where it was used
incorrectly, and got removed in the previous commit.
Make I/O non-blocking in io_open() unconditionally. Remove IO_NBLOCK
and io_noblocking().
The call switched the connection with the player to blocking I/O for
draining of output before closing the connection. Looks scary,
because blocking on I/O blocks the complete server process, not just
the player thread. But we don't do input, and we do output only with
IO_WAIT, which can't block. So this has no effect.
Chainsaw used this together with the notify callback to make the iop
data type usable for sockets it listened on, so that io_select() could
multiplex them along with the sockets used for actual I/O.
io_select() became unused in Empire 2, and finally got removed in
commit 875d72a0, v4.2.13. That made the IO_NEWSOCK and the notify
callback defunct. The latter got removed in commit 7d5a6b81, v4.3.1.
Fix empth_sleep() for argument in the past and pthreads
Calculation of sleep duration suffered integer underflow for unsigned
time_t and arguments in the past. This made empth_sleep() sleep for
"a few" years instead of not at all.
F_GETFL always failed with WSAEINVAL. io_noblocking() always failed
without doing anything. Callers didn't check for failure, and newly
opened sockets remained blocking. But because because
WSAEventSelect() makes sockets non-blocking automatically, they became
non-blocking soon enough to keep things working.
Remove the broken code to query the non-blocking state, and just
return 0. Document why this works.
While there, simplify the F_SETFL case by using ioctlsocket() instead
of WSAIoctl().
Replace the fixed $1 per ETU maintenance for capital/city sectors that
are at least 60% efficient by a configurable maintenance cost, payable
regardless of efficiency. The only change in the default
configuration is that inefficient capitals now pay maintenance.
Charging sector maintenance regardless of efficiency is consistent
with unit maintenance.
New struct dchrstr member d_maint and sector-chr selector maint. Make
show_sect_build() show it. Change produce_sect() to record
maintenance in new slot p_sect[SCT_MAINT] instead of abusing
p_sect[SCT_CAPIT]. Replace the "Capital maintenance" line in budget
by "Sector maintenance".
Print sector type mnemonic and name, like show sect s and c. Print
"can't" instead of negative number for sectors players can't designate
(this was not an issue before the previous commit). Show build cost
per 100%, like show ship, land, plane and nuke. Size the columns more
sensibly.
Fix show sect b not to omit undesignatable sectors
show sect b needs to explain any sector players can build.
show_sect_build() omitted sectors players can't designate. That's
wrong, because players can certainly own and thus build sectors they
can't designate. Test for infinite mobility cost instead, like
show_sect_stats().
Clean up automatic supply leftovers in ship update
Commit 7da69c92 (v4.3.20) removed use of automatic supply from
prod_ship(). It removed bp_enable_cachepath(), but left behind the
final bp_disable_cachepath(); bp_clear_cachepath(). Clean that up.
Fix mine production resource limit for peffic != 100%
With etu_per_update large and resource depletion quick, a sector can
produce more work than is required to fully deplete a mine. In that
case, produce() and prod() limit production to what is actually in the
ground. Except produce() got it wrong for sector types with
production efficiency other than 100%.
This affects mountains in the stock game, but only with impractically
large etu_per_update.
configure checked for library functions with LIBS instead of
LIBS_server, which could break detection of getaddrinfo() on systems
where LIB_SOCKET isn't empty.
GNUmakefile put @PTHREAD_LIBS@ only in LDLIBS, which breaks linking of
server and possibly client on systems where it is not empty.
We use the C run-time, so we better use its _beginthread(), too.
CreateThread() can lead to deadlocks, at least with some versions of
the C run-time. Broken in commit f082ef9f, v4.3.11.
Fix Windows client's stdin read thread's error handling
stdin_read_thread() zeroed bounce_status on failure, effectifely
treating it like EOF. Fix by setting to -1.
It treated main thread termination like failure, and set bounce_error
to a bogus value. Can't happen, because the program terminates when
the main thread terminates, and the only user of bounce_error is the
main thread anyway. Regardless, handle the case by terminating,
because that's more obviously correct.
Make Windows client read password without echo again
Commit 8c3b8d10 replaced the getpass() for Windows by a generic
ersatz_getpass(). This lost the "switch off echo" feature, with the
excuse that it doesn't work for me (MinGW & Wine). Turns out it works
under real Windows. Restore the feature.
The old upstream version carries the original BSD license, which is
incompatible with the GPL. Fix by rebasing to a version that is
licensed under the 2-clause BSD license.
Fix return value of s_commod() when it can't draw enough
s_commod() could incorrectly claim success when the sink ended up with
at least as many supplies than were missing initially. This caused a
number of problems:
* shp_torp() let a ship with two shells fire a torpedo, resulting in
-1 shells, which then made item_prewrite() oops. Affected missions
and return fire, but not the torpedo command.
* shp_missile_defense() let a ship with one shell use missile defense,
resulting in -1 shells, and the same item_prewrite() oops.
* Land units were considered in supply even when they had not quite
enough supplies. Such land units could defend without penalty,
attack and react. Commands load and lload weren't affected, because
they use lnd_in_supply(), which doesn't use s_commod().
upd_plane() upd_land() and left planes and land units lost to lack of
maintenance on their carriers. Cargo lists were fine anyway, because
unit_cargo_init() ignored dead units. But when the dead unit got
reused for building a new one, pln_prewrite() / lnd_prewrite() got
confused and attempted to take it off its carrier, which made
clink_rem() oops, because the unit wasn't on the cargo list. No real
harm done, as oops recovery was fine.
Fix upd_plane() and upd_land() to clear the carrier. Make
unit_cargo_init() oops when it finds dead units on carriers.