Initial fork

This commit is contained in:
Flerp 2025-11-16 17:48:40 -08:00
commit 122d0ab95f
10 changed files with 760 additions and 0 deletions

32
.gitignore vendored Normal file
View File

@ -0,0 +1,32 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 AzerothCore
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
README.md Normal file
View File

@ -0,0 +1,3 @@
# mod-noclip
This mod allows noclip with a command noclip. on / off
(IT's still WIP but it works mostly, btw. you can help me to correcting something with it if you want. Just contact me.

40
apps/ci/ci-codestyle.sh Normal file
View File

@ -0,0 +1,40 @@
#!/bin/bash
set -e
echo "Codestyle check script:"
echo
declare -A singleLineRegexChecks=(
["LOG_.+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above"
["[[:blank:]]$"]="Remove whitespace at the end of the lines above"
["\t"]="Replace tabs with 4 spaces in the lines above"
)
for check in ${!singleLineRegexChecks[@]}; do
echo " Checking RegEx: '${check}'"
if grep -P -r -I -n ${check} src; then
echo
echo "${singleLineRegexChecks[$check]}"
exit 1
fi
done
declare -A multiLineRegexChecks=(
["LOG_[^;]+GetCounter"]="Use ObjectGuid::ToString().c_str() method instead of ObjectGuid::GetCounter() when logging. Check the lines above"
["\n\n\n"]="Multiple blank lines detected, keep only one. Check the files above"
)
for check in ${!multiLineRegexChecks[@]}; do
echo " Checking RegEx: '${check}'"
if grep -Pzo -r -I ${check} src; then
echo
echo
echo "${multiLineRegexChecks[$check]}"
exit 1
fi
done
echo
echo "Everything looks good"

44
conf/mod-noclip.conf.dist Normal file
View File

@ -0,0 +1,44 @@
[worldserver]
########################################
# Mod-NoClip
########################################
#
# Module.Enabled
# Description: Enable/Disable the module
# Default: 1 - Enabled
# 0 - Disabled
#
Module.Enabled = 1
#
# Module.Announce.Enabled
# Description: Enable/Disable for module-announcement at players-login
# Default: 1 - Enabled
# 0 - Disabled
#
Module.Announce.Enabled = 1
#
# Module.AllowedAccountTypeMax
# Description: Specifies which account types can use the command .noclip on/off
# If the value is set to 2 as example, all accounts of type SEC_ADMINISTRATOR also can use this command
# 0 = SEC_PLAYER
# 1 = SEC_MODERATOR
# 2 = SEC_GAMEMASTER
# 3 = SEC_ADMINISTRATOR
# Default: 2
#
Module.AllowedAccountTypeMax = 2
#
# Module.TeleportDistance
# Description: Specifies the teleport-range (to go through objects or under the ground)
# Default: 2.0
#
Module.TeleportDistance = 2.0

0
include.sh Normal file
View File

25
pull_request_template.md Normal file
View File

@ -0,0 +1,25 @@
<!-- First of all, THANK YOU for your contribution. -->
## Changes Proposed:
-
-
## Issues Addressed:
<!-- If your fix has a relating issue, link it below -->
- Closes
## SOURCE:
<!-- If you can, include a source that can strengthen your claim -->
## Tests Performed:
<!-- Does it build without errors? Did you test in-game? What did you test? On which OS did you test? Describe any other tests performed -->
-
-
## How to Test the Changes:
<!-- Describe in a detailed step-by-step order how to test the changes -->
1.
2.
3.

463
src/NoClip.cpp Normal file
View File

@ -0,0 +1,463 @@
#include "NoClip.h"
/* Class: CustomPlayerInformation */
CustomPlayerInformation::CustomPlayerInformation()
{
this->IsNoClipEnabled = false;
}
CustomPlayerInformation::CustomPlayerInformation(Player* APlayer)
{
if (APlayer)
{
this->player = APlayer;
}
this->IsNoClipEnabled = false;
}
void CustomPlayerInformation::SetPlayer(Player* APlayer)
{
if (APlayer)
{
this->player = APlayer;
}
return;
}
Player* CustomPlayerInformation::GetMyPlayer() const
{
return this->player;
}
void CustomPlayerInformation::SetTeleportDistance(float ATeleportDistance)
{
if (ATeleportDistance > 0.0f)
{
{
std::lock_guard<std::mutex> lockGuard(NoClipMutex);
this->TeleportDistance = ATeleportDistance;
}
}
return;
}
float CustomPlayerInformation::GetTeleportDistance() const
{
return this->TeleportDistance;
}
void CustomPlayerInformation::SetFlagNoClipEnabled(bool AIsNoClipEnabled)
{
this->IsNoClipEnabled = AIsNoClipEnabled;
return;
}
bool CustomPlayerInformation::GetFlagNoClipEnabled() const
{
return this->IsNoClipEnabled;
}
bool CustomPlayerInformation::SetThreadHandlerState(int AState)
{
bool xResult = false;
if (this->player)
{
switch (AState)
{
case 1:
{
if (!this->IsNoClipEnabled)
{
{
std::lock_guard<std::mutex> lockGuard(NoClipMutex);
this->IsNoClipEnabled = true;
ThreadHandler = std::thread([this]()
{
while (this->IsNoClipEnabled)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
CustomPlayerInformation::HandleNoClipMovement();
}
});
ThreadHandler.detach();
NoClipThread = std::thread([this]()
{
while (this->IsNoClipEnabled)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
CustomPlayerInformation::HandleNoClipThread();
}
});
NoClipThread.detach();
xResult = true;
}
}
break;
}
case 2:
{
if (this->IsNoClipEnabled)
{
{
std::lock_guard<std::mutex> lockGuard(NoClipMutex);
this->IsNoClipEnabled = false;
xResult = true;
}
}
break;
}
default:
{
xResult = false;
break;
}
}
}
return xResult;
}
void CustomPlayerInformation::HandleNoClipMovement()
{
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
if (!this->player->CanFly())
{
this->player->SetCanFly(true);
}
while (this->IsNoClipEnabled)
{
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (this->player->CanFly())
{
this->player->SetCanFly(false);
}
}
}
void CustomPlayerInformation::HandleNoClipThread()
{
while (this->player && NC_Enable && this->IsNoClipEnabled)
{
if (this->TeleportDistance < 0.0f)
{
this->TeleportDistance = NC_TeleportDistance;
}
float xOrientation = this->player->GetOrientation();
float xPosX = this->player->GetPositionX();
float xPosY = this->player->GetPositionY();
float xPosZ = this->player->GetPositionZ();
uint32 xMapId = this->player->GetMapId();
float xNewPosX = xPosX;
float xNewPosY = xPosY;
float xNewPosZ = xPosZ;
if (IsPlayerMovingOrFlying())
{
xNewPosX += this->TeleportDistance * cos(xOrientation);
xNewPosY += this->TeleportDistance * sin(xOrientation);
Position xCollisionPos = player->GetFirstCollisionPosition(xPosX, xPosY, xPosZ, xNewPosX, xNewPosY);
Position xPlayerPos = Position(xPosX, xPosY, xPosZ, xOrientation);
if (this->player->isMoving() || IsPlayerFlyingDirections() || IsPlayerFlyingDown() || IsPlayerFlyingUp())
{
if ((IsObstacleInFront() || (!PathGenerator::IsWalkableClimb(xPosX, xPosY, xPosZ, xNewPosX, xNewPosY, xPosZ, this->player->GetCollisionHeight()) ||
(CollisionInFront(xPlayerPos, xCollisionPos)))))
{
this->player->TeleportTo(xMapId, xNewPosX, xNewPosY, xPosZ, xOrientation);
}
}
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return;
}
bool CustomPlayerInformation::IsPlayerMovingOrFlying()
{
//if (player && NC_Enable && NoClipActivated)
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
return (IsPlayerFlyingDirections() || IsPlayerFlyingDown() || IsPlayerFlyingUp()) ||
player->isMoving();
}
return false;
}
bool CustomPlayerInformation::IsPlayerFlyingUp()
{
//if (player && NC_Enable && NoClipActivated)
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
return player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_ASCENDING) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_PITCH_UP) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_ASCENDING) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_PITCH_UP);
}
return false;
}
bool CustomPlayerInformation::IsPlayerFlyingDown()
{
//if (player && NC_Enable && NoClipActivated)
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
return player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_DESCENDING) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_PITCH_DOWN) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_DESCENDING) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_PITCH_DOWN);
}
return false;
}
bool CustomPlayerInformation::IsPlayerFlyingDirections()
{
//if (player && NC_Enable && NoClipActivated)
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
return player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_FORWARD) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_BACKWARD) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_LEFT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_RIGHT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_STRAFE_LEFT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_FLYING | MOVEMENTFLAG_STRAFE_RIGHT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FORWARD) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_BACKWARD) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_LEFT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_RIGHT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_STRAFE_LEFT) ||
player->HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_STRAFE_RIGHT);
}
return false;
}
bool CustomPlayerInformation::IsInvisibleWallInFront(Player* player, float distance)
{
/*float playerX, playerY, playerZ;
player->GetPosition(playerX, playerY, playerZ);
float orientation = player->GetOrientation();
float checkX = playerX + distance * cos(orientation);
float checkY = playerY + distance * sin(orientation);
float distanceToPoint = player->GetDistance2d(checkX, checkY);
return (distanceToPoint < distance); // Falls die Distanz kleiner als erwartet ist, könnte etwas im Weg sein*/
return false;
}
bool CustomPlayerInformation::CollisionInFront(Position xPosPlayer, Position xPosCollision)
{
if (this->player)
{
float xPosPlayerX = xPosPlayer.GetPositionX();
float xPosPlayerY = xPosPlayer.GetPositionY();
float xPosPlayerZ = xPosPlayer.GetPositionZ();
float xPosPlayerOrientation = xPosPlayer.GetOrientation();
float xPosCollisionX = xPosCollision.GetPositionX();
float xPosCollisionY = xPosCollision.GetPositionY();
float xPosCollisionZ = xPosCollision.GetPositionZ();
float xPosCollisionOrientation = xPosCollision.GetOrientation();
bool isWaterNext = player->GetMap()->IsInWater(this->player->GetPhaseMask(), xPosPlayerX, xPosPlayerY, xPosPlayerZ, this->player->GetCollisionHeight());
PathGenerator path(this->player);
path.SetUseRaycast(true);
bool result = path.CalculatePath(xPosPlayerX, xPosPlayerY, xPosPlayerZ,
xPosCollisionX, xPosCollisionY, xPosCollisionZ, false);
bool notOnGround = path.GetPathType() & PATHFIND_NOT_USING_PATH
|| isWaterNext || (this->player && this->player->IsFlying());
// Check for valid path types before we proceed
if (!result || (!notOnGround && path.GetPathType() & ~(PATHFIND_NORMAL | PATHFIND_SHORTCUT | PATHFIND_INCOMPLETE | PATHFIND_FARFROMPOLY_END)))
{
return true;
}
G3D::Vector3 endPos = path.GetPath().back();
xPosCollisionX = endPos.x;
xPosCollisionY = endPos.y;
xPosCollisionZ = endPos.z;
// check static LOS
float halfHeight = this->player->GetCollisionHeight() * 0.5f;
bool col = VMAP::VMapFactory::createOrGetVMapMgr()->GetObjectHitPos(this->player->GetMapId(),
xPosPlayerX, xPosPlayerY, xPosPlayerZ + halfHeight,
xPosCollisionX, xPosCollisionY, xPosCollisionZ + halfHeight,
xPosCollisionX, xPosCollisionY, xPosCollisionZ, -CONTACT_DISTANCE);
// Collided with static LOS object, move back to collision point
if (col)
{
return true;
}
xPosCollisionZ -= halfHeight;
// check dynamic collision
col = this->player->GetMap()->GetObjectHitPos(this->player->GetPhaseMask(),
xPosPlayerX, xPosPlayerY, xPosPlayerZ + halfHeight,
xPosCollisionX, xPosCollisionY, xPosCollisionZ + halfHeight,
xPosCollisionX, xPosCollisionY, xPosCollisionZ, -CONTACT_DISTANCE);
xPosCollisionZ -= halfHeight;
// Collided with a gameobject, move back to collision point
if (col)
{
return true;
}
float xCurMapHeight = this->player->GetMapHeight(xPosPlayerX, xPosPlayerY, xPosPlayerZ);
float xNewMapHeight = this->player->GetMapHeight(xPosCollisionX, xPosCollisionY, xPosCollisionZ);
if (xCurMapHeight != xNewMapHeight)
{
if (this->player->isMoving() && !(IsPlayerFlyingDirections() || IsPlayerFlyingDown() || IsPlayerFlyingUp()))
{
return true;
}
}
}
return false;
}
bool CustomPlayerInformation::IsObstacleInFront()
{
//if (player && NC_Enable && NoClipActivated)
if (this->player && NC_Enable && this->IsNoClipEnabled)
{
float xOrientation = player->GetOrientation();
float xPosX = player->GetPositionX();
float xPosY = player->GetPositionY();
float xPosZ = player->GetPositionZ();
float xCheckPosX = xPosX + cos(xOrientation);
float xCheckPosY = xPosY + sin(xOrientation);
return (!player->IsWithinLOS(xCheckPosX, xCheckPosY, xPosZ, VMAP::ModelIgnoreFlags::Nothing, LINEOFSIGHT_ALL_CHECKS));
}
return false;
}
/* Class: NoClipConfigLoader */
void NoClipConfigLoader::OnBeforeConfigLoad(bool /*reload*/)
{
SetInitialWorldSettings();
return;
}
void NoClipConfigLoader::SetInitialWorldSettings()
{
NC_Enable = sConfigMgr->GetOption<bool>("Module.Enable", true);
NC_Announce_Enable = sConfigMgr->GetOption<bool>("Module.Announce.Enable", true && NC_Enable);
NC_AllowedAccountTypeMax = sConfigMgr->GetOption<int>("Module.AllowedAccountTypeMax", NC_AllowedAccountTypeMax);
NC_TeleportDistance = sConfigMgr->GetOption<float>("Module.TeleportDistance", NC_TeleportDistance);
return;
}
/* Class: NoClipCommand */
bool NoClipCommand::HandleSetTeleportDistanceCommand(ChatHandler* AChatHandler, float ATeleportDistance)
{
if (AChatHandler && NC_Enable && ATeleportDistance > 0.0f)
{
WorldSession* xPlayerSession = AChatHandler->GetSession();
if (xPlayerSession)
{
Player* xPlayer = xPlayerSession->GetPlayer();
if (xPlayer)
{
xPlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->SetTeleportDistance(ATeleportDistance);
return true;
}
}
}
return false;
}
bool NoClipCommand::HandleNoClipCommand(ChatHandler* AChatHandler)
{
//if (handler && NC_Enable && !NoClipActivated)
if (AChatHandler && NC_Enable)
{
WorldSession* xPlayerSession = AChatHandler->GetSession();
if (xPlayerSession)
{
Player* xPlayer = xPlayerSession->GetPlayer();
if (xPlayer)
{
if (!xPlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->GetFlagNoClipEnabled())
{
if (xPlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->SetThreadHandlerState(1))
{
AChatHandler->SendSysMessage("NoClip is now enabled!");
return true;
}
return false;
}
}
}
}
return false;
}
bool NoClipCommand::HandleClipCommand(ChatHandler* AChatHandler)
{
if (AChatHandler && NC_Enable)
{
WorldSession* xPlayerSession = AChatHandler->GetSession();
if (xPlayerSession)
{
Player* xPlayer = xPlayerSession->GetPlayer();
if (xPlayer)
{
if (xPlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->GetFlagNoClipEnabled())
{
if (xPlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->SetThreadHandlerState(2))
{
AChatHandler->SendSysMessage("NoClip is now disabled!");
return true;
}
return false;
}
}
}
}
return false;
}
/* Class: NoClipPlayer */
void NoClipPlayer::OnLogin(Player* APlayer)
{
if (APlayer && NC_Enable && NC_Announce_Enable)
{
WorldSession* xPlayerSession = APlayer->GetSession();
if (xPlayerSession)
{
APlayer->CustomData.Set("NoClip", new CustomPlayerInformation(APlayer));
ChatHandler(xPlayerSession).SendSysMessage("This server is running the |cff4CFF00NoClip |rmodule.");
}
}
}
void NoClipPlayer::OnBeforeLogout(Player* APlayer)
{
if (APlayer && NC_Enable && APlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->GetFlagNoClipEnabled())
{
APlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->SetFlagNoClipEnabled(false);
APlayer->SetCanFly(false);
APlayer->CustomData.GetDefault<CustomPlayerInformation>("NoClip")->SetThreadHandlerState(2);
}
}

126
src/NoClip.h Normal file
View File

@ -0,0 +1,126 @@
#include "ScriptMgr.h"
#include "Player.h"
#include "Config.h"
#include "Chat.h"
#include "World.h"
#include "VMapFactory.h"
#include "VMapMgr2.h"
#include "Geometry.h"
#include "Timer.h"
#include "Object.h"
#include "MapMgr.h"
#include "Creature.h"
#include "Transport.h"
#include "World.h"
#include "Util.h"
#include "GridDefines.h"
#include "condition_variable"
#include "iostream"
#include "mutex"
#include "chrono"
#include "thread"
#include "atomic"
#include "functional"
bool NC_Enable = true;
bool NC_Announce_Enable = false;
uint32 NC_AllowedAccountTypeMax = 2;
float NC_TeleportDistance = 1.0f;
class CustomPlayerInformation : public DataMap::Base
{
public:
CustomPlayerInformation();
CustomPlayerInformation(Player* APlayer);
void SetPlayer(Player* APlayer);
Player* GetMyPlayer() const;
void SetTeleportDistance(float ATeleportDistance);
float GetTeleportDistance() const;
void SetFlagNoClipEnabled(bool AIsNoClipEnabled);
bool GetFlagNoClipEnabled() const;
bool SetThreadHandlerState(int AState);
private:
Player* player = nullptr;
float TeleportDistance = 1.0f;
std::mutex NoClipMutex;
std::thread NoClipThread;
std::thread ThreadHandler;
std::atomic<bool> IsNoClipEnabled = false;
void HandleNoClipMovement();
void HandleNoClipThread();
bool IsPlayerMovingOrFlying();
bool IsPlayerFlyingUp();
bool IsPlayerFlyingDown();
bool IsPlayerFlyingDirections();
bool IsInvisibleWallInFront(Player* player, float distance);
bool CollisionInFront(Position xPosPlayer, Position xPosCollision);
bool IsObstacleInFront();
};
class NoClipConfigLoader : public WorldScript
{
public:
NoClipConfigLoader() : WorldScript("NoClipConfig") {}
void OnBeforeConfigLoad(bool /*reload*/) override;
void SetInitialWorldSettings();
};
class NoClipCommand : public CommandScript
{
public:
NoClipCommand() : CommandScript("NoClipCommand") {}
/* Command-Handler */
Acore::ChatCommands::ChatCommandTable GetCommands() const override
{
if (NC_Enable)
{
static Acore::ChatCommands::ChatCommandTable xNoClipCommandTable =
{
{ "on", NoClipCommand::HandleNoClipCommand, NC_AllowedAccountTypeMax, Acore::ChatCommands::Console::No },
{ "off", NoClipCommand::HandleClipCommand, NC_AllowedAccountTypeMax, Acore::ChatCommands::Console::No },
{ "set TeleportDistance", NoClipCommand::HandleSetTeleportDistanceCommand, NC_AllowedAccountTypeMax, Acore::ChatCommands::Console::No }
};
static Acore::ChatCommands::ChatCommandTable xNoClipIndividualBaseTable =
{
{ "noclip", xNoClipCommandTable }
};
return xNoClipIndividualBaseTable;
}
return Acore::ChatCommands::ChatCommandTable();
}
static bool HandleSetTeleportDistanceCommand(ChatHandler* AChatHandler, float ATeleportDistance);
static bool HandleNoClipCommand(ChatHandler* AChatHandler);
static bool HandleClipCommand(ChatHandler* AChatHandler);
};
class NoClipPlayer : public PlayerScript
{
public:
NoClipPlayer() : PlayerScript("NoClipPlayer") {}
void OnLogin(Player* APlayer) override;
void OnBeforeLogout(Player* APlayer) override;
};
void AddNoClipScripts()
{
new NoClipConfigLoader();
new NoClipCommand();
new NoClipPlayer();
}

6
src/NoClip_loader.cpp Normal file
View File

@ -0,0 +1,6 @@
void AddNoClipScripts();
void Addmod_noclipScripts()
{
AddNoClipScripts();
}