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
By turning things into their own thing pickers, we can avoid creating a
bunch of SingleThingPicker wrappers, which are now redundant. Because
the semantics of using things and thing pickers are quite different, it
does perhaps make it necessary to be a bit more careful about picking
the right type.
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>
We're good with on-demand imports for the static Hamcrest and Mockito
functions in test code, because the arrange and assert steps of unit
tests all make use of them, so it's known where they come from.
This commit makes the ThingGroupPicker return different values depending
on the size of the picked result set. If the set is empty, the picker
returns null, emulating a NothingPicker. If the set is a singleton, the
element itself is returned, emulating the SingleThingPicker. Finally, if
the set contains more than one element, a ThingGroup containing those
elements is returned.
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
Instead of relying on the evolving Material enum values, we can use the
new BlockData API as advised by some of the folks "in the know". It is
unclear how much of a performance impact the the "block data gathering"
and `instanceof` checks incur, but this is a pretty secluded place in
the code base, so probably nothing to worry about.
An alternative solution could have been to check if the _name_ of the
Material equals "SIGN" or "WALL_SIGN", or ends with "_SIGN". That should
cover all cases in a sorta kinda safe manner, but it isn't as resilient
as the BlockData/BlockState hierarchies.
We could also employ the new Materials utility class and enumerate all
sign types by name and just check for membership of the resulting set,
but this creates another brittle crash point.
Introduces the Materials utility class, which works much like the static
MACreature registration process, but for certain material types. Instead
of storing everything in a stringly typed map, certain Material values
are stored as constants.
Right now it's just the `OAK_SIGN`/`SIGN` pair for the autogeneration in
MAUtils, and chances are we can throw it out at some point in the near
future, but at least now there's room for more materials, should the
need arise.
Changes the fallback default wave in the wave parser to be consistent
with the default wave of the default config-file. Not a huge deal, but
it does feel a bit more neat.
Replaces the `zombie-pigmen` entry in the `spec1` wave of the default
configuration with `slimes-big`. This is due to the pig zombie rework
into piglins, which makes the custom name a little flaky. If we ever
decide to ditch the custom names somehow, this will probably make such a
transition a little easier.
While `slimes-big` is also a custom name, so are `powered-creepers` and
`angry-wolves`, but at least they are "API stable" in comparison, which
is really what this change is about.
It's K&R style braces in all modern portions of the code base, and
IntelliJ keeps complaining about final values, so let's just make it
happy.
The `plural` field should go away at some point, but for now it's fine
to just null it out, since the legacy `register()` method is only ever
used by the deprecated-for-removal constructors, both of which actually
set it to something non-null.
The new constructor and registration method pair is what other plugins
should be using. They provide a slightly cleaner interface, and we don't
need so many different ways to do the same thing.
This commit changes the way MACreature initializes its internal map of
available monster types. Specifically, it replaces all static references
to EntityType enum values with a series of registration helper methods
that try to resolve EntityType enum values from a given set of strings,
stopping on the first match. If nothing matches, no registration, but a
warning is logged unless a specific sentinel `null` value is passed as
an argument, in which case the key is just silently skipped (not in use
as of this commit, but it will be necessary for modern types like bees
from 1.15+).
While this is arguably a huge step back in terms of type safety, it does
make the code base more resilient to API version bumps, and it allows
compiling against modern API versions and still run on older (1.13-1.16)
servers. It _does_ mean that changes to the EntityType enum (such as
PIG_ZOMBIE to ZOMBIFIED_PIGLIN) must be discovered manually, which makes
maintenance a little more difficult. The warning in the server log does
make it fairly easy to fire up an early build of a new server version
and check if everything initializes correctly.
Spigot has decided to discontinue distribution of the Bukkit artifacts,
which means Bukkit "doesn't exist" after Minecraft 1.15. This commit
takes the plunge on that change and moves to the Spigot API rather than
the Bukkit API. Goodbye, Bukkit. Hello, Spigot.
The version is also bumped to 1.17 to give way to new features and bug
fixes otherwise blocked by staying on 1.13. Unfortunately, this brings
with it a swath of potential issues with version compatibility going
forward, because backwards compatibility in the API sometimes takes a
back seat, such as with the Material and EntityType enums. This commit
specifically changes some references that break compatibility with older
server versions, but compatibility with those is reinstated later.
Finally, the Spigot repository is changed to match the consensus about
which path on the Spigot Nexus instance to use. This seems to be what
everyone else is doing, so `bandwagon.jumpOn()`.
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
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
This is a very simple change that could potentially save a lot of
headaches when people look elsewhere in their config-file and see a
class name like "Big Gary" and wonder why putting that value in the
`default-class` property won't work.
This is really just an internal change, since the `arenaName()` method
is likely not used anywhere. If it is, however, it shouldn't be depended
on as an identifier anyway, so I'm confident that removing it won't mess
up anything that doesn't need a rework anyway.
This just removes the internal use of "lowercase name" in Upgrade Waves
and _should_ have no effect on the actual config-file and the parsing
done there.
Introduces support for multi-word arena names in the two commands. The
approach is to simply join the arguments by spaces. Because of the new
slug-based lookups, multi-word names are fairly straightforward.
This commit is a minor refactor of how arenas are resolved by name,
similar to the previous arena class resolution refactoring.
The difference this time around is that there is an ArenaMaster method
that does most of the work for a lot of different areas of the plugin,
`getArenaWithName(String)`. This method is called from well over 30
different places in the code base, making it a cornerstone of all arena
resolution. Luckily, it is called primarily from places that shouldn't
care _how_ an arena is resolved.
Affected by this commit are all commands that resolve arenas by name,
including all those not listed in the diff, because of the change to
`getArenaWithName(String)` on ArenaMaster.
Commands that can tab complete arena name arguments now complete slugs
instead of config names.
Going forward, it should be possible to show "pretty" arena names by
simply using a pretty name in the config-file instead of slug-like names
now that slugs are on the horizon.
Conversely to how the "lowercase name" of ArenaClass was deprecated in
favor of the slug, the "arena name" method here is deprecated in favor
of the "config name" of arenas. Instead of trying to pretty up an arena
name (which is doomed to fail), we just use the "config name" for pretty
printing instead, and start using slugs elsewhere.
This should give way to a much better experience with multi-word arena
names as well.
Swaps the weird "camel casing" approach in the autogenerate command out
with simply using the "config name" of the arena classes when creating
class selection signs.
This fixes the breaking change to how classes are resolved in a previous
commit, but only in future arena generation procedures. Arena generated
before this change may still contain broken class signs.
This commit is a minor refactoring of the class selection functionality
plugin-wide. Instead of selecting classes based on the "lowercase name"
of a class, commands and listeners are now "slug aware", as it were.
The ArenaClass class now uses its slug instead of its "lowercase name"
for equality and hash codes.
The `/ma class` command now tab completes slugs, but it still supports
class names as they appear in the config-file when executing the command
itself. The same applies to the `/ma classchest` command.
The sign handling in ArenaListener slugifies sign text instead of just
lowercasing and stripping spaces.
This commit changes the permission checks for arenas and classes to a
slug-based approach instead of the "config names", which are somewhat
arbitrary and may contain spaces, which are generally not supported by
permissions plugins.
This is a breaking change, which means it will be necessary for users
to change their permission setups. Backwards compatibility could have
been implemented, but it just leaves more room for ambiguity and will
make a necessary transition later down the road less obvious. Instead,
we burn the ships!
As a result of this change, access to the "My Items" class can now be
revoked as intended with the key `mobarena.classes.my-items`.
Fixes#647
Whitespace and punctuation in identifiers is a fairly big source of
issues in areas like permissions and commands where whitespace isn't
directly supported (or at least makes things needlessly difficult).
This commit introduces the concept of a "slug" in arenas and classes,
giving them a _consistent_ `kebab-case` name for use in such places, but
it does not implement their use anywhere. Slugs are expected to be the
solution to problems like the one posed in issue #647.
At the time of writing, we're only concerned with simple stuff like
removing periods, commas, parentheses and replacing underscores and
spaces with dashes. If it turns out that people have unanticipatedly
problematic arena and class names, we may have to expand the slug
definition rules or allow for custom slugs.
This commit introduces a new announcement value for the `addreward`
command. This allows server owners to completely remove the message by
setting it to the empty string while retaining wave reward messages.
In general, we shouldn't reuse announcements unless the actions that
send them are identical, because it makes everything a lot more tangled
than it has to be.
This command takes an arena player and a Thing as input. The Thing is
parsed in the same way as all other rewards are parsed, so it supports
the new `all()` and `random()` functions and the `nothing` keyword.
The "target audience" of the command is mainly scripts. It opens up the
possibility of hooking into MobArena's rewards from command blocks or
other plugins, but with very low coupling.
Closes#643
By using a ThingPicker instead of a Thing, boss rewards can now, just
like regular wave rewards, make use of the new `random()` and `all()`
functions as well as the `nothing` keyword.
Closes#628
This commit introduces the strange concept of a singleton ThingPicker
that only ever picks `null`. The purpose of this picker is to allow for
a type of "loot table" experience similar to that found in other games.
An example would be a piece of equipment that only has a 50 % chance of
dropping. With the current state of MobArena, it would be necessary to
something conjure up a CommandThing or something to emulate nothingness,
but now there is native support for it with the `nothing` keyword.
The nullability of rewards also has the side effect that we got to clean
up the MASpawnThread `addReward` method a bit.
Closes#638
This commit makes the MAUtils class responsible for parsing reward maps
use the new ThingPickerManager to parse the reward lists. As a result,
the new `all()` and `random()` pickers are available for use in the
rewards section.
This should allow for granting item sets and similar types of "bundles"
as rewards in the arena, e.g. a diamond sword and permission to join a
more difficult arena.
Closes#386
This commit makes breaking changes to the Arena interface to switch to
the new ThingPicker framework for rewards. It is unfortuante that the
Arena interface has so many disparate responsibilities that changes like
these are necessary on such a central component when the change itself
is actually very isolated to just rewards handling.
With this change, rewards are now wrapped in pickers, which should give
way to some grouping and "well-defined entropy".
Introduces the ThingPicker framework to the plugin by bootstrapping a
manager for it in the main plugin class. By default, we're just running
the group and random picker parsers, but other plugins should be able to
hook in with their own parsers.
Nothing actually _uses_ the pickers yet, but that's next on the menu.
This little nugget is what we will wire up in the main plugin class once
we're ready to introduce it to the code base for real. Its purpose is
similar to that of the ThingManager, in that it _is_ a parser and can be
passed around wherever a parser is needed, but it also is the main entry
point for anything that is ThingPickerParser instances.
To ensure that we are extensible from the get-go, this commit introduces
the parser aspect of the ThingPicker framework with parsers for the two
non-trivial picker implementations.
Nothing is wired up yet.
Things work best as hardwired, atomic pieces of "stuff", so fitting RNG
into the framework is a little difficult. We could have a RandomThing,
but the issue with that is that the entropy has to live inside of it for
it to be random every time it is given, taken, or checked. This makes it
impossible to make a well-defined, atomic Thing with entropy involved.
Enter the pickers.
The ThingPicker is an early stage extension to the Things framework that
encapsulates the possibilities of entropy and grouping _around_ Things,
a type of Factory pattern.
This introductory stage consists of a "leaf picker" that can only ever
pick a single Thing, a Composite Pattern "group picker" that wraps a
list of other pickers and shoves their resulting Thing instances into a
ThingGroup (introduced in the previous commit).
These simple constructs should now make it possible to introduce entropy
without placing the responsibility on the Thing framework itself. Using
this extension requires adapting code to using the ThingPicker interface
instead of using Thing directly.
Introduces a group of Things as a construct for bundling Things that
should constitute a single unit for whatever reason.
One reason could be to make an _item set_ an atomic reward, e.g. a set
of diamond tools for beating the `workshop` arena. Instead of having to
spread the rewards out into multiple waves or use a different plugin to
create item groups, this component allows MobArena to support that kind
of intent natively.
Note that nothing is wired up in this commit, so really this commit is
just introducing unused code.
Introduces the concept of an Announcer, which is invoked whenever the
`announce` methods on the Arena interface are invoked. Previously, all
announcements would simply be sent to the arena's Messenger, but this
new feature allows us to move them up into titles to help declutter the
somewhat overloaded chat box.
By default, to help people discover the new feature, the announcer type
is `title`, but it is possible to switch back to `chat` for the original
behavior.
Closes#272
Errata to commit 84a7a2ed8a.
The protocol after the join/leave process rework was:
- `spectate-on-death: true`: leave, then spec
- `spectate-on-death: false`: leave
The protocol now is:
- `spectate-on-death: true`: spec
- `spectate-on-death: false`: spec, then "leave"
I put "leave" in quotes, because _discarding_ the player here does not
invoke the part of the leave protocol that tries to ensure teleports go
through according to plan. By capitalizing on the "leave" part, making
it explicit that that's what we're doing in the next tick after warping
the player to the spectator area, we return to the form of the original
rework where we use coarser-grained state control to make things easier
to reason about.
A side-effect of this change is that if players somehow manage to kill
themselves in the lobby, they will be refunded the entry fee.
This promiscuous commit goes around touching almost every file in the
repository with the intention of removing trailing whitespace and adding
newlines to files missing them.
Trailing whitespace has been a pain for along time, especially in terms
of contributing to the project without accidentally littering commits
with whitespace stripping, so this commit is long overdue.
As for the newlines, well, the script I found on StackOverflow to strip
trailing whitespace also happened to add missing newlines, which is
something I wanted to tackle anyway, but in a different commit. It's all
good though.
This commit introduces a quality-of-life feature for spreading players
out during the warp to the arena floor. The feature is configured via
the new per-arena setting `arena-warp-offset`. When the value is greater
than 0.01, MobArena will add a random number of units between 0 and the
offset value to the X and Z axes in either direction. For example, if
the value is set to 3, players will be teleported to a random location
in a 3x3 square centered on the arena warp, rather than directly to the
arena warp itself.
Closes#371
Certain class chest items are cloned before they are made unbreakable,
but not all. This commit changes that, so all items in the input array
are cloned prior to setting the unbreakable flag on them. The items that
were previously cloned specifically no longer need to be, since they are
included in the initial cloning.
Fixes#637
Plugins like Multiverse and EssentialsX have features that override the
world spawn location for players respawning both with and without bed
locations. If MobArena catches the PlayerRespawnEvent too early, these
plugins will likely overwrite the respawn location set for players that
respawn after dying in an arena session.
Because MobArena is a minigame and its respawn manipulation logic only
runs for respawning arena players, it is perfectly reasonable for it to
get the final say in where these players should respawn. It would have
been nice to just use `HIGH`, but that is what EssentialsX runs at, so
we gotta go higher than that.
This commit forgoes some of the work done in the join/leave refactoring
of commit b1c6b61827.
The intent behind the original commit was to make the join/leave process
a very strictly defined set of steps that would work atomically to try
to ensure a stable, predictable process. As a result, the idea of making
spectators out of respawning players meant first kicking them out of the
arena and then re-joining as spectators, as if they had manually typed
`/ma leave` followed by `/ma spec`.
However, on some multi-world setups, this process causes people to get
thrown around a bit, which makes for a poor user experience. This commit
changes the behavior when `spectate-on-death: true` such that the player
isn't kicked, but is instead added to the spectator player pool when
they respawn. If the flag is false, players will still get sent to the
spectator area for one tick and then immediately kicked out as if they
had manually typed `/ma leave`. This does create a little bit of jumping
around, but because there is only one world change (as there would be
anyway), it is deemed acceptable at this point in time.
Closes#508
This commit changes the way Equippable wrappers are created, such that
they more closely match the way class chest armor pieces are "guessed".
That is, instead of looking directly at the Material value of the given
item, we instead look at the name _suffix_, i.e. the part after the last
underscore in the name, e.g. `BOOTS` in `IRON_BOOTS`.
The neat thing about this approach is that it is compatible with future
items that follow the same naming convention, such as Netherite armor
pieces.
The downside is that it is stringly typed and not particularly "pretty",
and if the naming convention breaks or new items are introduced (such as
the elytra), we will have to make modifications anyway.
Fixes#636
The vanilla behavior for most mobs is that they just stop minding their
targets' business if aggro is lost. The same applies to guardians and
elder guardians.
To prevent the pace of gameplay from stagnating, MobArena compensates
for target loss by helping the mobs find a new target. The issue with
this in terms of guardians is that breaking line of sight is the only
way - besides killing it very quickly - to avoid taking damage from a
guardian due to its lock-on laser attack. This means that guardians and
elder guardians need to be excluded from the retargeting logic to make
sense in an arena setting.
This commit introduces a new EnumSet of EntityTypes to exclude from the
retargeting logic. It is not a particularly pretty solution, especially
not since ArenaListener is such a huge class already, but it does make
it easier to add more mobs later down the road, and it does a slightly
better job at giving way to a config setting at some point.
For the Mobs Rework, a per-monster flag like `auto-retarget` or similar
might be a much better solution, so it's possible to have encounters
like a small batch of guardians that _don't_ lose their targets but have
very little health, so the "race against time"-aspect can exist, but in
a much more configurable way.
Fixes#601
This commit fixes the per-arena config setting `soft-restore`. Due to
commit 92c4ce1a8b, the soft restore logic
won't run on BlockBreakEvents, because to reach that specific part of
the event handler, the `protect` flag has to be set to `true`. However,
due to an early return in the soft restore logic if the `protect` flag
_is_ set to `true`, it is effectively impossible for it to run.
The fact that this functionality has been broken for over 6 years (!)
with almost no reports of it is perhaps a testament to how little it is
being used in the wild.
The integration only works reliably with the `clear-wave-` settings set
to `true`, because it relies on there being only one "current" wave.
This is a fundamental issue with the way sessions run right now, and is
another good example of why the Sessions Rework is necessary.
It is possible to achieve the exact same functionality by just making a
couple of Upgrade Waves before and after the waves that need to have
limited skill sets (thanks Lucy). With just a couple of quality-of-life
features like wave "triggers", it's possible to make things just the way
they were with the integration, but by using some more general building
blocks.
Closes#609
Catches ConfigError and prints its message rather than letting it bubble
up to the command handler as an uncaught exception. This means that
instead of seeing "An internal error occurred...", command senders now
see the underlying error message, as well as a short message telling
them to fix the error in the config-file and reload.
Closes#599
This commit fixes the `player-time-in-arena` per-arena setting by switching to `toUpperCase()` on the string value, which means that the values can actually result in something meaningful, rather than always throwing an exception.
The feature was broken in commit b1c6b61827, and it appears to be the only such instance to sneak through.
Fixes#621
Co-authored-by: Bobcat00 <Bobcat00@users.noreply.github.com>
Co-authored-by: Chew <chew@chew.pw>
This commit completely reworks the Root Target ability implementation by
replacing the repeated teleportation code with potion effects.
The old implementation relied on teleporting the target every tick for a
given duration, but because teleporting also involves pitch and yaw, the
result was a constant "snapping back in place" experience.
The new implementation works by applying the following potion effects
with a very large amplification:
- Slowness, to prevent the player from moving out of place.
- Slow falling, to negate fall damage.
- Negative jump boost, to prevent the player from jumping around.
Note that the jump boost uses a _negative_ amplification to make it have
the inverse effect, i.e. a much worse jump ability. It is still possible
to jump an arguably negligible amount away from the root location.
Fixes#490
Makes the class chest armor guessing logic pair `"ELYTRA"` up with the
chest piece slot. This specific matching works, because there are no
underscores in the `"ELYTRA"` item name.
Fixes#616
If someone spins up MobArena with a really old config-file or simply
removes the `pet-items` section, the plugin throws an NPE. This commit
fixes that by allowing the section to not exist.
It might be a good idea to log some helpful information, but let's wait
and see if this isn't good enough.
Fixes#606, closes#608.
Thanks Chew!
Adds support for a new `ready` state in the template engine for arena signs.
A sign is in the `ready` state when there are players in the lobby and all of them have readied up. This is only relevant in arenas with start delay timers, as the arena session automatically starts when all players are ready otherwise.
The new state is completely optional, even when no base state is provided, and it inherits from the `joining` state when not defined.
Closes#593
This commit adds support for listing arena players on arena signs by
introducing the following _dynamic variables_:
- `<arena-n>` the _nth_ live arena player
- `<lobby-n>` the _nth_ player in the lobby
- `<ready-n>` the _nth_ ready player in the lobby
- `<notready-n>` the _nth_ player in the lobby who hasn't readied up
Each variable points to a list of players (sorted by name), and the `n`
is the index into that list (1-indexed). This means that putting the
variable `<notready-1>` in a template will result in the name of the
first player in the list of lobby players who haven't readied up, sorted
by player name.
Implements #592
This commit changes how the ArenaRegion `intersects()` method works:
- The implicit null checks in the `setup` and `lobbySetup` flags have been replaced with actual null checks inside the auxiliary `intersects()` function. Not only does this make the auxiliary method more robust for potential future use, it also helps tidy up the code a bit. So neat!
- The semantics have changed, since `setup` depends on more than just the `p1` and `p2` points. This fixes an (unreported) bug where the check would report a false negative in case an overlapping arena region was defined, but e.g. the arena warp was missing.
- Instead of only checking arena vs. arena and lobby vs. lobby, we now also check arena vs. lobby and lobby vs. arena. That is, if the arena region is defined, we check it against both the arena region and lobby region of the other ArenaRegion (if they are defined). Same deal with the lobby region. This should ensure that no combination of overlaps pass through the check.
This commit fixes an issue with the new `intersects()` method on ArenaRegion. Instead of blindly assuming that the region points `p1` and `p2` are set when the method is called, we first make sure both regions are properly set.
Fixes#590
This commit introduces an `intersects()` method on ArenaRegion that
allows us to check whether two regions intersect.
The new method is employed in ArenaMasterImpl during arena load. If an
arena's region intersects with any other arena's region (in the same
world), we print a warning to the server log.
Fixes#367
This commit introduces a null check in the ConfigUtils `parseLocation`
method. If the world of a location string does not exist, the method now
throws an IllegalArgumentException, which allows callers to fail more
gracefully instead of having to resort to null checks.
Additionally, the parsing of exit warps, leaderboards, or linked class
chests is wrapped in try-catch statements that re-throw ConfigErrors
for better error reporting.
Fixes#421
This commit removes the old version checker that used the DBO resource
page and replaces it with a custom checker that uses the "legacy" Spigot
resource API. The Spigot API is much more lightweight and doesn't
require any sort of parsing.
The new version checker uses a simple cache, keeping version checks
fresh for up to one hour, reducing the need to go fishing on every op
login. The cache resets on restarts, though, but this is acceptable.
Note that no attempt has been made to ensure correctness on multiple,
consecutive invocations when the cache is stale. If a cache refresh is
initiated, all update checks invoked before the cache refresh has ended
will behave as if no update is available. This is acceptable, because
update checks are non-essential, the time frame is extremely narrow, and
the most common result of an update check is "no updates available",
since the amount of update checks made is vastly greater than the amount
of updates released.
Since the actual execution of these commands use the regular player name
instead of the display name, the tab completion is useless if it doesn't
also use the regular player name.
Fixes#589
This commit is a complete rewrite of the target event handling logic in
the ArenaListener class.
Instead of the complex, inconsistent code structure with too many line
breaks between control flow branches, we just have a thin logic wrapper
that delegates the event handling to smaller, more focused functions
that handle arena pets, arena monsters, and foreign entities on their
own.
A couple of auxiliary methods are introduced to try to limit the amount
of warnings produced by checking set membership with `contains()` when
the entity/target is an Entity and the collection is a sub type.
Fixes#572
This commit removes a condition in ArenaImpl#canJoin(Player) that checks to see if the arena's WaveManager has any recurrent waves.
Removing this condition is safe, because the WaveManager already makes sure to have a "catch all" default wave at hand for when no wave definitions match a given wave number. As such, the condition in ArenaImpl is completely unnecessary, and was in fact the root cause of a bug.
Fixes#566
Due to the package structure and location of MASpawnThread, SpawnsPets doesn't need to be opened up for this to work.
This change opens up for quite a few different upgrade strategies, since pet items obtained outside of Upgrade Waves are transformed as well. It is technically a breaking change, because it breaks the invariant that "after arena start, no items are transformed to pets". However, since pet items are customizable, it is possible to just change the `bone` to `sponge` in the config-file if bones carry some sort of implicit meaning in an existing setup.
Closes#524
It doesn't seem like there was any real reason to leave out potion effects as upgrades in Upgrade Waves previously, so they were likely just forgotten in the Things API overhaul.
Closes#565
This is a bit of an overhaul of how CreatureSpawnEvent is handled:
- Events happening outside the arena region are still ignored, as are armor stands placed in edit mode.
- Otherwise, if the arena isn't running, we blanket reject all entities.
- Player-mode iron golems and snowmen are still allowed.
- Anything that isn't spawned by a plugin is rejected, _unless_ it is a Vex spawned with "default" spawn reason, which is what the Evoker summon spell uses.
In the end we have a "custom" spawn reason inside the arena region while it's running, which is perfectly "legal". And because of the unfortunate use of Creature (instead of Mob) in MACreature, Slimes and Magma Cubes still need to be handled separately here.
Fixes#564
When a player takes damage from a hostile mob, the player's class pets will have their target set to that mob. This means that even zombies can function as "real" pets!
This commit removes the `auto-respawn` arena setting and the logic associated with it.
MobArena's hacky implementation of auto-respawning is buggy and discouraged. To get true auto-respawn functionality we'd have to move to the Spigot API instead of the Bukkit API.
This commit adds custom names to class pets in the form `<player name>'s pet`. This should help players more easily distinguish between pets and hostile mobs.
This commit changes the pet parsing and handling such that it is a lot more dynamic. Rather than limiting pets to wolves and ocelots, any living entity is now a potential pet. This means that we can do away with the logic that specifically targets the Wolf and Ocelot interfaces and instead work with the more general Tameable interface. As a result, the discrepancies between Minecraft 1.13 and 1.14 in this regard are largely irrelevant, because server owners can just specify which entity they want to spawn given which item in their config-files.
MonsterManager's two Wolf- and Ocelot-specific addPet() methods are replaced with a generic addPet() method that takes an owner (Player) and a pet (Entity). This is technically a breaking API change, but MonsterManager is an internal component that is very unlikely to be used outside of MobArena.
The `global-settings` node in the config-file can no longer be forced into strict compliance via ConfigUtils, because the `pet-items` node is now dynamic rather than static.
The default `pet-items` node in the config-file no longer has `ocelot` in there to avoid confusion on 1.14. It now only contains `wolf` and people will have to add their own ocelot/cat in if they haven't added it already.
Fixes#563