Get rid of `iConditionBitFlag` in favor of a system of individual buff
objects that get composited to a bitflag on-the-fly.
Buff objects can have callbacks for application, expiration, and tick,
making them pretty flexible. Scripting languages can eventually use
these for custom behavior, too.
TODO:
- Get rid of bitflag in BaseNPC
- Apply buffs from passive nano powers
- Apply buffs from active nano powers
- Move eggs to new system
- ???
Was getting frustrated by the inconsistency in our include statements,
which were causing me problems. As a result, I went through and manually
re-organized every include statement in non-core files.
I'm just gonna copy my rant from Discord:
FOR HEADER FILES (.hpp):
- everything you use IN THE HEADER must be EXPLICITLY INCLUDED with the exception of things that fall under Core.hpp
- you may NOT include ANYTHING ELSE
FOR SOURCE FILES (.cpp):
- you can #include whatever you want as long as the partner header is included first
- anything that gets included by another include is fair game
- redundant includes are ok because they'll be harmless AS LONG AS our header files stay lean.
the point of this is NOT to optimize the number of includes used all around or make things more efficient necessarily. it's to improve readability & coherence and make it easier to avoid cyclical issues
Group structures are used now. Adds more checks in some places but simplifies things overall.
We can expand this system to entities as well now pretty trivially.
Start by replacing `hitMob` with `takeDamage` interface function.
Simplify `pcAttackChars` a little by utilizing the new interface, then add more interface functions as needed.
A lot of the combat logic is tied to the `Mob` class. Need to start moving stuff over to CombatNPC.
Mobs and CombatNPCs still need theirs in order to properly set their
roaming and spawn coords. Assignment of the latter has been moved to the
CombatNPC constructor, where it should have already been.
Storing certain things in appearance data and others in their own fields
was gross. Now everything is stored on the same level and functions have
been added to generate appearance data when it's needed by the client.
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.
In our original implementation, quest item drops were rolled on the
spot, so the chances of getting two quest items for different missions
in a single kill (where both missions have you kill the same mob) were
independent of each other.
When we made quest item drop chances shared between group members so
players doing missions together would progress at the same rate, we
accidentally linked the quest item odds of different missions together.
This change makes it so that the odds are per-task, so they're shared
between different group members doing the same tasks, but distinct for
different tasks being done by the same player.
* Removed a redundant failure case in endTask()
* Fixed a misleading comment in startTask()
* Removed a redundant level check in updateFusionMatter()
* Cleared up misleading comment and code layout in taskEnd()
* Removed unnecessary comment in mobKilled()
Couldn't get a reliable repro, but this is probably what that bug was.
It's not very throughly investigated, but we'll be tweaking those parts
of the codebase anyway, so we can examine if there's a deeper issue
later.
* Restrict fcntl() to only the flags we need
* Non-fatally deny tgkill() and rt_sigaction() so that segfaults don't
result in a SIGSYS. They're debuggable either way, but this way it's
clearer what the issue is right away.
* Allow truncate() and ftruncate() for sqlite's alternate journal modes
* Slight macro cleanup
* Add missing colon in a DB log message
We don't need to worry about compilation problems arising if glibc or
musl-libc add their own wrapper for the seccomp() syscall in the future.
Ours will/would just silently take precedence over the external one
without interfering with compilation. This should work regardless of
whether libc uses weak symbols and regardless of whether libc is
dynamically or statically linked into the executable. The wrapper's
signature has been stripped of its static and inline qualifiers, as it
must match the exact declaration the libc headers will/would use.
Further, if a pre-compiled binary is run on a system which genuinely
doesn't support seccomp(), it'll just return ENOSYS and the server will
terminate with an error. The user can then just disable the sandbox in
the config file. We don't need any special logic for that scenario.
There are some network configurations in which it's undesirable; such as
reverse tunneling through ssh. These are obscure enough to allow leaving
the option undocumented (in the example config file).
This fixes a UX issue, where if you accidentally capitalized a letter
in the username when logging in, it would instead create a new account.
The behavior was confusing, since to the user it looks as if their
characters were deleted or progress was not saved.
In order for this to work, duplicate accounts (e.g. username and USERNAME)
need to be deleted/renamed. The server will *detect* if any duplicates
exist. If any are found, it will direct the server operator to a pruning
script, or they can choose to resolve the duplicates manually.
This wasn't strictly necessary as this command has outlived its
usefulness, but I had gone ahead and rewritten it because it was (barring
taskStart()) the only place in the codebase that accesses Chunking::chunks
outside of Chunking.cpp. This became apparent during a (currently paused)
effort to improve the API that the Chunking namespace exposes to the
rest of the codebase.
I went ahead and rewrote the rest of this command as it was poorly
implemented anyway. This has been sitting in my working directory
uncommitted for a few months, so I may as well push it.
Explanation: it was uncertain whether mobs could perform critical hits, since the color of damage numbers didn't change at all. However, I found that male characters will actually use a different sound effect when receiving a crit (I confirmed this SFX appeared in old FF videos), so I went ahead and re-enabled it.