BetterSpoofer v1.2.0
Changelog
Released June 2026
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆ New Features
- Hologram System (TextDisplay) Holograms are now fully implemented using PacketEvents TextDisplay entities. Each fake player spawns configurable name and ping holograms above their head with billboard CENTER rotation, see-through rendering, and true shadows. Format supports MiniMessage and legacy &x codes with
<name>and<ping>placeholders.- Phantom Fake Player Mode Added
--realand--phantomflags to/spoofer add. Administrators can now explicitly choose between NMS-backed interactive fakes and lightweight packet-only phantoms. Auto-detection remains the default when no flag is specified.- Webhook Rate Limiting Discord webhook integration now uses a batched queue system. Add and remove events are collected and flushed every 3 seconds as grouped messages. A 1.5-second global cooldown prevents all rate limit hits. HTTPS-only enforcement blocks accidental plain-HTTP URLs.
◆ Improvements
- Webhook Daily Summary Precision The daily activity summary now fires exactly at the configured hour in the server's timezone instead of on a random minute. Uses calculated delay instead of hourly polling.
- Command Name Validation
/spoofer addnow validates usernames against Minecraft rules (1-16 chars,A-Za-z0-9_) before attempting spawn. Invalid names are rejected with a clear error message.- Shared Location Resolution Extracted duplicated spawn-location fallback logic into a single
resolveDefaultSpawnLocation()method shared byFakePlayerServiceandPersistenceStore. All three fallback tiers (exact coords, default-world + radius, first-loaded-world) are now maintained in one place.- Hologram Position Sync Holograms now follow fake players during random wandering via
moveAll(). Joining players receive hologram snapshots alongside entity and TAB snapshots.
◆ Fixes
- Entity ID CAS Edge Case Replaced the manual CAS loop in
FakePlayer.nextEntityId()withAtomicInteger.getAndUpdate(), eliminating a potential ID collision when the wrap-around reset raced between concurrent spawn threads.- Scheduler Expired Fake Cleanup Fakes that exceed
max-stay-minutesare now force-removed regardless of the scheduler's paused state. Previously, pausing the scheduler could leave expired fakes lingering indefinitely.- Config Default Mismatch Fixed
look-at-nearby-playersdefaulting totruein code whileconfig.ymlshipped withfalse. Both now default tofalse.- Skin Command Message Key Fixed
/spoofer skinusing a non-existentunknown-subcommandmessage key when an invalid skin source was provided. Now correctly falls back tounknown-command.
◆ Technical Changes
FakePlayerService- added public staticresolveDefaultSpawnLocation(SpooferConfig, Logger)HologramManager- rewritten from stub to full TextDisplay implementation withspawn(),despawn(),sendInitialSnapshot(),moveAll()WebhookSender- added batched queue withaddQueue,removeQueue,flushBatched(),scheduleFlush(), globallastSendMscooldownSpooferCommand- addedMC_NAMEregex pattern,--real/--phantomflag parsing,explicitRealresolution logicFakePlayer-nextEntityId()simplified to singlegetAndUpdate()callDailyCurveScheduler- reordered tick logic: forced expiry removal before paused checkRealPlayerListener- addedhologramManager.sendInitialSnapshot()on player joinFakeEntityManager- addedhologramManager.moveAll()intickWander()PersistenceStore- replaced duplicated location resolution with sharedFakePlayerService.resolveDefaultSpawnLocation()ConfigLoader- correctedlook-at-nearby-playersdefault fromtruetofalse
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
BetterSpoofer by Bishyy - Paper 1.21+ - NMS-backed fake player management
BetterSpoofer
-----------------------------------------
v1.1 - Bug Fix & Stability Update
Bug Fixes
- Floating fake players - NMS ServerPlayer entities have no server-side gravity; the server never simulates physics for player-type entities. The old setGravity(true) call was a no-op. The plugin now calls World.getHighestBlockYAt(x, z) before spawning to snap the fake player directly to the ground. No more floating fakes above trees, roofs or uneven terrain.
- Ghost phantom fakes on restart - On server reload, phantom fakes (real=false) were being restored from persistence.yml and added to the registry and TAB list, but since they have no world entity they were invisible in-game and showed up as ghost entries in /sp list. The plugin now skips phantom fakes entirely on restore; only NMS-backed fakes are brought back on startup.
- Infinite teleport loop on spawn - ViaVersion's pipeline fired a PlayerLoginEvent inside placeNewPlayer, which set awaitingPositionFromClient in the fake player's connection handler. Since the fake never sends a teleport ACK, this field caused the server to re-teleport the entity every tick and would disconnect it after 600 ticks. Fixed by overriding tick() in FakeServerGamePacketListenerImpl to clear the field via reflection before each tick.
- Mojang API 403 on skin lookup - Paper called getUpdatedProfile() asynchronously whenever a GameProfile had no textures property, hitting Mojang's session API and getting a 403 because fake UUIDs do not exist in their database. Fixed by ensuring the GameProfile always has a textures property (using Steve's skin as the default), both on NMS spawn and in the GUI skull renderer.
Improvements
- Ground-snapping config option - The existing gravity-enabled config key is now correctly documented. When true (default), the plugin snaps the fake to the highest solid block at spawn. Set to false to spawn at the exact configured Y coordinate (useful for display builds or mid-air setups).
- LuckPerms async pre-load - LuckPerms user data is now fetched on an async thread before the fake player joins the main thread. Eliminates a potential server-thread stall of up to 2 seconds per fake spawn on servers with large LP databases.
- NMS TAB name format - NMS-backed fakes now correctly display the configured name-format in the TAB list. Previously, placeNewPlayer broadcast the raw player name; a follow-up UPDATE_DISPLAY_NAME packet is now sent immediately after spawn.
- Persistence real-flag - The real flag is now saved and loaded from persistence.yml, allowing the plugin to distinguish NMS-backed fakes from phantom fakes across restarts.
- Skin cache thread safety - Fixed a potential data race in SkinService when multiple async skin requests completed simultaneously. Cache save now holds the lock for the full write duration.
- Scheduler timezone - The daily curve scheduler now correctly reads the configured scheduler.timezone instead of always defaulting to Europe/Warsaw.
- Discord webhook timezone - Webhook timestamps now use the timezone from config. Additionally, non-HTTPS webhook URLs are rejected at load time.
-----------------------------------------
