This fixes an issue where there server would start up fine even if NPC
types from a later build were found in the gruntwork file. Because of
the semantics of the C++ array indexing operator, the index into NPCData
in loadGruntworkPost() would silently create extra entries in the
nlohmann::json array, which would break future NPC type limit checks and
subsequently crash the server at the next invocation of /summonW, and
likely other places.
We fix this by refusing to start the server if any invalid NPC types are
found, because simply skipping them in the gruntwork file would silently
omit them from further writes to the gruntwork file, which would be
undesirable data loss.
The server now checks the libsqlite both at compile time and on server
startup. The version the executable was built with and the one it's
running with may be different, so long as they're both at or above the
minimum supported version. One or both version numbers are printed on
startup, depending on if they're identical or not.
The compile-time ("Built with") version depends on the sqlite3.h header
used during compilation, while the runtime ("Using") version depends on
either:
* The sqlite3.c version used during compilation, if statically linked.
(Which may be different from the header version and still compile and run
fine.)
* The version of the libsqlite3.so or sqlite3.dll that the server
loaded, if dynamically linked. Version mismatches here are normal,
especially on Unix systems with their own system libraries.
The current minimum version is 3.33.0, from 2020-08-14, as that's the
one that introduced the UPDATE-FROM syntax used during login by
Database::updateSelectedByPlayerId().
Also rearranged the prints and initialization calls in main() slightly.
Previously, only loadGruntworkPost() would be skipped if the gruntwork
was null, but it was never null at that point because loadGruntworkPre()
would inadvertently create gruntwork["paths"] when it indexes it to
iterate through it.
Now, the gruntwork loading messages will no longer be misleadingly
printed to stdout when there isn't a gruntwork file.
UBSAN complains about the casting approach because it loads a 64-bit
integer from the defaultKeys string which isn't guaranteed to be 64-bit
aligned, which is undefined behavior.
CNSocket::kill() will now no longer call close() on already closed sockets.
close() should never be called on already closed file descriptors, yet
CNSocket::kill() was lacking any protection against that, despite its
use as both a high-level way of killing player connections and as a
means of ensuring that closing connections have been properly terminated
in the poll() loop.
This was causing close() to be erroneously called on each socket at least
one extra time. It was also introducing a race condition where the login
and shard threads could close each other's newly opened sockets due to
file descriptor reuse when a connection was accept()ed after the first
call to close(), but before the second one. See the close(2) manpage for
details.
Previously, terminating a running server from the terminal would
sometimes print a benign warning message if the server was currently
handling an incoming packet. This happened because CNServer::step()
would continue handling the packet after CNServer::kill() released the
activeCrit mutex. Now it first re-checks if active has been set to false
in the mean time after acquiring the mutex.
Revisiting this again; the issue was that the comparison operator was
facing the wrong way, so connections were being pruned every 30 seconds
or less. This was effectively a race condition kicking an unlucky player
every so often when pruning happened exactly during an attempt to enter
the game.
Now that the proper timeout is being enforced, I've reduced it to 5
minutes, down from 15, since it really doesn't need to be that long.
This should fix issues with segfaults when the server is being
terminated that sometimes occur because things like NPC path traversal
keep running while the process is executing the signal handler.
* Separate pruning frequency from timeout
* Pluralize CNShared map: login -> logins
* Increase connection timeout to 15 minutes
* Do not deallocate a nullptr in playerEnter()
* Kill connections rejected by playerEnter()
* Remove redundant inclusions of mutex headers in a few places
* Use a specialized connection object
* Copy the Player object less frequently
* Use a randomly generated serial key for shard auth
* Refuse invalid shard connection attempts
* Clean up connection metadata when a Player joins the shard
* Prune abandoned connections when they time out
This prevents logic errors related to being in chunk 0 0 0.
Also:
* Moved some duplicated chunk teleportation logic to a new helper
function
* Made ChunkPos into a proper class so it can default to INVALID_CHUNK
when default-initialized
* Reversed the inclusion order of Chunking.hpp and Entities.hpp to work
around problems with type definitions
The client will actually do this itself when clicking the quit button in the tilde menu, but for the idle timer the connection would remain open until the game is closed.
* Download and list artifacts after build
* Add commit hash + file extension to artifact name
* Initial SSH implementation
* Don't build artifacts for PRs
* Fetch endpoint from secret
* Zip artifacts before uploading to CDN
* Use short SHA in archive names