mirror of
https://github.com/shavitush/bhoptimer.git
synced 2025-12-07 18:38:26 +00:00
Change the behaviour of shavit_misc_resettargetname (#1123)
* Redo shavit_misc_resettargetname Also changes the way event abuses are fixed * Obsolete shavit_misc_resettargetname/classname cvars Also few minor optimisations to the code * Revert the deletion of shavit_misc_resettargetname/classname * Move targetname/classname resets to OnStartPre() forward * Rename shavit_misc_resettargetname cvar * Move event reset code to shavit-zones Also replaces PhysicsCheckForEntityUntouch() function call with PhysicsRemoveTouchedList() * don't hook teleport on bots & some random code-style changes * remove added whitespace Co-authored-by: rtldg <55846624+rtldg@users.noreply.github.com>
This commit is contained in:
parent
d58d3ee1d5
commit
0fee1862c8
@ -1,124 +1,44 @@
|
||||
|
||||
"Map fixes"
|
||||
{
|
||||
"bhop_apathy"
|
||||
{
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
"shavit_misc_resettargetname_main" "apathy"
|
||||
}
|
||||
"bhop_appaisaniceman3"
|
||||
{
|
||||
"shavit_misc_resetclassname_main" "main"
|
||||
"shavit_misc_resetclassname_bonus" "bonus_filter"
|
||||
}
|
||||
"bhop_amaranthglow"
|
||||
{
|
||||
"shavit_zones_prebuilt_visual_offset" "16"
|
||||
}
|
||||
"bhop_blackshit"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "fil_fw"
|
||||
}
|
||||
"bhop_crash_egypt"
|
||||
{
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
"shavit_misc_resettargetname_main" "player"
|
||||
}
|
||||
"bhop_decbble"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "p1"
|
||||
}
|
||||
"bhop_downtown"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "fil_fw"
|
||||
}
|
||||
"bhop_drop"
|
||||
{
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
"shavit_misc_resettargetname_main" "activator_boost"
|
||||
}
|
||||
"bhop_futile"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"bhop_futile_fix"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"bhop_horseshit_5"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "last"
|
||||
}
|
||||
"bhop_interloper"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "l"
|
||||
}
|
||||
"bhop_japan"
|
||||
{
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
"shavit_misc_resetclassname_main" "beginner"
|
||||
}
|
||||
"bhop_kirous"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "state0"
|
||||
}
|
||||
"bhop_lowg"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"bhop_microwave"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"bhop_overthinker"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "noobinside"
|
||||
"shavit_misc_resettargetname_bonus" "filter_bonus"
|
||||
"shavit_misc_resetclassname_main" "cp1"
|
||||
"shavit_misc_resetclassname_bonus" "cp1"
|
||||
}
|
||||
"bhop_overthinker_go"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "noobinside"
|
||||
"shavit_misc_resettargetname_bonus" "filter_bonus"
|
||||
"shavit_misc_resetclassname_main" "cp1"
|
||||
"shavit_misc_resetclassname_bonus" "cp1"
|
||||
}
|
||||
"bhop_shutdown"
|
||||
{
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
"shavit_misc_resettargetname_main" "asdf"
|
||||
}
|
||||
"bhop_space"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"bhop_strafecontrol"
|
||||
{
|
||||
"shavit_zones_extra_spawn_height" "1.0"
|
||||
}
|
||||
"bhop_symbiotic"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "filter_main"
|
||||
"shavit_misc_resettargetname_bonus" "filter_bonus"
|
||||
}
|
||||
"bhop_tranquility"
|
||||
{
|
||||
"shavit_zones_prebuilt_visual_offset" "16"
|
||||
}
|
||||
"kz_bhop_izanami"
|
||||
"bhop_solitude"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"kz_bhop_kairo"
|
||||
{
|
||||
"shavit_misc_resettargetname" "0"
|
||||
}
|
||||
"kz_bhop_sanctum"
|
||||
{
|
||||
"shavit_misc_resetclassname_main" "A1"
|
||||
}
|
||||
"kz_bhop_strafe_comjump2"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "pass1"
|
||||
}
|
||||
"kz_bhop_strafe_comjump2_v2"
|
||||
{
|
||||
"shavit_misc_resettargetname_main" "pass1"
|
||||
"shavit_misc_forcetargetnamereset" "1"
|
||||
}
|
||||
}
|
||||
@ -101,6 +101,13 @@
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x08\x56\x8B\xF1\x8B\x86\xD0\x00\x00\x00"
|
||||
"linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x2C\x8B\x5D\x08\xC7\x44\x24\x04\x01\x00\x00\x00\x89\x1C\x24"
|
||||
}
|
||||
// search string: "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n".
|
||||
// function with one argument is PhysicsRemoveTouchedList
|
||||
"PhysicsRemoveTouchedList"
|
||||
{
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x0C\x57\x8B\xF9\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84"
|
||||
"linux" "\x55\x89\xE5\x57\x56\x53\x83\xEC\x5C\x8B\x55\x08\xC7\x44\x24\x2A\x2A\x2A\x2A\x2A\x89\x14\x24\xE8"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,6 +184,13 @@
|
||||
"windows" "\x55\x8B\xEC\x56\x8B\xF1\xE8\x2A\x2A\x2A\x2A\x8B\x45\x2A\x83\xE8\x02"
|
||||
"linux" "@_ZN12CCSGameRules8TeamFullEi"
|
||||
}
|
||||
// search string: "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n".
|
||||
// function with one argument is PhysicsRemoveTouchedList
|
||||
"PhysicsRemoveTouchedList"
|
||||
{
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\x7D\x08\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84"
|
||||
"linux" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -232,6 +246,13 @@
|
||||
"windows" "\x55\x8B\xEC\x56\x8B\x75\x2A\x85\xF6\x75\x2A\x33\xC0\x5E\x5D\xC3\x8B\x56"
|
||||
"linux" "@_ZN12CTFGameRules15CalcPlayerScoreEP12RoundStats_tP9CTFPlayer"
|
||||
}
|
||||
// search string: "remove 0x%p: %s-%s (%d-%d) [%d in play, %d max]\n".
|
||||
// function with one argument is PhysicsRemoveTouchedList
|
||||
"PhysicsRemoveTouchedList"
|
||||
{
|
||||
"windows" "\x55\x8B\xEC\x83\xEC\x08\x57\x8B\x7D\x08\x8B\x87\x2A\x2A\x2A\x2A\xD1\xE8\xA8\x01\x0F\x84"
|
||||
"linux" "@_ZN11CBaseEntity24PhysicsRemoveTouchedListEPS_"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,6 @@
|
||||
#include <shavit/zones>
|
||||
#include <eventqueuefix>
|
||||
|
||||
#include <shavit/physicsuntouch>
|
||||
#include <shavit/weapon-stocks>
|
||||
|
||||
#pragma newdecls required
|
||||
@ -102,7 +101,7 @@ Convar gCV_AdvertisementInterval = null;
|
||||
Convar gCV_RemoveRagdolls = null;
|
||||
Convar gCV_ClanTag = null;
|
||||
Convar gCV_DropAll = null;
|
||||
Convar gCV_ResetTargetname = null;
|
||||
Convar gCV_ForceTargetnameReset = null;
|
||||
Convar gCV_ResetTargetnameMain = null;
|
||||
Convar gCV_ResetTargetnameBonus = null;
|
||||
Convar gCV_ResetClassnameMain = null;
|
||||
@ -277,7 +276,7 @@ public void OnPluginStart()
|
||||
gCV_RemoveRagdolls = new Convar("shavit_misc_removeragdolls", "1", "Remove ragdolls after death?\n0 - Disabled\n1 - Only remove replay bot ragdolls.\n2 - Remove all ragdolls.", 0, true, 0.0, true, 2.0);
|
||||
gCV_ClanTag = new Convar("shavit_misc_clantag", "{tr}{styletag} :: {time}", "Custom clantag for players.\n0 - Disabled\n{styletag} - style tag.\n{style} - style name.\n{time} - formatted time.\n{tr} - first letter of track.\n{rank} - player rank.\n{cr} - player's chatrank from shavit-chat, trimmed, with no colors", 0);
|
||||
gCV_DropAll = new Convar("shavit_misc_dropall", "1", "Allow all weapons to be dropped?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_ResetTargetname = new Convar("shavit_misc_resettargetname", "1", "Reset the player's targetname and eventqueue upon timer start?\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_ForceTargetnameReset = new Convar("shavit_misc_forcetargetnamereset", "0", "Reset the player's targetname upon timer start?\nRecommended to leave disabled. Enable via per-map configs when necessary.\n0 - Disabled\n1 - Enabled", 0, true, 0.0, true, 1.0);
|
||||
gCV_ResetTargetnameMain = new Convar("shavit_misc_resettargetname_main", "", "What targetname to use when resetting the player. You don't need to touch this");
|
||||
gCV_ResetTargetnameBonus = new Convar("shavit_misc_resettargetname_bonus", "", "What targetname to use when resetting the player (on bonus tracks). You don't need to touch this");
|
||||
gCV_ResetClassnameMain = new Convar("shavit_misc_resetclassname_main", "", "What classname to use when resetting the player. You don't need to touch this");
|
||||
@ -370,8 +369,6 @@ void LoadDHooks()
|
||||
SetFailState("Couldn't get the offset for \"CGameRules::IsSpawnPointValid\" - make sure your gamedata is updated!");
|
||||
}
|
||||
|
||||
LoadPhysicsUntouch(hGameData);
|
||||
|
||||
delete hGameData;
|
||||
}
|
||||
|
||||
@ -2130,16 +2127,6 @@ public Action Command_Specs(int client, int args)
|
||||
return Plugin_Handled;
|
||||
}
|
||||
|
||||
void ClearClientEventsFrame(int serial)
|
||||
{
|
||||
int client = GetClientFromSerial(serial);
|
||||
|
||||
if (client > 0 && gB_Eventqueuefix)
|
||||
{
|
||||
ClearClientEvents(client);
|
||||
}
|
||||
}
|
||||
|
||||
public Action Shavit_OnStartPre(int client)
|
||||
{
|
||||
if (Shavit_GetStyleSettingInt(gI_Style[client], "prespeed") == 0 && GetEntityMoveType(client) == MOVETYPE_NOCLIP)
|
||||
@ -2157,7 +2144,7 @@ public Action Shavit_OnStart(int client)
|
||||
SetClientEventsPaused(client, false);
|
||||
}
|
||||
|
||||
if(gCV_ResetTargetname.BoolValue)
|
||||
if(gCV_ForceTargetnameReset.BoolValue)
|
||||
{
|
||||
char targetname[64];
|
||||
char classname[64];
|
||||
@ -2181,14 +2168,6 @@ public Action Shavit_OnStart(int client)
|
||||
}
|
||||
|
||||
SetEntPropString(client, Prop_Data, "m_iClassname", classname);
|
||||
|
||||
// Used to clear some (mainly basevelocity) events that can be used to boost out of the start zone.
|
||||
if(gB_Eventqueuefix)
|
||||
{
|
||||
ClearClientEvents(client); // maybe unneeded?
|
||||
// The RequestFrame is the on that's actually needed though...
|
||||
RequestFrame(ClearClientEventsFrame, GetClientSerial(client));
|
||||
}
|
||||
}
|
||||
|
||||
return Plugin_Continue;
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
|
||||
#include <shavit/core>
|
||||
#include <shavit/zones>
|
||||
#include <shavit/physicsuntouch>
|
||||
|
||||
#undef REQUIRE_PLUGIN
|
||||
#include <adminmenu>
|
||||
@ -35,6 +36,7 @@
|
||||
#undef REQUIRE_EXTENSIONS
|
||||
#include <cstrike>
|
||||
#include <tf2>
|
||||
#include <eventqueuefix>
|
||||
|
||||
#pragma semicolon 1
|
||||
#pragma newdecls required
|
||||
@ -91,6 +93,8 @@ int gI_GridSnap[MAXPLAYERS+1];
|
||||
bool gB_SnapToWall[MAXPLAYERS+1];
|
||||
bool gB_CursorTracing[MAXPLAYERS+1];
|
||||
|
||||
int gI_LatestTeleportTick[MAXPLAYERS+1];
|
||||
|
||||
// player zone status
|
||||
bool gB_InsideZone[MAXPLAYERS+1][ZONETYPES_SIZE][TRACKS_SIZE];
|
||||
bool gB_InsideZoneID[MAXPLAYERS+1][MAX_ZONES];
|
||||
@ -153,6 +157,12 @@ Handle gH_Forwards_EnterZone = null;
|
||||
Handle gH_Forwards_LeaveZone = null;
|
||||
Handle gH_Forwards_StageMessage = null;
|
||||
|
||||
// sdkcalls
|
||||
Handle gH_PhysicsRemoveTouchedList = null;
|
||||
|
||||
// dhooks
|
||||
DynamicHook gH_TeleportDhook = null;
|
||||
|
||||
// kz support
|
||||
float gF_ClimbButtonCache[MAXPLAYERS+1][TRACKS_SIZE][2][3]; // 0 - location, 1 - angles
|
||||
|
||||
@ -162,6 +172,8 @@ bool gB_StartAnglesOnly[MAXPLAYERS+1][TRACKS_SIZE];
|
||||
float gF_StartPos[MAXPLAYERS+1][TRACKS_SIZE][3];
|
||||
float gF_StartAng[MAXPLAYERS+1][TRACKS_SIZE][3];
|
||||
|
||||
// modules
|
||||
bool gB_Eventqueuefix = false;
|
||||
bool gB_ReplayRecorder = false;
|
||||
|
||||
// custom zone stuff
|
||||
@ -309,6 +321,8 @@ public void OnPluginStart()
|
||||
|
||||
Convar.AutoExecConfig();
|
||||
|
||||
LoadDHooks();
|
||||
|
||||
// misc cvars
|
||||
sv_gravity = FindConVar("sv_gravity");
|
||||
|
||||
@ -332,6 +346,7 @@ public void OnPluginStart()
|
||||
}
|
||||
|
||||
gB_ReplayRecorder = LibraryExists("shavit-replay-recorder");
|
||||
gB_Eventqueuefix = LibraryExists("eventqueuefix");
|
||||
|
||||
if (gB_Late)
|
||||
{
|
||||
@ -343,6 +358,7 @@ public void OnPluginStart()
|
||||
if (IsValidClient(i))
|
||||
{
|
||||
OnClientConnected(i);
|
||||
OnClientPutInServer(i);
|
||||
|
||||
if (AreClientCookiesCached(i) && !IsFakeClient(i))
|
||||
{
|
||||
@ -358,6 +374,70 @@ public void OnPluginEnd()
|
||||
UnloadZones(0);
|
||||
}
|
||||
|
||||
void LoadDHooks()
|
||||
{
|
||||
Handle hGameData = LoadGameConfigFile("shavit.games");
|
||||
|
||||
if (hGameData == null)
|
||||
{
|
||||
SetFailState("Failed to load shavit gamedata");
|
||||
}
|
||||
|
||||
LoadPhysicsUntouch(hGameData);
|
||||
|
||||
if (gEV_Type == Engine_CSGO)
|
||||
{
|
||||
StartPrepSDKCall(SDKCall_Entity);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartPrepSDKCall(SDKCall_Static);
|
||||
}
|
||||
|
||||
if (!PrepSDKCall_SetFromConf(hGameData, SDKConf_Signature, "PhysicsRemoveTouchedList"))
|
||||
{
|
||||
SetFailState("Failed to find \"PhysicsRemoveTouchedList\" signature!");
|
||||
}
|
||||
|
||||
if (gEV_Type != Engine_CSGO)
|
||||
{
|
||||
PrepSDKCall_AddParameter(SDKType_CBaseEntity, SDKPass_Pointer);
|
||||
}
|
||||
|
||||
gH_PhysicsRemoveTouchedList = EndPrepSDKCall();
|
||||
|
||||
if (!gH_PhysicsRemoveTouchedList)
|
||||
{
|
||||
SetFailState("Failed to create sdkcall to \"PhysicsRemoveTouchedList\"!");
|
||||
}
|
||||
|
||||
delete hGameData;
|
||||
|
||||
hGameData = LoadGameConfigFile("sdktools.games");
|
||||
if (hGameData == null)
|
||||
{
|
||||
SetFailState("Failed to load sdktools gamedata");
|
||||
}
|
||||
|
||||
int iOffset = GameConfGetOffset(hGameData, "Teleport");
|
||||
if (iOffset == -1)
|
||||
{
|
||||
SetFailState("Couldn't get the offset for \"Teleport\"!");
|
||||
}
|
||||
|
||||
gH_TeleportDhook = new DynamicHook(iOffset, HookType_Entity, ReturnType_Void, ThisPointer_CBaseEntity);
|
||||
|
||||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||||
gH_TeleportDhook.AddParam(HookParamType_VectorPtr);
|
||||
if (GetEngineVersion() == Engine_CSGO)
|
||||
{
|
||||
gH_TeleportDhook.AddParam(HookParamType_Bool);
|
||||
}
|
||||
|
||||
delete hGameData;
|
||||
}
|
||||
|
||||
public void OnAllPluginsLoaded()
|
||||
{
|
||||
// admin menu
|
||||
@ -380,6 +460,10 @@ public void OnLibraryAdded(const char[] name)
|
||||
{
|
||||
gB_ReplayRecorder = true;
|
||||
}
|
||||
else if (StrEqual(name, "eventqueuefix"))
|
||||
{
|
||||
gB_Eventqueuefix = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnLibraryRemoved(const char[] name)
|
||||
@ -393,6 +477,10 @@ public void OnLibraryRemoved(const char[] name)
|
||||
{
|
||||
gB_ReplayRecorder = false;
|
||||
}
|
||||
else if (StrEqual(name, "eventqueuefix"))
|
||||
{
|
||||
gB_Eventqueuefix = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnConVarChanged(ConVar convar, const char[] oldValue, const char[] newValue)
|
||||
@ -913,6 +1001,16 @@ public void OnMapEnd()
|
||||
delete gH_DrawAllZones;
|
||||
}
|
||||
|
||||
public void OnClientPutInServer(int client)
|
||||
{
|
||||
gI_LatestTeleportTick[client] = 0;
|
||||
|
||||
if (!IsFakeClient(client) && gH_TeleportDhook != null)
|
||||
{
|
||||
gH_TeleportDhook.HookEntity(Hook_Pre, client, DHooks_OnTeleport);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEntityCreated(int entity, const char[] classname)
|
||||
{
|
||||
if(StrEqual(classname, "func_button", false))
|
||||
@ -4192,6 +4290,26 @@ public void CreateZoneEntities(bool only_create_dead_entities)
|
||||
gB_ZoneCreationQueued = false;
|
||||
}
|
||||
|
||||
public MRESReturn DHooks_OnTeleport(int pThis, DHookParam hParams)
|
||||
{
|
||||
if (!IsValidEntity(pThis) || !IsClientInGame(pThis))
|
||||
{
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
if (!hParams.IsNull(1))
|
||||
{
|
||||
gI_LatestTeleportTick[pThis] = GetGameTickCount();
|
||||
}
|
||||
|
||||
return MRES_Ignored;
|
||||
}
|
||||
|
||||
void PhysicsRemoveTouchedList(int client)
|
||||
{
|
||||
SDKCall(gH_PhysicsRemoveTouchedList, client);
|
||||
}
|
||||
|
||||
public void StartTouchPost(int entity, int other)
|
||||
{
|
||||
if(other < 1 || other > MaxClients || gI_EntityZone[entity] == -1 || !gA_ZoneCache[gI_EntityZone[entity]].bZoneInitialized || IsFakeClient(other) ||
|
||||
@ -4324,6 +4442,34 @@ public void TouchPost(int entity, int other)
|
||||
{
|
||||
case Zone_Start:
|
||||
{
|
||||
if (gB_Eventqueuefix)
|
||||
{
|
||||
static int tick_served[MAXPLAYERS + 1];
|
||||
int curr_tick = GetGameTickCount();
|
||||
|
||||
// GAMMACASE: This prevents further abuses related to external events being ran after you teleport from the trigger, with events setup, outside the start zone into the start zone.
|
||||
// This accounts for the io events that might be set inside the start zone trigger in OnStartTouch and wont reset them!
|
||||
// Logic behind this code is that all events in this chain are not instantly fired, so checking if there were teleport from the outside of a start zone in last couple of ticks
|
||||
// and doing PhysicsRemoveTouchedList() now to trigger all OnEndTouch that should happen at the same tick but later and removing them allows further events from OnStartTouch be separated
|
||||
// and be fired after which is the expected and desired effect.
|
||||
// This also kills all ongoing events that were active on the client prior to the teleportation to start and also resets targetname and classname
|
||||
// before the OnStartTouch from triggers in start zone are run, thus preventing the maps to be abusable if they don't have any reset triggers in place
|
||||
if (curr_tick != tick_served[other])
|
||||
{
|
||||
if (gI_LatestTeleportTick[other] <= curr_tick <= gI_LatestTeleportTick[other] + 4)
|
||||
{
|
||||
PhysicsRemoveTouchedList(other);
|
||||
ClearClientEvents(other);
|
||||
|
||||
tick_served[other] = curr_tick;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tick_served[other] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (GetEntPropEnt(other, Prop_Send, "m_hGroundEntity") == -1 && !Shavit_GetStyleSettingBool(Shavit_GetBhopStyle(other), "startinair"))
|
||||
{
|
||||
return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user