v1.5.8 update
This commit is contained in:
1 parent
c7370dcbae
commit
226e6b211c
6 files changed
+331
-48
No files matched your search
+198
@@ -0,0 +1,198 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to TheHerobrine-OG are documented here.
|
||||
|
||||
## 1.5.8 - 2026-07-29
|
||||
|
||||
### Changes
|
||||
|
||||
- Repair Herobrine's visible-phase combat and phase-three loadout:
|
||||
- Equip and select the phase-three sword in hotbar slot 0 and place poison
|
||||
vials in slot 1.
|
||||
- Give Herobrine one normal golden apple in hotbar slot 2.
|
||||
- Stop clearing valid KnockbackSync knockback one tick after visible-phase
|
||||
hits while retaining knockback immunity during the invisible phases.
|
||||
- Preserve arrows as projectile hits with their direction and enchantment
|
||||
behavior instead of converting them to synthetic melee hits.
|
||||
- Apply game damage only after OldCombatMechanics has accepted a hit, and run
|
||||
hit effects only for non-cancelled damage.
|
||||
- Declare KnockbackSync-OG as a soft dependency so combat listeners are ordered
|
||||
consistently when it is installed.
|
||||
- Identify the active Herobrine in chat with a dedicated red role prefix and
|
||||
bold message text.
|
||||
- Bundle the canonical `joinsigns.yaml` registry and install it on first startup.
|
||||
- Correct the bundled default lobby's voting-map count.
|
||||
- Update the GxUI-OG and Utilities-OG integrations.
|
||||
|
||||
## 1.5.7 - 2026-06-16
|
||||
|
||||
### Changes
|
||||
|
||||
- Pin spawned shards to their configured block centers so they cannot drift.
|
||||
- Add the `/hbwizard` command for an on-demand map setup walkthrough.
|
||||
- Return joining players to the correct lobby spawn, improve hub-return
|
||||
behavior, and preserve real pre-lobby return locations while switching
|
||||
between managed worlds.
|
||||
- Add lobby-local chat delivery and fix duplicate chat messages.
|
||||
- Route `/vote` and `/v` to Herobrine voting only inside managed lobby worlds.
|
||||
- Accept full, case-insensitive, and numeric lobby aliases such as `HB1`, `hb1`,
|
||||
and `1` in `/hbjoin`.
|
||||
- Fix players remaining invisible after returning to a lobby.
|
||||
- Fix managed-world mob cleanup.
|
||||
- Fix join-sign rendering for 1.8 clients.
|
||||
- Update GxUI-OG and Utilities-OG.
|
||||
|
||||
## 1.5.6 - 2026-05-20
|
||||
|
||||
### Changes
|
||||
|
||||
- Add persistent, permission-controlled join signs with live lobby status and
|
||||
click-to-join behavior.
|
||||
- Add `/hub` for leaving a lobby or game and returning to the main world.
|
||||
- Improve join-sign labels and aggregate lobby status rendering.
|
||||
- Rewrite the README around the current MyWorlds setup and administration flow.
|
||||
|
||||
## 1.5.5 - 2026-05-15
|
||||
|
||||
### Changes
|
||||
|
||||
- Add `/hbsetspawn`, tab completion, and a guided map setup wizard.
|
||||
- Harden shard spawning, bounds checking, cleanup, and map validation.
|
||||
- Add configurable Herobrine skin textures for the visible phase.
|
||||
- Add startup diagnostics that explain invalid maps and failed game starts.
|
||||
- Expand spectator joining, toggling, navigation, and stat-handling behavior
|
||||
while correcting related edge cases.
|
||||
- Add a void-world generator for first-time map setup.
|
||||
- Correct several map setup edge cases.
|
||||
- Prepare managed worlds for MyWorlds multi-world inventories.
|
||||
- Bundle static default lobby configuration resources.
|
||||
- Update bundled BKCommonLib, MyWorlds, and ProtocolLib dependencies.
|
||||
|
||||
## 1.5.2 - 2026-04-22
|
||||
|
||||
### Changes
|
||||
|
||||
- Make MyWorlds a required dependency for the Purpur 1.19.4 target.
|
||||
- Fail startup cleanly when SQL or Redis cannot initialize.
|
||||
- Validate lobby resources before registering new lobbies.
|
||||
- Rebuild active lobbies when `/hbreloadconfigs` runs so refreshed lobby
|
||||
configuration takes effect immediately.
|
||||
- Remove ProtocolLib lobby listeners during shutdown and close Redis pools when
|
||||
the plugin is disabled.
|
||||
- Update GxUI-OG and Utilities-OG.
|
||||
|
||||
## 1.5.1 - 2026-04-13
|
||||
|
||||
### Changes
|
||||
|
||||
- Complete the initial MyWorlds compatibility work across plugin and lobby
|
||||
lifecycle handling.
|
||||
- Add optional IllegalStack-OG compatibility, exempting active lobby players
|
||||
and reliably removing exemptions when they leave or a lobby shuts down.
|
||||
- Disable animal and monster spawning in managed game worlds.
|
||||
- Update the TrueOG APIs and adapt to the newer GxUI-OG interface.
|
||||
|
||||
## 1.5.0 - 2026-03-30
|
||||
|
||||
### Changes
|
||||
|
||||
- Replace Multiverse world management with MyWorlds.
|
||||
- Remove the bundled Multiverse legacy API and migrate lobby/world operations
|
||||
to BKCommonLib and MyWorlds APIs.
|
||||
- Update the GxUI-OG and Utilities-OG integrations.
|
||||
|
||||
## 1.4.0 - 2025-12-12
|
||||
|
||||
### Changes
|
||||
|
||||
- Introduce the multi-lobby architecture used by the TrueOG deployment.
|
||||
- Refactor game, world, stat, kit, command, and message state to be scoped per
|
||||
lobby instead of relying on a single global game.
|
||||
- Add commands and completion for creating, deleting, joining, reloading, and
|
||||
spectating lobbies.
|
||||
- Begin the Multiverse 5 compatibility migration.
|
||||
- Add the Gradle wrapper, automatic formatting, and source-backed GxUI-OG and
|
||||
Utilities-OG submodules.
|
||||
- Modernize the Gradle tooling while retaining the Java 17 target.
|
||||
- Update runtime and build dependencies throughout 2024 and 2025.
|
||||
|
||||
## 1.3.3 - 2024-10-25
|
||||
|
||||
### Changes
|
||||
|
||||
- Target Java 17 and add the Purpur 1.19.4 API.
|
||||
- Convert the Gradle build and settings files to Kotlin DSL.
|
||||
- Produce reproducible archives and modernize Shadow plugin packaging.
|
||||
|
||||
## 1.3.2 - 2024-01-15
|
||||
|
||||
### Changes
|
||||
|
||||
- Update the server target from Minecraft 1.18.2 to 1.19.4.
|
||||
- Update ProtocolLib, Jackson, and MySQL Connector/J.
|
||||
- Update packet sound access for the newer ProtocolLib API.
|
||||
|
||||
## 1.3.1 - 2023-05-05
|
||||
|
||||
### Changes
|
||||
|
||||
- Add a spectator compass GUI for teleporting between active players.
|
||||
- Add per-map minimum and maximum shard Y bounds.
|
||||
- Destroy out-of-bounds shards, keep spawned shards aligned, and clean up shard
|
||||
entities safely.
|
||||
- Keep invisible-phase Herobrine players invisible after potion interactions.
|
||||
- Correct the bundled Ancient map data.
|
||||
|
||||
## 1.3.0 - 2023-04-11
|
||||
|
||||
### Changes
|
||||
|
||||
- Rebalance and reorganize kit inventories, armor, and abilities.
|
||||
- Add permission-aware kit definitions and richer item/ability descriptions.
|
||||
- Add the start-timer pause command and improve timer behavior as a lobby fills
|
||||
or an operator pauses it.
|
||||
- Add `/v` as a voting alias.
|
||||
- Improve Herobrine smoke and waiting-state feedback.
|
||||
|
||||
## 1.2.0 - 2023-04-08
|
||||
|
||||
### Changes
|
||||
|
||||
- Hide Herobrine's name tag until the third shard makes Herobrine visible.
|
||||
- Separate stat tracker shutdown from stat reset so completed game statistics
|
||||
are persisted before being cleared.
|
||||
- Delay end checks after disconnects so Herobrine and survivor outcomes are
|
||||
evaluated against updated online state.
|
||||
- Record disconnects during a live game as deaths.
|
||||
- Add a configurable point threshold for displaying the DeathBringer rank.
|
||||
- Add end-of-game diagnostic logging.
|
||||
|
||||
## 1.1.0 - 2023-04-07
|
||||
|
||||
### Changes
|
||||
|
||||
- Add a lobby statistics sidebar for points, captures, kills, and deaths.
|
||||
- Rebalance Herobrine's weapon damage.
|
||||
- Add explicit shard-destruction handling when a carrier falls into the void.
|
||||
- Make spawned and dropped shards invulnerable.
|
||||
- Correct stat access, lifecycle handling, and shard-destruction narration.
|
||||
- Restore production defaults of 8 minimum players, 13 maximum players, and a
|
||||
90-second start timer.
|
||||
- Document the MySQL and Redis requirements.
|
||||
|
||||
## 1.0.0 - 2023-04-06
|
||||
|
||||
### Changes
|
||||
|
||||
- Deliver the first tagged, playable remake with complete lobby, map, shard,
|
||||
capture, survivor, Herobrine, spectator, voting, and win-state flows.
|
||||
- Add the Archer, Priest, Scout, Wizard, Mage, Paladin, and Sorcerer kits with
|
||||
their associated abilities.
|
||||
- Add SQL-backed statistics, ranks, points, captures, kills, and deaths.
|
||||
- Add force-start, Herobrine selection, shard-drop, and vote commands.
|
||||
- Add scoreboards, colored name tags, map voting, configurable maps, and
|
||||
permission-gated kits.
|
||||
- Refactor the 2021 prototype into the OG server target and rebalance combat,
|
||||
shards, kits, abilities, sounds, timers, and scoring.
|
||||
- Fix Bat Bomb kill attribution, totem behavior, shard handling, game startup,
|
||||
world cleanup, and numerous pre-release gameplay edge cases.
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
Remake of HiveMC's "The Herobrine!" v2 for Purpur `1.19.4`, using `MyWorlds` for world management.
|
||||
|
||||
Current version: `1.5.8` ([changelog](CHANGELOG.md)).
|
||||
|
||||
## Requirements
|
||||
- Purpur `1.19.4`
|
||||
- `ProtocolLib`
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ kotlin { jvmToolchain(17) }
|
||||
/* ----------------------------- Metadata ------------------------------ */
|
||||
group = "uk.hotten.herobrine"
|
||||
|
||||
version = "1.5.7"
|
||||
version = "1.5.8"
|
||||
|
||||
val apiVersion = "1.19" // Minecraft server target version.
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package uk.hotten.herobrine.game;
|
||||
|
||||
import com.bergerkiller.bukkit.mw.MyWorlds;
|
||||
import io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent;
|
||||
import io.papermc.paper.event.player.AsyncChatEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -43,9 +46,10 @@ import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||
import org.bukkit.event.player.PlayerVelocityEvent;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
import uk.hotten.herobrine.HerobrinePluginOG;
|
||||
import uk.hotten.herobrine.chat.LocalLobbyChatDelivery;
|
||||
import uk.hotten.herobrine.chat.LobbyChatDelivery;
|
||||
@@ -70,6 +74,7 @@ public class GMListener implements Listener {
|
||||
private LobbyChatDelivery lobbyChatDelivery;
|
||||
private ArrayList<Player> kitCooldown = new ArrayList<>();
|
||||
private Set<UUID> returningToMainOnLogin = ConcurrentHashMap.newKeySet();
|
||||
private Map<UUID, Integer> suppressedHerobrineKnockback = new HashMap<>();
|
||||
|
||||
public GMListener(GameManager gm, GameLobby gl) {
|
||||
|
||||
@@ -655,7 +660,7 @@ public class GMListener implements Listener {
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOWEST)
|
||||
public void onDamage(EntityDamageByEntityEvent event) {
|
||||
public void onCombatAuthorization(EntityDamageByEntityEvent event) {
|
||||
|
||||
if (!event.getEntity().getWorld().getName().startsWith(gameLobby.getLobbyId()))
|
||||
return;
|
||||
@@ -674,33 +679,12 @@ public class GMListener implements Listener {
|
||||
|
||||
}
|
||||
|
||||
// Allows arrow damage
|
||||
if (event.getEntity() instanceof Player && event.getDamager() instanceof Arrow) { // If the damaged is a player
|
||||
if (event.getEntity() instanceof Player && event.getDamager() instanceof Arrow) {
|
||||
|
||||
// and damager is an arrow
|
||||
Player player = (Player) event.getEntity();
|
||||
Player attacker = (Player) ((Arrow) event.getDamager()).getShooter();
|
||||
|
||||
if (!(gameManager.isSurvivor(attacker) && gameManager.isHerobrine(player))) { // Evals to
|
||||
|
||||
// true if
|
||||
// either a)
|
||||
// the
|
||||
// attacker
|
||||
// isnt a
|
||||
// survivor
|
||||
// b)
|
||||
// the damaged isnt herobrine
|
||||
Player attacker = getPlayerAttacker(event);
|
||||
if (attacker == null || !(gameManager.isSurvivor(attacker) && gameManager.isHerobrine(player)))
|
||||
event.setCancelled(true);
|
||||
|
||||
} else {
|
||||
|
||||
event.setCancelled(true);
|
||||
event.getDamager().remove();
|
||||
player.damage(4.5, attacker);
|
||||
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
@@ -709,9 +693,7 @@ public class GMListener implements Listener {
|
||||
if (event.getEntity() instanceof Player && event.getDamager() instanceof Wolf) {
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
if (gameManager.isHerobrine(player))
|
||||
event.setDamage(6);
|
||||
else
|
||||
if (!gameManager.isHerobrine(player))
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
|
||||
@@ -737,40 +719,135 @@ public class GMListener implements Listener {
|
||||
Player player = (Player) event.getEntity();
|
||||
Player attacker = (Player) event.getDamager();
|
||||
|
||||
if (gameManager.isSurvivor(attacker)) { // If attacker is a survivor
|
||||
|
||||
if (gameManager.isSurvivor(player)) { // If the person taking damage is also a survivor, cancel
|
||||
if (gameManager.isSurvivor(attacker)) {
|
||||
|
||||
if (gameManager.isSurvivor(player))
|
||||
event.setCancelled(true);
|
||||
return;
|
||||
|
||||
}
|
||||
} else if (!gameManager.isHerobrine(attacker)) {
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onCombatDamage(EntityDamageByEntityEvent event) {
|
||||
|
||||
if (!event.getEntity().getWorld().getName().startsWith(gameLobby.getLobbyId()))
|
||||
return;
|
||||
|
||||
if (event.getEntity() instanceof Player && event.getDamager() instanceof Arrow) {
|
||||
|
||||
event.setDamage(4.5);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (event.getEntity() instanceof Player && event.getDamager() instanceof Wolf) {
|
||||
|
||||
event.setDamage(6);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!(event.getEntity() instanceof Player) || !(event.getDamager() instanceof Player))
|
||||
return;
|
||||
|
||||
Player attacker = (Player) event.getDamager();
|
||||
if (gameManager.isSurvivor(attacker)) {
|
||||
|
||||
// Attacking THB
|
||||
double damage = gameManager.getHerobrineHitDamage(attacker.getInventory().getItemInMainHand().getType(),
|
||||
attacker.hasPotionEffect(PotionEffectType.INCREASE_DAMAGE));
|
||||
if (damage != -1)
|
||||
event.setDamage(damage);
|
||||
|
||||
PlayerUtil.animateHbHit(gameLobby, player.getLocation());
|
||||
|
||||
// Delay the velocity change by a tick so it actually works
|
||||
Bukkit.getServer().getScheduler().runTaskLater(gameManager.getPlugin(),
|
||||
() -> player.setVelocity(new Vector(0, 0, 0)), 1);
|
||||
|
||||
} else if (gameManager.isHerobrine(attacker)) {
|
||||
|
||||
PlayerUtil.playSoundAt(attacker.getLocation(), Sound.ENTITY_GHAST_AMBIENT, 1f, 0f);
|
||||
double damage = gameManager.getSurvivorHitDamage(attacker.getInventory().getItemInMainHand().getType());
|
||||
if (damage != -1)
|
||||
event.setDamage(damage);
|
||||
|
||||
} else {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
public void onAcceptedCombatDamage(EntityDamageByEntityEvent event) {
|
||||
|
||||
if (!event.getEntity().getWorld().getName().startsWith(gameLobby.getLobbyId()))
|
||||
return;
|
||||
|
||||
if (!(event.getEntity() instanceof Player) || event.getFinalDamage() <= 0)
|
||||
return;
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
Player attacker = getPlayerAttacker(event);
|
||||
if (attacker == null)
|
||||
return;
|
||||
|
||||
if (gameManager.isSurvivor(attacker) && gameManager.isHerobrine(player))
|
||||
PlayerUtil.animateHbHit(gameLobby, player.getLocation());
|
||||
else if (gameManager.isHerobrine(attacker) && gameManager.isSurvivor(player))
|
||||
PlayerUtil.playSoundAt(attacker.getLocation(), Sound.ENTITY_GHAST_AMBIENT, 1f, 0f);
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST)
|
||||
public void onHerobrineKnockback(EntityPushedByEntityAttackEvent event) {
|
||||
|
||||
if (!event.getEntity().getWorld().getName().startsWith(gameLobby.getLobbyId()))
|
||||
return;
|
||||
|
||||
if (!(event.getEntity() instanceof Player))
|
||||
return;
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
if (!gameManager.isHerobrine(player) || gameManager.getShardCount() == 3)
|
||||
return;
|
||||
|
||||
// Purpur owns the server-side acceleration here. KnockbackSync can still supply
|
||||
// a legacy
|
||||
// velocity packet, so retain this marker until the matching velocity event is
|
||||
// handled.
|
||||
event.setCancelled(true);
|
||||
|
||||
UUID playerId = player.getUniqueId();
|
||||
int suppressionVersion = suppressedHerobrineKnockback.merge(playerId, 1, Integer::sum);
|
||||
Bukkit.getServer().getScheduler().runTaskLater(gameManager.getPlugin(), () -> {
|
||||
|
||||
Integer activeVersion = suppressedHerobrineKnockback.get(playerId);
|
||||
if (activeVersion != null && activeVersion == suppressionVersion)
|
||||
suppressedHerobrineKnockback.remove(playerId);
|
||||
|
||||
}, 2);
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||
public void onHerobrineVelocity(PlayerVelocityEvent event) {
|
||||
|
||||
if (suppressedHerobrineKnockback.containsKey(event.getPlayer().getUniqueId()))
|
||||
event.setCancelled(true);
|
||||
|
||||
}
|
||||
|
||||
private Player getPlayerAttacker(EntityDamageByEntityEvent event) {
|
||||
|
||||
if (event.getDamager() instanceof Player)
|
||||
return (Player) event.getDamager();
|
||||
|
||||
if (event.getDamager() instanceof Arrow) {
|
||||
|
||||
ProjectileSource shooter = ((Arrow) event.getDamager()).getShooter();
|
||||
if (shooter instanceof Player)
|
||||
return (Player) shooter;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.LOW)
|
||||
@@ -993,7 +1070,11 @@ public class GMListener implements Listener {
|
||||
|
||||
} else if (gameManager.getGameState() == GameState.LIVE || gameManager.getGameState() == GameState.ENDING) {
|
||||
|
||||
if (gameManager.isHerobrine(player) || gameManager.isSurvivor(player)) {
|
||||
if (gameManager.isHerobrine(player)) {
|
||||
|
||||
formattedMessage = "&4THE HEROBRINE &8| &r&l" + rawMessage;
|
||||
|
||||
} else if (gameManager.isSurvivor(player)) {
|
||||
|
||||
formattedMessage = rank.getDisplay() + " " + endMessage;
|
||||
|
||||
|
||||
@@ -714,10 +714,11 @@ public class GameManager {
|
||||
|
||||
herobrine.getInventory().clear();
|
||||
|
||||
giveVials(-1, 2);
|
||||
|
||||
GUIItem item = new GUIItem(Material.IRON_SWORD).displayName("&bSword of &lChances!").unbreakable(true);
|
||||
herobrine.getInventory().addItem(item.build());
|
||||
herobrine.getInventory().setItem(0, item.build());
|
||||
giveVials(1, 2);
|
||||
herobrine.getInventory().setItem(2, new ItemStack(Material.GOLDEN_APPLE, 1));
|
||||
herobrine.getInventory().setHeldItemSlot(0);
|
||||
|
||||
herobrine.removePotionEffect(PotionEffectType.DAMAGE_RESISTANCE);
|
||||
break;
|
||||
|
||||
@@ -10,6 +10,7 @@ depend:
|
||||
softdepend:
|
||||
- My_Worlds
|
||||
- IllegalStack-OG
|
||||
- KnockbackSync-OG
|
||||
|
||||
commands:
|
||||
hbsetherobrine:
|
||||
|
||||
Reference in new issue
Block a user