Ability to bypass Damage and Hub Inventory.
This commit is contained in:
+14
-1
@@ -11,6 +11,9 @@ HubPlugin (entry)
|
||||
+-- CancelBreakBlockSystem ECS: cancels BreakBlockEvent in hub worlds
|
||||
+-- CancelPlaceBlockSystem ECS: cancels PlaceBlockEvent in hub worlds
|
||||
+-- CancelDamageBlockSystem ECS: cancels DamageBlockEvent in hub worlds
|
||||
+-- CancelDamageSystem ECS: cancels entity damage in hub worlds
|
||||
+-- DamageBypassFlags shared temporary opt-in for combat plugins
|
||||
+-- HubBypassFlags shared temporary opt-in for normal hub controls
|
||||
```
|
||||
|
||||
## What runs when
|
||||
@@ -21,7 +24,7 @@ HubPlugin (entry)
|
||||
2. Register `AllWorldsLoadedEvent` listener -> `HubWorldService.applyToConfiguredWorlds()`. Also call it once now (covers reload after worlds already loaded).
|
||||
3. Register `PlayerReadyEvent` -> `HubJoinListener.onReady(...)`.
|
||||
4. Register `AddPlayerToWorldEvent` / `RemovedPlayerFromWorldEvent` -> `HubMessageListener` (replaces Hytale's default join/leave broadcast text, or suppresses entirely).
|
||||
5. Register the three `EntityEventSystem` subclasses (one per protected event) with `getEntityStoreRegistry()`. Each is gated on its own `protection.disable_*` toggle.
|
||||
5. Register the protection ECS systems with `getEntityStoreRegistry()`. Each is gated on its own `protection.disable_*` toggle.
|
||||
|
||||
`HubWorldService` walks every configured hub world and sets:
|
||||
- `isPvpEnabled = false` (if `protection.disable_pvp`)
|
||||
@@ -29,6 +32,10 @@ HubPlugin (entry)
|
||||
|
||||
Only the flags that actually differ from current state are written. `markChanged()` is called once if anything changed.
|
||||
|
||||
`DamageBypassFlags` lets another plugin temporarily allow incoming damage for a player in a hub world, for example while that player is inside a duel arena. For hub-world duel arenas, keep `disable_damage=true` and `disable_pvp=false`: Hytale world PvP must be enabled, then Hub cancels damage unless the victim is bypassed.
|
||||
|
||||
`HubBypassFlags` lets another plugin temporarily bypass hub control behavior for a player. Bypassed players keep normal HUD/hotbar controls and do not trigger the hub menu from hotbar slot changes.
|
||||
|
||||
### Per-player, on `PlayerReadyEvent`
|
||||
|
||||
For each player who's just finished joining a hub world (i.e. their world is in `worlds.hub_worlds`, or that list is empty meaning "every world on this server is a hub"):
|
||||
@@ -61,6 +68,12 @@ Hytale's player-add system fires `AddPlayerToWorldEvent` with a default `joinMes
|
||||
| [protection/CancelBreakBlockSystem.java](../src/main/java/net/kewwbec/hub/protection/CancelBreakBlockSystem.java) | ECS: cancel BreakBlockEvent in hubs. |
|
||||
| [protection/CancelPlaceBlockSystem.java](../src/main/java/net/kewwbec/hub/protection/CancelPlaceBlockSystem.java) | ECS: cancel PlaceBlockEvent in hubs. |
|
||||
| [protection/CancelDamageBlockSystem.java](../src/main/java/net/kewwbec/hub/protection/CancelDamageBlockSystem.java) | ECS: cancel DamageBlockEvent in hubs. |
|
||||
| [protection/CancelDamageSystem.java](../src/main/java/net/kewwbec/hub/protection/CancelDamageSystem.java) | ECS: cancel entity damage in hubs. |
|
||||
| [protection/DamageBypassFlags.java](../src/main/java/net/kewwbec/hub/protection/DamageBypassFlags.java) | Shared temporary damage opt-in. |
|
||||
| [protection/HubBypassFlags.java](../src/main/java/net/kewwbec/hub/protection/HubBypassFlags.java) | Shared temporary hub-control opt-in. |
|
||||
| [service/HubWorldService.java](../src/main/java/net/kewwbec/hub/service/HubWorldService.java) | Applies world-level flags (PvP, fall damage). |
|
||||
| [listener/HubJoinListener.java](../src/main/java/net/kewwbec/hub/listener/HubJoinListener.java) | PlayerReadyEvent: spawn-teleport + private welcome. |
|
||||
| [listener/HubMessageListener.java](../src/main/java/net/kewwbec/hub/listener/HubMessageListener.java) | Replaces/suppresses Hytale's built-in join/leave broadcast. |
|
||||
| [listener/HubInventoryListener.java](../src/main/java/net/kewwbec/hub/listener/HubInventoryListener.java) | PlayerReadyEvent: clear inventory, set resting slot, hide HUD. |
|
||||
| [listener/MenuItemActivateSystem.java](../src/main/java/net/kewwbec/hub/listener/MenuItemActivateSystem.java) | ECS: hotbar slot hub-menu trigger. |
|
||||
| [listener/HubHudVisibility.java](../src/main/java/net/kewwbec/hub/listener/HubHudVisibility.java) | Applies hidden/default hub HUD layouts. |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"disable_block_break": true,
|
||||
"disable_block_place": true,
|
||||
"disable_block_damage": true,
|
||||
"disable_pvp": true,
|
||||
"disable_pvp": false,
|
||||
"disable_fall_damage": true,
|
||||
"apply_world_config": true
|
||||
},
|
||||
@@ -45,10 +45,13 @@
|
||||
| `disable_block_break` | `true` | Register an ECS system that cancels `BreakBlockEvent` in hub worlds. Players cannot mine blocks. |
|
||||
| `disable_block_place` | `true` | Register an ECS system that cancels `PlaceBlockEvent` in hub worlds. Players cannot place blocks. |
|
||||
| `disable_block_damage` | `true` | Register an ECS system that cancels `DamageBlockEvent` in hub worlds. Blocks cannot be damaged (the partial-damage step before a break). |
|
||||
| `disable_damage` | `true` | Register an ECS system that cancels entity damage in hub worlds unless the victim has `DamageBypassFlags`. Keep this on for duel arenas in hub worlds. |
|
||||
| `disable_pvp` | `true` | Apply `WorldConfig.setPvpEnabled(false)` on the hub world. Players can't damage each other. |
|
||||
| `disable_fall_damage` | `true` | Apply `WorldConfig.setFallDamageEnabled(false)`. Players don't take fall damage. |
|
||||
| `apply_world_config` | `true` | Master toggle for the world-flag application (PvP + fall damage). Set false if you want to manage these flags yourself in Hytale's world config and have Hub leave them alone. Does NOT affect the ECS block protections - toggle those individually via the `disable_block_*` flags. |
|
||||
|
||||
For duel arenas inside a hub world, use `disable_damage=true` and `disable_pvp=false`. Hytale needs world PvP enabled for player-vs-player damage to exist; Hub then blocks normal hub damage and lets duel victims through via `DamageBypassFlags`.
|
||||
|
||||
> Note: Hytale's `GameMode` only has `Adventure` and `Creative`. `Adventure` is the equivalent of Minecraft's *Survival* (players can still break/place). There is no gamemode that prevents block actions, which is why hub protection uses ECS event cancellation instead.
|
||||
|
||||
### messages
|
||||
|
||||
@@ -7,8 +7,9 @@ What Hub v1 protects against, and what it doesn't.
|
||||
- **Block break**: `CancelBreakBlockSystem` cancels `BreakBlockEvent` for any breaker entity inside a hub world.
|
||||
- **Block place**: `CancelPlaceBlockSystem` cancels `PlaceBlockEvent` likewise.
|
||||
- **Block damage** (the partial-damage ticks before a break): `CancelDamageBlockSystem` cancels `DamageBlockEvent`.
|
||||
- **PvP**: `WorldConfig.isPvpEnabled = false`. Players can't damage other players in the hub world.
|
||||
- **PvP**: Players can't damage other players in the hub world unless another plugin temporarily opts them into damage.
|
||||
- **Fall damage**: `WorldConfig.isFallDamageEnabled = false`.
|
||||
- **Hub controls**: Hub can hide HUD elements, keep players on the resting hotbar slot, and open the hub menu from the trigger slot unless another plugin temporarily opts the player into normal hub controls.
|
||||
- **Spawn location**: every joining player is teleported to the spawn point. They can't end up somewhere weird from a previous session.
|
||||
- **Duplicate join/leave broadcasts**: Hub replaces (or suppresses) Hytale's built-in `AddPlayerToWorldEvent.joinMessage` and `RemovedPlayerFromWorldEvent.leaveMessage` rather than emitting alongside them.
|
||||
|
||||
@@ -31,6 +32,14 @@ In some Minecraft-style servers `GameMode.Adventure` prevents block break/place.
|
||||
|
||||
Hub doesn't know about other Hub servers. Each Hub server protects its own world(s). If you have multiple lobbies, install Hub on each. Config can differ per server.
|
||||
|
||||
## Runtime bypass flags
|
||||
|
||||
`DamageBypassFlags` is for temporary combat inside hub worlds. It lets incoming damage through for opted-in victims. Bypassed attackers can also damage non-player entities, which keeps PvE training working without allowing them to hit ordinary hub players.
|
||||
|
||||
`HubBypassFlags` is for temporary normal controls inside hub worlds. It stops Hub from hiding HUD, forcing the resting hotbar slot, or opening the hub menu from slot changes for opted-in players.
|
||||
|
||||
Both are in-memory runtime flags with a JVM system-property fallback. Plugins should enable them only while needed and clear them when the player leaves the mode, disconnects, or the match ends.
|
||||
|
||||
## Timing of world flag application
|
||||
|
||||
`AllWorldsLoadedEvent` fires after Hytale finishes loading worlds at boot. Hub's `applyToConfiguredWorlds()` runs then. There's a brief window during boot (before `AllWorldsLoadedEvent`) where the world's flags are whatever was saved on disk. Connecting players in that window get the saved-disk flags. In practice nobody connects during this window because the server isn't accepting players yet.
|
||||
|
||||
@@ -92,8 +92,7 @@ public final class HubPlugin extends JavaPlugin {
|
||||
protected void start() {
|
||||
this.worldService = new HubWorldService(config);
|
||||
HubJoinListener joinListener = new HubJoinListener(config, filter);
|
||||
HubMessageListener messageListener = new HubMessageListener(config, filter,
|
||||
net.kewwbec.networkcore.NetworkCore.getInstance().getVanishService());
|
||||
HubMessageListener messageListener = new HubMessageListener(config, filter, resolveVanishService());
|
||||
this.hudVisibility = new HubHudVisibility(config.inventory.hidden_hud_components);
|
||||
hudVisibility.setChangeListener(this::onHudVisibilityChange);
|
||||
HubInventoryListener inventoryListener = new HubInventoryListener(config.inventory, filter, hudVisibility);
|
||||
@@ -215,6 +214,21 @@ public final class HubPlugin extends JavaPlugin {
|
||||
}
|
||||
}
|
||||
|
||||
@javax.annotation.Nullable
|
||||
private Object resolveVanishService() {
|
||||
try {
|
||||
Class<?> vanishType = Class.forName("net.kewwbec.networkcore.api.VanishService");
|
||||
Object core = NetworkCore.getInstance();
|
||||
try {
|
||||
return core.getClass().getMethod("getVanishService").invoke(core);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
return core.getClass().getMethod("findService", Class.class).invoke(core, vanishType);
|
||||
}
|
||||
} catch (ReflectiveOperationException | LinkageError ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showXpHud(@Nonnull PlayerReadyEvent event) {
|
||||
Ref<EntityStore> ref = event.getPlayerRef();
|
||||
if (ref == null || !ref.isValid()) return;
|
||||
|
||||
@@ -35,7 +35,7 @@ public final class HubConfig {
|
||||
/** Cancel DropItemEvent in hub worlds (Q/throw is suppressed for every player). */
|
||||
public boolean disable_item_drop = true;
|
||||
/** WorldConfig.isPvpEnabled = false on hub worlds. */
|
||||
public boolean disable_pvp = true;
|
||||
public boolean disable_pvp = false;
|
||||
/** WorldConfig.isFallDamageEnabled = false on hub worlds. */
|
||||
public boolean disable_fall_damage = true;
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.HudComponent;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.UpdateVisibleHudComponents;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import net.kewwbec.hub.protection.HubBypassFlags;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -63,6 +64,10 @@ public final class HubHudVisibility {
|
||||
|
||||
public void applyHidden(@Nonnull PlayerRef ref) {
|
||||
if (!anyHidden) return;
|
||||
if (HubBypassFlags.isEnabled(ref.getUuid())) {
|
||||
showAll(ref);
|
||||
return;
|
||||
}
|
||||
if (globalForceShow) {
|
||||
showAll(ref);
|
||||
return;
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import net.kewwbec.hub.config.HubConfig;
|
||||
import net.kewwbec.hub.protection.HubBypassFlags;
|
||||
import net.kewwbec.hub.protection.HubWorldFilter;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -36,6 +37,7 @@ public final class HubInventoryListener {
|
||||
Store<EntityStore> store = ref.getStore();
|
||||
PlayerRef playerRef = store.getComponent(ref, PlayerRef.getComponentType());
|
||||
if (playerRef == null) return;
|
||||
if (HubBypassFlags.isEnabled(playerRef.getUuid())) return;
|
||||
World world = store.getExternalData().getWorld();
|
||||
if (world == null || !filter.isHub(world)) return;
|
||||
|
||||
|
||||
@@ -9,12 +9,11 @@ import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import net.kewwbec.hub.config.HubConfig;
|
||||
import net.kewwbec.hub.protection.HubWorldFilter;
|
||||
import net.kewwbec.hub.util.HubMessageFormatter;
|
||||
import net.kewwbec.networkcore.api.VanishService;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.UUID;
|
||||
// Nullable already imported above
|
||||
|
||||
/**
|
||||
* Replaces (or suppresses) Hytale's default join/leave broadcasts on hub worlds.
|
||||
@@ -28,20 +27,22 @@ public final class HubMessageListener {
|
||||
|
||||
private final HubConfig config;
|
||||
private final HubWorldFilter filter;
|
||||
@Nullable private final VanishService vanish;
|
||||
@Nullable private final Object vanish;
|
||||
@Nullable private final Method isVanished;
|
||||
|
||||
public HubMessageListener(@Nonnull HubConfig config,
|
||||
@Nonnull HubWorldFilter filter,
|
||||
@Nullable VanishService vanish) {
|
||||
@Nullable Object vanish) {
|
||||
this.config = config;
|
||||
this.filter = filter;
|
||||
this.vanish = vanish;
|
||||
this.isVanished = resolveIsVanished(vanish);
|
||||
}
|
||||
|
||||
public void onAdd(@Nonnull AddPlayerToWorldEvent event) {
|
||||
if (!filter.isHub(event.getWorld())) return;
|
||||
Subject s = resolveSubject(event.getHolder());
|
||||
if (vanish != null && vanish.isVanished(s.uuid)) {
|
||||
if (isVanished(s.uuid)) {
|
||||
event.setBroadcastJoinMessage(false);
|
||||
return;
|
||||
}
|
||||
@@ -57,7 +58,7 @@ public final class HubMessageListener {
|
||||
public void onRemove(@Nonnull RemovedPlayerFromWorldEvent event) {
|
||||
if (!filter.isHub(event.getWorld())) return;
|
||||
Subject s = resolveSubject(event.getHolder());
|
||||
if (vanish != null && vanish.isVanished(s.uuid)) {
|
||||
if (isVanished(s.uuid)) {
|
||||
event.setBroadcastLeaveMessage(false);
|
||||
return;
|
||||
}
|
||||
@@ -78,5 +79,29 @@ public final class HubMessageListener {
|
||||
return new Subject(ref.getUsername(), ref.getUuid());
|
||||
}
|
||||
|
||||
private boolean isVanished(@Nonnull UUID uuid) {
|
||||
if (vanish == null || isVanished == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Object result = isVanished.invoke(vanish, uuid);
|
||||
return result instanceof Boolean b && b;
|
||||
} catch (ReflectiveOperationException | RuntimeException ignored) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Method resolveIsVanished(@Nullable Object vanish) {
|
||||
if (vanish == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return vanish.getClass().getMethod("isVanished", UUID.class);
|
||||
} catch (NoSuchMethodException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private record Subject(@Nonnull String name, @Nonnull UUID uuid) {}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import net.kewwbec.hub.config.HubConfig;
|
||||
import net.kewwbec.hub.koth.KothService;
|
||||
import net.kewwbec.hub.protection.HubBypassFlags;
|
||||
import net.kewwbec.hub.protection.HubWorldFilter;
|
||||
import net.kewwbec.hub.service.BuildModeService;
|
||||
import net.kewwbec.hub.ui.HubMenuPage;
|
||||
@@ -72,6 +73,7 @@ public final class MenuItemActivateSystem extends EntityEventSystem<EntityStore,
|
||||
|
||||
PlayerRef playerRef = chunk.getComponent(chunkSlot, PlayerRef.getComponentType());
|
||||
if (playerRef == null) return;
|
||||
if (HubBypassFlags.isEnabled(playerRef.getUuid())) return;
|
||||
if (koth != null && koth.isInActiveArena(playerRef.getUuid())) return;
|
||||
if (buildMode.isOn(playerRef.getUuid())) return;
|
||||
if (hudVisibility != null && hudVisibility.isGlobalForceShow()) return;
|
||||
|
||||
@@ -2,6 +2,7 @@ package net.kewwbec.hub.protection;
|
||||
|
||||
import com.hypixel.hytale.component.ArchetypeChunk;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.SystemGroup;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
@@ -51,8 +52,12 @@ public final class CancelDamageSystem extends DamageEventSystem {
|
||||
World world = store.getExternalData().getWorld();
|
||||
if (!filter.isHub(world)) return;
|
||||
|
||||
PlayerRef victim = chunk.getComponent(chunkSlot, PlayerRef.getComponentType());
|
||||
if (hasDamageBypass(victim) || allowsNpcDamageFromBypassedSource(victim, event, store)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (koth != null) {
|
||||
PlayerRef victim = chunk.getComponent(chunkSlot, PlayerRef.getComponentType());
|
||||
if (victim != null && koth.isInArena(victim.getUuid())) {
|
||||
event.setAmount(0f);
|
||||
amplifyKnockback(event, koth.getKnockbackMultiplier());
|
||||
@@ -63,6 +68,28 @@ public final class CancelDamageSystem extends DamageEventSystem {
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
private static boolean hasDamageBypass(@Nullable PlayerRef player) {
|
||||
return player != null && DamageBypassFlags.isEnabled(player.getUuid());
|
||||
}
|
||||
|
||||
private static boolean allowsNpcDamageFromBypassedSource(@Nullable PlayerRef victim, @Nonnull Damage event, @Nonnull Store<EntityStore> store) {
|
||||
if (victim != null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Damage.Source source = event.getSource();
|
||||
if (!(source instanceof Damage.EntitySource entitySource)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Ref<EntityStore> sourceRef = entitySource.getRef();
|
||||
if (sourceRef == null || !sourceRef.isValid() || sourceRef.getStore() != store) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasDamageBypass(store.getComponent(sourceRef, PlayerRef.getComponentType()));
|
||||
}
|
||||
|
||||
private static void amplifyKnockback(@Nonnull Damage event, double multiplier) {
|
||||
if (multiplier == 1.0) return;
|
||||
KnockbackComponent kb = event.getIfPresentMetaObject(Damage.KNOCKBACK_COMPONENT);
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package net.kewwbec.hub.protection;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Shared runtime flags for plugins that intentionally need damage in hub-protected worlds.
|
||||
* Owners are tracked independently so multiple plugins can opt the same player in safely.
|
||||
*/
|
||||
public final class DamageBypassFlags {
|
||||
public static final String SYSTEM_PROPERTY = "hytale.damageBypass.players";
|
||||
private static final String DEFAULT_OWNER = "unknown";
|
||||
private static final Map<UUID, Set<String>> ENABLED_BY_OWNER = new ConcurrentHashMap<>();
|
||||
|
||||
private DamageBypassFlags() {
|
||||
}
|
||||
|
||||
public static void enable(@Nonnull UUID playerId, @Nullable String owner) {
|
||||
ENABLED_BY_OWNER.computeIfAbsent(playerId, ignored -> ConcurrentHashMap.newKeySet())
|
||||
.add(normalizeOwner(owner));
|
||||
}
|
||||
|
||||
public static void disable(@Nonnull UUID playerId, @Nullable String owner) {
|
||||
Set<String> owners = ENABLED_BY_OWNER.get(playerId);
|
||||
if (owners == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
owners.remove(normalizeOwner(owner));
|
||||
if (owners.isEmpty()) {
|
||||
ENABLED_BY_OWNER.remove(playerId, owners);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear(@Nonnull UUID playerId) {
|
||||
ENABLED_BY_OWNER.remove(playerId);
|
||||
}
|
||||
|
||||
public static boolean isEnabled(@Nonnull UUID playerId) {
|
||||
Set<String> owners = ENABLED_BY_OWNER.get(playerId);
|
||||
return (owners != null && !owners.isEmpty()) || systemPropertyContains(playerId);
|
||||
}
|
||||
|
||||
private static String normalizeOwner(@Nullable String owner) {
|
||||
return owner == null || owner.isBlank() ? DEFAULT_OWNER : owner.trim().toLowerCase(java.util.Locale.ROOT);
|
||||
}
|
||||
|
||||
private static boolean systemPropertyContains(@Nonnull UUID playerId) {
|
||||
String value = System.getProperty(SYSTEM_PROPERTY, "");
|
||||
if (value.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
String needle = playerId.toString();
|
||||
for (String token : value.split(",")) {
|
||||
if (needle.equalsIgnoreCase(token.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package net.kewwbec.hub.protection;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Shared runtime flags for plugins that need normal player controls inside hub-protected worlds.
|
||||
*/
|
||||
public final class HubBypassFlags {
|
||||
public static final String SYSTEM_PROPERTY = "hytale.hubBypass.players";
|
||||
private static final String DEFAULT_OWNER = "unknown";
|
||||
private static final Map<UUID, Set<String>> ENABLED_BY_OWNER = new ConcurrentHashMap<>();
|
||||
|
||||
private HubBypassFlags() {
|
||||
}
|
||||
|
||||
public static void enable(@Nonnull UUID playerId, @Nullable String owner) {
|
||||
ENABLED_BY_OWNER.computeIfAbsent(playerId, ignored -> ConcurrentHashMap.newKeySet())
|
||||
.add(normalizeOwner(owner));
|
||||
}
|
||||
|
||||
public static void disable(@Nonnull UUID playerId, @Nullable String owner) {
|
||||
Set<String> owners = ENABLED_BY_OWNER.get(playerId);
|
||||
if (owners == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
owners.remove(normalizeOwner(owner));
|
||||
if (owners.isEmpty()) {
|
||||
ENABLED_BY_OWNER.remove(playerId, owners);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear(@Nonnull UUID playerId) {
|
||||
ENABLED_BY_OWNER.remove(playerId);
|
||||
}
|
||||
|
||||
public static boolean isEnabled(@Nonnull UUID playerId) {
|
||||
Set<String> owners = ENABLED_BY_OWNER.get(playerId);
|
||||
return (owners != null && !owners.isEmpty()) || systemPropertyContains(playerId);
|
||||
}
|
||||
|
||||
private static String normalizeOwner(@Nullable String owner) {
|
||||
return owner == null || owner.isBlank() ? DEFAULT_OWNER : owner.trim().toLowerCase(java.util.Locale.ROOT);
|
||||
}
|
||||
|
||||
private static boolean systemPropertyContains(@Nonnull UUID playerId) {
|
||||
String value = System.getProperty(SYSTEM_PROPERTY, "");
|
||||
if (value.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
String needle = playerId.toString();
|
||||
for (String token : value.split(",")) {
|
||||
if (needle.equalsIgnoreCase(token.trim())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -51,6 +51,11 @@ public final class RefillStaminaSystem extends EntityTickingSystem<EntityStore>
|
||||
@Nonnull ArchetypeChunk<EntityStore> chunk,
|
||||
@Nonnull Store<EntityStore> store,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer) {
|
||||
PlayerRef player = chunk.getComponent(index, PlayerRef.getComponentType());
|
||||
if (player != null && HubBypassFlags.isEnabled(player.getUuid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
EntityStatMap statMap = chunk.getComponent(index, EntityStatMap.getComponentType());
|
||||
if (statMap == null) return;
|
||||
int staminaIndex = DefaultEntityStatTypes.getStamina();
|
||||
|
||||
Reference in New Issue
Block a user