Per-viewer administrative invisibility for SA-MP and open.mp using Pawn.RakNet.
Author: WroxExM
Repository: github.com/WroxExM/samp-invisibility
License: MIT
- Hide administrators from normal players per viewer.
- Allow staff at
INVISIBLE_VIEW_LEVELor higher to see invisible players. - Safe stream-in/stream-out synchronization and
RPC_SPAWN_PLAYERfiltering. - Optional vehicle, damage, weapon, interaction, and normal-chat restrictions.
- Toggle cooldown, death disable, audit logging, and debug logging.
- No timers or per-frame loops.
Include Pawn.RakNet before invisible.inc:
#include <a_samp>
#include <Pawn.RakNet>
#include "invisible.inc"The library does not define normal server callbacks. It uses this required gamemode callback to read admin levels:
public GetInvisibleAdminLevel(playerid)
{
return PlayerInfo[playerid][pAdmin];
}Define these before including the file:
#define INVISIBLE_ADMIN_LEVEL (6)
#define INVISIBLE_VIEW_LEVEL (6)
#define INVISIBLE_BLOCK_VEHICLES (1)
#define INVISIBLE_BLOCK_DAMAGE (1)
#define INVISIBLE_COOLDOWN (3000)
#define INVISIBLE_DISABLE_ON_DEATH (1)
#define INVISIBLE_BLOCK_NORMAL_CHAT (0)1 enables a restriction, 0 disables it, and cooldown is in milliseconds. Optional INVISIBLE_DEBUG prints changes. Optional INVISIBLE_AUDIT_LOG(playerid, enabled) can connect your audit logger.
SetPlayerInvisible(playerid, bool:toggle);
TogglePlayerInvisible(playerid);
IsPlayerInvisible(playerid);
IsInvisibleAdmin(playerid);
CanSeeInvisiblePlayer(viewerid, targetid);
CanEnableInvisible(playerid);
CheckInvisiblePermission(playerid);TogglePlayerInvisible checks admin permission and cooldown. Call CheckInvisiblePermission after changing a player's admin level.
RefreshInvisiblePlayer(playerid);
RefreshInvisibleViewer(viewerid);
ResetInvisiblePlayer(playerid);
OnInvisiblePlayerConnect(playerid);
OnInvisiblePlayerDisconnect(playerid);
OnInvisiblePlayerSpawn(playerid);
OnInvisiblePlayerDeath(playerid);
OnInvisiblePlayerStreamIn(playerid, forplayerid);
OnInvisiblePlayerStreamOut(playerid, forplayerid);Call the matching lifecycle function from OnPlayerConnect, OnPlayerDisconnect, OnPlayerSpawn, OnPlayerDeath, OnPlayerStreamIn, and OnPlayerStreamOut. Refresh after admin-level, world, or interior changes.
CanUseInvisibleInteraction(playerid);
CanUseInvisibleChat(playerid);
OnInvisiblePlayerText(playerid);
CanUseInvisibleVehicle(playerid);
OnInvisiblePlayerEnterVehicle(playerid, vehicleid, ispassenger);
OnInvisiblePlayerStateChange(playerid, newstate, oldstate);
CanUseInvisibleCombat(playerid);
OnInvisiblePlayerWeaponShot(playerid);
OnInvisiblePlayerGiveDamage(attackerid, damagedid);
OnInvisiblePlayerTakeDamage(playerid, issuerid);Example callback integration:
public OnPlayerText(playerid, text[])
{
#pragma unused text
return OnInvisiblePlayerText(playerid);
}
public OnPlayerStateChange(playerid, newstate, oldstate)
{
OnInvisiblePlayerStateChange(playerid, newstate, oldstate);
return 1;
}
public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
return OnInvisiblePlayerEnterVehicle(playerid, vehicleid, ispassenger);Return the weapon and damage helper results from their corresponding callbacks. Combine the result if another system also uses those callback return values.
SendPlayerRemove(targetid, viewerid);
SendPlayerSpawn(targetid, viewerid);
HideInvisiblePlayer(targetid, viewerid);
ShowInvisiblePlayer(targetid, viewerid);
ShouldBlockInvisibleDamage(attackerid, damagedid);
LogInvisibleChange(playerid, bool:enabled);Normally use lifecycle and refresh functions instead of sending packets directly.
Before showing labels, nametags, GPS markers, AFK indicators, tracking results, attached objects, voice streams, or custom UI, check:
if (CanSeeInvisiblePlayer(viewerid, targetid))
{
// Show the custom player-related element.
}Invisibility is runtime-only and resets on disconnect. Plugin-owned objects, labels, voice streams, and tracking systems need their own integration. Test with the exact client, server, and Pawn.RakNet versions used by your project.
Issues and pull requests are welcome at the GitHub repository.