This commit reworks how MobArena interfaces with Vault economy provider
instances. Instead of affixing an Economy _instance_ to the main plugin
at "setup time", we create an instance of the new _Finance_ abstraction
at "(re)load time", and then use this new abstraction at every call site
(really just the MoneyThing) instead of Economy. This accomplishes two
important things:
1. Due to how the Bukkit load order works and how Vault providers are
registered with the plugin, the Finance abstraction gives us a means of
deferring the retrieval of the Economy provider needed to integrate with
the underlying economy plugin (or script). MobArena still uses Finance
as if it was a "complete" object and is completely unaware of what goes
on under the hodd. This is a much more elegant solution compared to the
terrible past proposals of fragmenting MobArena's bootstrapping phase,
especially considering the past proposals were server tick-centric...
2. It creates a simple and maintainable interface between MobArena and
Vault, effectively giving control back to MobArena as to how everything
should mesh together.
The biggest downside to this solution is that we no longer know if an
economy provider is available for when we need it at a convenient time
in the startup process. Instead, we need to react accordingly when an
economy operation is invoked. The implementation takes a somewhat mild
approach by simply _logging_ any issues encountered and defaulting to
"best effort" operations when an economy provider is not available. At
worst, this is a bunch of log spam and potentially a lot of work for
server owners to clean up after (compensating players for rewards and
stuff like that), but MobArena doesn't handle arbitrary exceptions in
its join/leave and start/end procedures very well, so it's probably a
better way to go about it than throwing exceptions.
If the server does not have Vault installed, an UnsupportedFinance is
created. This implementation just logs errors and "fails to work" on
every operation. If Vault is installed, a VaultFinance is created, and
this implementation should only log errors if a Vault economy provider
couldn't be found. We could have opted for a single implementation, but
this approach allows us to potentially support other economy registrars
in the future, so we (probably) won't have to make sweeping changes in
order to swap to something else.
Closes#797
This commit "fixes" the problem introduced in the "no sign edits" commit
by not invoking the sign actions for interact events where the player is
sneaking. This is not the ideal way to go about it, but since we need to
bump the Spigot API version to access the sign change event that would
fix the underlying issue, this will have to do for now.
Fixes#791
Turns out this isn't necessary at all. I think the server used to throw
an exception if there were too many characters on a sign, but it doesn't
seem to be the case anymore (tested on 1.21 and 1.13.2).
This commit simply removes the truncation logic from the sign rendering
procedure, and it fixes issues where text that would otherwise fit on a
sign would be cut off, but more specifically one where color codes would
sort of "push" characters out of the sign despite not being visible.
Fixes#790
This commit changes the "source" of the explosions from `obsidian-bomb`
to _something_ that isn't the boss itself. Originally, these explosions
were block explosions, but without a "source" from the damage caused to
other mobs, the `monster-infight` flag was largely ignored. By switching
to "entity explosions" with the boss as the "source", the flag was now
respected, and mobs would no longer take damage from these explosions if
the `monster-infight` flag was set to `false`.
However, the handler for "entity explosions" now started reacting to the
explosions caused by the `obsidian-bomb` ability as if they were normal
entity explosions, e.g. those caused by creepers, and because entities
that explode usually "die" (although without an EntityDeathEvent), the
handler removes the entity from the monster manager, which effectively
"unregisters" the boss, leaving the entity itself just hanging around in
the arena as a normal mob (with a huge health pool).
The "obvious" solution is to change back to "block explosions", and that
could perhaps have worked if the API had consistent interplay between an
"explode event" and the resulting "damage event". It turns out that the
EntityExplodeEvent results in an EntityDamageByEntityEvent, and the same
can be said for BlockExplodeEvent and EntityDamageByBlockEvent, except
the latter will have a `null` block in it, so even if we were to attach
some metadata to the block, we wouldn't be able to retrieve it in the
damage handler. So we're sticking with "entity explosions"...
The hacky solution: We just spawn a new entity and pin the explosion on
it instead of the boss. That way the new entity is removed by the entity
explosion handler (which is a no-op, because the entity isn't an arena
monster) instead of the boss. It technically doesn't matter which entity
we spawn, but by spawning a primed TNT block, we can naturally stick a
source on it, and the damage handler will treat it as if the boss placed
a TNT block and detonated it, which is an apt analogy after all. Just to
make sure we don't forget the dual responsibility, we've added a little
comment in the damage handler.
An alternative solution would have been to spawn any other entity and
stick the boss entity ID on it, then look up the entity by that ID in
the damage handler if the damager had metadata on it. This is just a bit
more streamlined and likely a lot more performant.
Fixes#789
Introduces a means of "tracking" both the players and the boss itself
during the `shuffle-positions` ability for the sake of _not_ blocking
the teleport event(s) that could potentially be blocked otherwise.
Note that the change introduced in the PlayerTeleportEvent handler is
not strictly necessary. The final block of code _should_ always yield
the same result, as `shuffle-positions` _should_ be working solely on
positions that are all inside the arena region, but the out-of-bounds
check might be delayed, so we don't know for sure. It's just a safety
precaution, but it really shouldn't be necessary.
Fixes#792
This commit changes two things; it allows multiple single waves to have
the same wave number, and it makes the wave selection procedure pick
randomly between multiple valid matches.
Effectively, this means that MobArena now picks randomly when clashing
recurrent waves _also_ clash on priority (rather than deferring the
decision to the arbitrary but deterministic nature of sorted sets), and
it also picks randomly between multiple single waves with the same wave
number (which was not possible before).
This should allow server owners to add variety into their wave setups
while maintaining a great deal of control over the randomness involved.
For instance, it's now possible to have wave 10 be a boss wave, but the
_actual_ boss wave is randomly picked between three different waves all
with wave number 10. Upgrade waves could be randomized as well. As for
randomized recurrent waves, perhaps instead of having potentially four
different mobs in the same wave, a setup could instead have two _pairs_
of mobs that are randomly selected for a different kind of randomness.
Closes#795
This commit cleans up the old fake event show that was implemented when
the BlockExplodeEvent was introduced and caused all sorts of issues back
in the day. It should have been cleaned up years ago, but "if it ain't
broke"... Well, it is now! With this commit, however, we get rid of the
constructor invocation that throws up, but we obviously need to replace
it with something else to take care of the problem it used to fix.
The handler is close to a carbon copy of the EntityExplodeEvent handler,
simply because the code isn't very reusable due to direct method calls
on the event object itself. But because of the refactoring step of the
previous commit, we don't actually have to explicitly repeat the gnarly
"repairable" code that handles the block list, yay... We obviously don't
have an entity to check for (and remove from the monster manager), and
we need to dig a little deeper for the Location object needed for the
region check, but other than that, it's the same thing.
Fixes#796
This setting allows server owners to allow arena monsters to teleport
around _while inside the region_. They still can't teleport out of the
region.
Taken at face value, this should just be the default behavior. However,
due to arena regions being boxes, any non-box shaped arena will need a
region that covers more than the physical arena structure, which means
mobs like Endermen will be able to teleport into possibly unreachable
areas of the physical structure. So we have to make do with a setting.
Closes#762
Using the new `mobarena.admin.errors` permission, this commit provides
server owners with a way to make the infamous Spigot health error much
more visible by sending the error message to any "admins" online when
the error occurs.
Closes#764
Introduces a new permission for "admins" that can be used to increase
visibility of errors caught by the plugin. Server owners may not want
_all_ online players to see these types of messages, so the permission
gives error handlers a way to filter the list of online players before
sending the error message.
Introduces the concept of a _saved item_; an in-game item that has been
captured in a YAML file via Bukkit's item serialization mechanism. These
items can be referenced in the config-file in all places that any other
normal item can be used, assuming the ThingManager is in charge of the
parsing. This should help bridge the gap between class chests and the
config-file by allowing any Bukkit serializable item stack to be stored
and referenced as if MobArena's item syntax directly supported it.
Three new setup commands are introduced to help manage the items, such
that they can be created, deleted, and loaded (for "editing" purposes).
The commands are somewhat rough around the edges and may need a little
bit of polish going forward.
Together with the new inventory referencing Things, this functionality
should help provide most of the flexibility people have been missing
from the item syntax for about a decade... Hell, it's about time.
Closes#212
Adds three new Thing types that can be used to reference items in chests
(or any block-based InventoryHolder):
- InventoryIndexThing looks up an item by index/slot in an inventory.
- InventoryGroupThing groups all non-null/non-air items in an inventory
into a ThingGroup.
- InventoryRangeThing groups all non-null/non-air items in a given range
of an inventory into a ThingGroup.
The new Thing types aim to bridge a gap between the class chests and the
rest of the Thing-based parts of the config-file. The goal is two-fold:
allow for more in-game configuration so access to the config-file isn't
_quite_ as crucial, and propagate the item-wise feature completeness of
class chests to other parts of the plugin.
While class chests are low configuration and a bit "all or nothing", the
inventory Thing types require manually punching in the coords for chests
and possibly indices/ranges for items. This means that the initial setup
could be a bit unwieldy, and highly volatile wave setups are definitely
not a good fit. If the wave setup is mostly pre-defined, it is fairly
easy to tweak upgrade waves and rewards in the same way class chests are
tweaked.
As for item-wise feature completeness, the inventory Thing types share
the same "if Bukkit can copy it, it will work" rule of thumb as class
chests do, which means items with metadata such as custom names, lore,
or even NBTs, should just work. This could remove the need to employ
other plugins.
By no means can this solution be considered "optimal", but it it _does_
enable some long-requested features.
Closes#456
Since Minecraft 1.20, players can edit signs by right-clicking on them,
and that poses a problem for the sign-centric portions of the plugin,
such as class selection signs and the various types of arena signs.
This commit refactors the PlayerInteractEvent handler in ArenaListener
in order to break open the possibility of handling non-lobby players as
well. We're a little more strict with lobby players, and we still want
to handle class sign clicks and iron block clicks here. For players who
aren't in the lobby, we're really just blocking the event according to
the regular protection rules (block is inside region, protect is on, and
arena is not in edit mode).
It also blanket cancels events in the HandlesSignClicks event handler,
because there is no meaningful way to edit an arena sign, since their
contents come from the template file and not from what is written on
them by the sign renderer.
Ideally, we'd refactor some of this event handler logic, because it's a
lot easier to take care of the individual responsibilities in separate
event handlers.
Fixes#765
It boggles the mind that this tiny little class has worked as intended
since 2011 (!!), and all of a sudden, signs no longer retain their text
in the repair procedure...
By calling the somewhat arbitrary `update()` method on the sign after
setting the contents, the sign appears to correctly update again.
Fixes#772
It's not clear why this variation in the auto-ready logic exists, and
the commit history doesn't seem to have any clues either. Perhaps the
actual readying up logic was incompatible with auto-ready at some point,
but at this point in time it doesn't seem like this is necessary at all,
and it appears to be causing a bug with the MobArenaStats extension.
By simply calling the player ready procedure regardless of the status of
the auto start timer, MobArena fires the arena player ready event that
MobArenaStats depends on for some of its pre-session bookkeeping. It
could be argued that MobArenaStats should be more robust, but we would
much rather fix the root problem than slack on the otherwise fairly
sound strictness of the MobArenaStats data model.
Fixes#746
Makes the boss entity the source of the obsidian bomb explosion, which
then makes the damage event listener handle the explosion damage as if
the boss is the damager, which means the `monster-infight` flag should
be respected.
Fixes#759
Makes the sheep entity the source of the explosion that's created when
it triggers close to a player. This, in turn, makes the damage event
listener handle the explosion damage as if the sheep is the damager,
which means it will respect the `monster-infight` flag.
Fixes#758
This new spawn reason was introduced somewhere between 1.18 and 1.18.1,
and unfortunately it is a breaking change, so we have to employ it for
MobArena to properly allow and register vexes (again...), but we also
have to maintain a variation of the old logic so we don't break support
for older server versions.
Fixes#719
This time around we're trying to keep the API version bumped for the
sake of not leaving the people who actually keep their servers updated
behind. It does present some potential backwards compatibility issues
when we do employ new API, but we'll just have to be mindful of _how_ we
go about doing so...
When a player with pets dies in the arena, we want their pets to be
removed. One could probably argue that the pets _should_ be able to
stick around, but the original intent was for them to be removed
alongside their owner.
Fixes#721
Removes the first `_` in the `_SHULKER_BOX` matching to ensure that normal shulker boxes with ID `SHULKER_BOX` are removed from My Items as well.
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
This new setting allows changing the fuse time for auto-ignited TNT,
which is normally a hardcoded 80 ticks in Minecraft. Note the somewhat
weak safeguarding without any sort of error message - with great power
comes great responsibility...
Closes#715
This property makes TNT explosions look more "real" to other plugins who
may be consuming events from MobArena's sessions. It also gives way to a
potential rework of the "planter" logic that currently makes use of the
Bukkit Metadata API.
Closes#718
Changes the behavior of the ArenaListener's teleport event handler such
that it _ignores_ teleport attempts instead of _rejecting_ them when the
player in question has the `mobarena.admin.teleport` permission. Because
the global listener's event handler only ever checks the permission if
at least one per-arena listener has _rejected_ the teleport attempt, and
none of them have explicitly _allowed_ it, the change means that it will
never check the permission, because its internal `allow` flag will never
change to `false`. Thus, the check can be safely removed from the global
listener's logic.
When the response is to ignore instead of reject, the message that would
have otherwise been sent to the player is skipped. This fixes#702.
It is perhaps tempting to move the permission check up into the section
of sanity checks in the global listener, but this is very specifically
avoided to allow MobArena to _uncancel_ teleport events that have been
cancelled by other plugins, but that MobArena might need to go through.
Please see afcc526a71 for more info.
This is technically not necessary when pets are simple entities like
wolves and zombies, because these types of pets will never target and
thus attack other players in the arena. However, projectile entities
such as Blazes and Ghasts may hit players in the line of fire, and so
any such damage should be cancelled.
Fixes#712
Introduces a new arena setting that changes the "emptiness check" for
`clear-wave-before-next`, `clear-wave-before-boss`, and the final wave
check, allowing for a customizable _leeway_.
By default, the leeway is 0, which means the monster set has to be
completely empty for the checks to pass. With a value of 2, the set
may contain up to two monsters for the checks to pass, and so on.
The leeway should help alleviate some of the issues some people have
with their arena sessions when monsters "disappear" behind terrain or
end up in hard-to-reach areas. This is by no means a real "solution"
to the underlying problem, since the build-up of monsters in longer
sessions will just result in the issue being pushed to later waves.
We'll see if the setting leaves any additional customization to be
desired and perhaps defer any such requests to the Sessions Rework.
Closes#706
Sets the player fall-distance to 0 before performing a player teleport
during "move player" steps. This should cancel out the any fall damage
that may have built up before the teleport was initiated.
Fixes#698
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
It turns out that, according to the Spigot API, flaming arrows actually
_change_ TNT blocks to air before/instead of igniting them. While this
is a little counter-intuitive, the fix seems to revolve around allowing
the change to happen if the changed block is TNT and the "changer" is an
arrow.
Allowing the change event to happen means "foreign" primed TNT entities
will spawn, which makes it necessary to clean them up during the session
cleanup phase in ArenaImpl.
Fixes#696
It's not clear if this change actually solves the underlying issue,
because it has not been tested in a controlled environment. However,
considering the EntityExplodeEvent constructor, which doesn't take a
cancel state value, and the `HIGHEST` priority setting of MobArena's
event handler, the issue is realistic, and this change very likely
solves the issue.
Fixes#704
The arena signs predate the use of arena slugs everywhere, so something
slipped through the cracks in this regard. Incidentally, the handler for
arena updates is one of the few classes in the signs package that has no
unit tests, probably due to it being "obvious implementation". Not so
obvious after all, it seems, so now we have a basic test for it.
Fixes#705
This commit (unfortunately) makes two somewhat unrelated changes:
- Forces certain ageable entities into adulthood to prevent the occasional baby spawns (fixes#687).
- Introduces support for baby versions of certain ageable entities (fixes#689).
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
Introduces four "new" boss abilities that work like their pre-existing counterparts, but affect all players in the arena. This makes the four _ability sets_ a bit more complete, as it were, in that it is now possible to choose between single targets, nearby, distant, or _all_ players for the given abilities.
Closes#434
Due to a breaking change in Spigot 1.17, sending a title with an empty string no longer works, even if a non-empty subtitle is provided. The TitleAnnouncer and TitleHealthBar components only use the subtitle portion of the title API to avoid obtrusive behavior.
With this commit, the two components send a single space character as the title. Effectively, there is no difference between showing the empty string or a single space on the client-side. It's a dirty hack to fix something that should be fixed in Spigot instead, but odds are that it won't be.
Fixes#683
Introduces a new arena setting to force spectators to leave the arena
when the current session ends. If set to `true`, it "overrides" the
behavior of `spectate-on-death: true` when players respawn, such that
they only become spectators if there is an active arena session.
The respawn behavior technically means that it is possible for a player
to begin spectating "the next session" if a new session begins between
them dying and clicking Respawn on the death screen. Ideally, we would
want the player to auto-leave, because the session _they_ participated
in ended, but we don't have a concept of "previous sessions", so this
quirky behavior will have to do.
Closes#682
When piglins, piglin brutes, and hoglins go through zombification, the current entity is removed, and a new one spawns. MobArena prevents the zombified entity from spawning, so it feels like the mobs just "vanish".
We don't actually want the mobs to zombify, however, so this commit makes them immune to the zombification process. Note that this shouldn't cause any problems on server versions prior to 1.16, because the piglin and hoglin keys never get registered on those versions, so there is no risk of hitting those branches in the `switch` statement.
Fixes#684
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
Adds a new optional `pet-name` property to arena class configurations that lets server owners set a custom pet name template instead of the hardcoded "<player>'s pet". This allows for translation and color coding. To accommodate setups where the player's "display name" isn't a good fit, e.g. because it is too long, the more generic "player name" is available for use instead.
Closes#595
Co-authored-by: Bobcat00 <Bobcat00@users.noreply.github.com>
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
Changes the way monster equipment is handled from clearing _all_ items to clearing just the armor contents. This means that monsters that naturally spawn with weapons won't need their weapons added back in.
It also means that monsters that _occasionally_ spawn naturally with weapons now may do that in arenas. This is deemed acceptable, because:
- occasional weapons don't have as much of an impact as occasional armor,
- the forwards compatibility aspect is too valuable to pass up on, and
- should occasional weapons become too much of an issue, they can be conditionally removed, i.e. we can implement the _inverse_ behavior of what we had previously.
Fixes#641Fixes#686
Co-authored-by: Andreas Troelsen <garbagemule@gmail.com>
This commit filters the result list of a ThingGroupPicker by a non-null
predicate to avoid null values in the resulting ThingGroup instance.
Since null values represent `nothing`, and we don't usually announce it
when players earn a `nothing` reward, it makes sense that they wouldn't
bubble up and somehow "manifest" in groups of things either.
Fixes#691
This commit introduces a new type variant, `angry-bee`, which is a bee
whose anger level is maximized upon spawning, much in the same vein as
its angry wolf cousin.
Note that bees are not available prior to Minecraft 1.15, so a sentinel
`null` name is used in the registration to prevent warnings from being
logged on server versions that don't have a concept of bees.
Closes#584
Because this bullet covers both the commit with the version bump and the
following compatibility commits, it didn't really fit properly into any
one of those commits, so now it gets its own!
This commit introduces two reload events, MobArenaPreReloadEvent and
MobArenaReloadEvent. Both events are fired when MobArena reloads, the
former right before the reload, and the latter right after.
The reason for two events is to allow plugins to reload either entirely
or partially along with MobArena, if it makes sense for them to do so,
_when_ it makes sense for them to do so. Some plugins need to reload and
re-register themselves with MobArena before the config-file itself is
loaded (e.g. ThingParsers), while others either require MobArena to be
fully loaded or are more decoupled and thus don't depend on anything in
MobArena's innards to function. The "pre" event is for the former, while
the other event is for the latter.
As for naming, the choice of "Pre" and no prefix was made for the sake
of consistency with the event names in the Bukkit API, which has just
one example of such a pair (PlayerPreLoginEvent and PlayerLoginEvent).
Some event naming conventions (e.g. in the .NET world) seem to favor
present and past tense (reloading, reloaded), but this would be wildly
inconsistent with the rest of the event names, so it might be better to
just stay consistent. Names may change before an actual release, but for
now, this is what we're rolling with.
Closes#677
This commit introduces a new `register()` method on the CommandHandler
class to allow registering pre-instantiated subcommands. This means that
subcommands are no longer restricted in terms of instantiation, so they
can have their dependencies injected at creation, rather than having to
resort to Singleton anti-pattern means.
Also refactors the existing internal MobArena command registration to
use the new method to "drink our own champagne" and to reuse the code.
Fixes#675
This commit changes the pattern of the player list command to one that
isn't quite as greedy. This change is enough to fix issue #676 and thus
allow MobArenaStats to take control of its own command.
The command framework could definitely do with a bit of a rework away
from pattern matching towards aliases, which would prevent similar
issues from cropping up down the line. For now, this is good enough.
Fixes#676
These should have been included in their respective commits, but they
didn't seem particularly interesting for end users at the time. Might as
well include them, though.
This commit re-frames the formula concept used by the wave growth, swarm
amount, and boss health wave configuration properties. It fundamentally
changes how these values are calculated, from a static, compile-time set
of enum values and hardcoded expressions, to a powerful math expression
feature that supports constants, variables, operators, and functions.
In part to remain backwards compatible with existing MobArena setups,
and in part for a better user experience, the old enum-based expressions
are relocated into a new file, `formulas.yml`, as _macros_. The file is
written to the plugin folder if missing, and it contains a formula for
each of the legacy values for each of the enums. Additionally, it has a
global section with some predefined macros for inspiration's sake. The
goal of this file is to allow people to define new formulas and reuse
them in their wave configurations instead of having to duplicate the
same formulas again and again.
Parts of the system are extensible. It is possible for other plugins to
register additional constants, variables, operators, and functions.
Closes#460Closes#461
This commit constitutes a major rewrite of how arena signs are stored
and loaded. It fixes an issue where an unloaded or missing world would
cause MobArena to throw errors if there were any arena signs recorded in
said world.
The solution is to load signs of a given world when it is available,
rather than loading all signs at once indiscriminately. At startup,
signs for all _currently available_ worlds are loaded. This fixes the
errors. When a world loads and a WorldLoadEvent is fired, signs in that
world are loaded. This ensures that all valid signs in existing worlds
will _eventually_ load. To keep things tidy, a WorldUnloadEvent will
unload all signs for the unloaded world.
Bukkit's own YAML deserialization implementation doesn't re-throw all
deserialization errors, which means we can't actually catch the problem
of missing worlds without doing an awkward "scan" of the deserialized
objects. This prompted a rewrite of the serialization and data storage
into a custom CSV-like format which is both simpler and also provides a
lot more control over the process. Instead of relying on world _names_,
the new format uses world _UUIDs_. While the rest of the plugin won't
necessarily adapt well to a world being renamed, the signs data store
should be resilient enough to handle it.
Most of the actual sign rendering code and almost all of the template
code is intact, but quite a lot of other classes have been rewritten or
replaced. Some of the rewrites weren't strictly necessary, but because
the components were already fairly small, rewrites were much faster and
a lot less awkward than attempting to adapt existing code.
Fixes#645