#pragma once
#include "../Defines.h"
#include "../FunctionRef.h"
// fwd:
class cBlockEntityWithItems;
class cBrewingstandEntity;
class cChunkDesc;
class cClientHandle;
class cCommandOutputCallback;
class cCraftingGrid;
class cCraftingRecipe;
class cEntity;
class cHopperEntity;
class cItems;
class cMonster;
class cPickup;
class cPlayer;
class cPlugin;
class cProjectileEntity;
class cWindow;
class cWorld;
class cSettingsRepositoryInterface;
class cDeadlockDetect;
struct TakeDamageInfo;
typedef std::shared_ptr<cPlugin> cPluginPtr;
typedef std::vector<cPluginPtr> cPluginPtrs;
// tolua_begin
class cPluginManager
{
public:
enum CommandResult
{
crExecuted,
crUnknownCommand,
crError,
crBlocked,
crNoPermission,
} ;
/** Defines the status of a single plugin - whether it is loaded, disabled or errored. */
enum ePluginStatus
{
/** The plugin has been loaded successfully. */
psLoaded,
/** The plugin is disabled in settings.ini. */
psDisabled,
/** The plugin is enabled in settings.ini but has been unloaded (by a command). */
psUnloaded,
/** The plugin is enabled in settings.ini but has failed to load.
m_LoadError is the description of the error. */
psError,
/** The plugin has been loaded before, but after a folder refresh it is no longer present.
The plugin will be unloaded in the next call to ReloadPlugins(). */
psNotFound,
};
enum PluginHook
{
HOOK_BLOCK_SPREAD,
HOOK_BLOCK_TO_PICKUPS,
HOOK_BREWING_COMPLETING,
HOOK_BREWING_COMPLETED,
HOOK_CHAT,
HOOK_CHUNK_AVAILABLE,
HOOK_CHUNK_GENERATED,
HOOK_CHUNK_GENERATING,
HOOK_CHUNK_UNLOADED,
HOOK_CHUNK_UNLOADING,
HOOK_COLLECTING_PICKUP,
HOOK_CRAFTING_NO_RECIPE,
HOOK_DISCONNECT,
HOOK_PLAYER_ANIMATION,
HOOK_ENTITY_ADD_EFFECT,
HOOK_ENTITY_CHANGING_WORLD,
HOOK_ENTITY_CHANGED_WORLD,
HOOK_EXECUTE_COMMAND,
HOOK_EXPLODED,
HOOK_EXPLODING,
HOOK_HANDSHAKE,
HOOK_HOPPER_PULLING_ITEM,
HOOK_HOPPER_PUSHING_ITEM,
HOOK_KILLED,
HOOK_KILLING,
HOOK_LOGIN,
HOOK_LOGIN_FORGE,
HOOK_PLAYER_BREAKING_BLOCK,
HOOK_PLAYER_BROKEN_BLOCK,
HOOK_PLAYER_DESTROYED,
HOOK_PLAYER_EATING,
HOOK_PLAYER_FISHED,
HOOK_PLAYER_FISHING,
HOOK_PLAYER_FOOD_LEVEL_CHANGE,
HOOK_PLAYER_JOINED,
HOOK_PLAYER_LEFT_CLICK,
HOOK_PLAYER_MOVING,
HOOK_PLAYER_OPENING_WINDOW,
HOOK_PLAYER_PLACED_BLOCK,
HOOK_PLAYER_PLACING_BLOCK,
HOOK_PLAYER_CROUCHED,
HOOK_PLAYER_RIGHT_CLICK,
HOOK_PLAYER_RIGHT_CLICKING_ENTITY,
HOOK_PLAYER_SHOOTING,
HOOK_PLAYER_SPAWNED,
HOOK_ENTITY_TELEPORT,
HOOK_PLAYER_TOSSING_ITEM,
HOOK_PLAYER_USED_BLOCK,
HOOK_PLAYER_USED_ITEM,
HOOK_PLAYER_USING_BLOCK,
HOOK_PLAYER_USING_ITEM,
HOOK_PLUGIN_MESSAGE,
HOOK_PLUGINS_LOADED,
HOOK_POST_CRAFTING,
HOOK_PRE_CRAFTING,
HOOK_PROJECTILE_HIT_BLOCK,
HOOK_PROJECTILE_HIT_ENTITY,
HOOK_SERVER_PING,
HOOK_SPAWNED_ENTITY,
HOOK_SPAWNED_MONSTER,
HOOK_SPAWNING_ENTITY,
HOOK_SPAWNING_MONSTER,
HOOK_TAKE_DAMAGE,
HOOK_TICK,
HOOK_UPDATED_SIGN,
HOOK_UPDATING_SIGN,
HOOK_WEATHER_CHANGED,
HOOK_WEATHER_CHANGING,
HOOK_WORLD_STARTED,
HOOK_WORLD_TICK,
// tolua_end
// Note that if a hook type is added, it may need processing in cPlugin::CanAddHook() descendants,
// and it definitely needs adding in cPluginLua::GetHookFnName() !
// Keep these two as the last items, they are used for validity checking and get their values automagically
HOOK_NUM_HOOKS,
HOOK_MAX = HOOK_NUM_HOOKS - 1,
} ; // tolua_export
/** Used as a callback for enumerating bound commands */
class cCommandEnumCallback
{
public:
virtual ~cCommandEnumCallback() {}
/** Called for each command; return true to abort enumeration
For console commands, a_Permission is not used (set to empty string)
*/
virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0;
} ;
/** Interface that must be provided by any class that implements a command handler, either in-game or console command. */
class cCommandHandler
{
public:
// Force a virtual destructor in descendants
virtual ~cCommandHandler() {}
/** Executes the specified in-game command.
a_Split is the command string, split at the spaces.
a_Player is the player executing the command, nullptr in case of the console.
a_Command is the entire command string.
a_Output is the sink into which the additional text returned by the command handler should be sent; only used for console commands. */
virtual bool ExecuteCommand(
const AStringVector & a_Split,
cPlayer * a_Player,
const AString & a_Command,
cCommandOutputCallback * a_Output = nullptr
) = 0;
};
typedef std::shared_ptr<cCommandHandler> cCommandHandlerPtr;
/** The interface used for enumerating and extern-calling plugins */
using cPluginCallback = cFunctionRef<bool(cPlugin &)>;
typedef std::list<cPlugin *> PluginList;
/** Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition, removal) */
void Tick(float a_Dt);
/** Returns the instance of the Plugin Manager (there is only ever one) */
static cPluginManager * Get(void); // tolua_export
/** Refreshes the m_Plugins list based on the current contents of the Plugins folder.
If an active plugin's folder is not found anymore, the plugin is set as psNotFound, but not yet unloaded. */
void RefreshPluginList(); // tolua_export
/** Schedules a reload of the plugins to happen within the next call to Tick(). */
void ReloadPlugins(); // tolua_export
/** Adds the plugin to the list of plugins called for the specified hook type.
If a plugin adds multiple handlers for a single hook, it is added only once (ignore-duplicates). */
void AddHook(cPlugin * a_Plugin, int a_HookType);
/** Returns the number of all plugins in m_Plugins (includes disabled, unloaded and errored plugins). */
size_t GetNumPlugins() const; // tolua_export
/** Returns the number of plugins that are psLoaded. */
size_t GetNumLoadedPlugins(void) const; // tolua_export
// Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort
bool CallHookBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source);
bool CallHookBlockToPickups (cWorld & a_World, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, const cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool, cItems & a_Pickups);
bool CallHookBrewingCompleting (cWorld & a_World, cBrewingstandEntity & a_Brewingstand);
bool CallHookBrewingCompleted (cWorld & a_World, cBrewingstandEntity & a_Brewingstand);
bool CallHookChat (cPlayer & a_Player, AString & a_Message);
bool CallHookChunkAvailable (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookChunkGenerated (cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
bool CallHookChunkGenerating (cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc);
bool CallHookChunkUnloaded (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookChunkUnloading (cWorld & a_World, int a_ChunkX, int a_ChunkZ);
bool CallHookCollectingPickup (cPlayer & a_Player, cPickup & a_Pickup);
bool CallHookCraftingNoRecipe (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookDisconnect (cClientHandle & a_Client, const AString & a_Reason);
bool CallHookEntityAddEffect (cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier);
bool CallHookEntityTeleport (cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition);
bool CallHookEntityChangingWorld (cEntity & a_Entity, cWorld & a_World);
bool CallHookEntityChangedWorld (cEntity & a_Entity, cWorld & a_World);
bool CallHookExecuteCommand (cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result); // If a_Player == nullptr, it is a console cmd
bool CallHookExploded (cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookExploding (cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData);
bool CallHookHandshake (cClientHandle & a_ClientHandle, const AString & a_Username);
bool CallHookHopperPullingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum);
bool CallHookHopperPushingItem (cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum);
bool CallHookKilled (cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage);
bool CallHookKilling (cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI);
bool CallHookLogin (cClientHandle & a_Client, UInt32 a_ProtocolVersion, const AString & a_Username);
bool CallHookLoginForge (cClientHandle & a_Client, AStringMap & a_Mods);
bool CallHookPlayerAnimation (cPlayer & a_Player, int a_Animation);
bool CallHookPlayerBreakingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerBrokenBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerDestroyed (cPlayer & a_Player);
bool CallHookPlayerEating (cPlayer & a_Player);
bool CallHookPlayerFished (cPlayer & a_Player, const cItems & a_Reward);
bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward);
bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel);
bool CallHookPlayerJoined (cPlayer & a_Player);
bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition, bool a_PreviousIsOnGround);
bool CallHookPlayerOpeningWindow (cPlayer & a_Player, cWindow & a_Window);
bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
bool CallHookPlayerCrouched (cPlayer & a_Player);
bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
bool CallHookPlayerShooting (cPlayer & a_Player);
bool CallHookPlayerSpawned (cPlayer & a_Player);
bool CallHookPlayerTossingItem (cPlayer & a_Player);
bool CallHookPlayerUsedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerUsedItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPlayerUsingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
bool CallHookPlayerUsingItem (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
bool CallHookPluginMessage (cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message);
bool CallHookPluginsLoaded (void);
bool CallHookPostCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookPreCrafting (cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe);
bool CallHookProjectileHitBlock (cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos);
bool CallHookProjectileHitEntity (cProjectileEntity & a_Projectile, cEntity & a_HitEntity);
bool CallHookServerPing (cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon);
bool CallHookSpawnedEntity (cWorld & a_World, cEntity & a_Entity);
bool CallHookSpawnedMonster (cWorld & a_World, cMonster & a_Monster);
bool CallHookSpawningEntity (cWorld & a_World, cEntity & a_Entity);
bool CallHookSpawningMonster (cWorld & a_World, cMonster & a_Monster);
bool CallHookTakeDamage (cEntity & a_Receiver, TakeDamageInfo & a_TDI);
bool CallHookUpdatedSign (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4, cPlayer * a_Player);
bool CallHookUpdatingSign (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, AString & a_Line1, AString & a_Line2, AString & a_Line3, AString & a_Line4, cPlayer * a_Player);
bool CallHookWeatherChanged (cWorld & a_World);
bool CallHookWeatherChanging (cWorld & a_World, eWeather & a_NewWeather);
bool CallHookWorldStarted (cWorld & a_World);
bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec);
/** Queues the specified plugin to be unloaded in the next call to Tick().
Note that this function returns before the plugin is unloaded, to avoid deadlocks. */
void UnloadPlugin(const AString & a_PluginFolder); // tolua_export
/** Loads the plugin from the specified plugin folder.
Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */
bool LoadPlugin(const AString & a_PluginFolder); // tolua_export
/** Removes all hooks the specified plugin has registered */
void RemoveHooks(cPlugin * a_Plugin);
/** Removes the plugin of the specified name from the internal structures and deletes its object. */
void RemovePlugin(const AString & a_PluginName);
/** Removes all command bindings that the specified plugin has made */
void RemovePluginCommands(cPlugin * a_Plugin);
/** Returns true if the specified plugin is loaded. */
bool IsPluginLoaded(const AString & a_PluginName); // tolua_export
/** Binds a command to the specified handler.
Returns true if successful, false if command already bound.
Exported in ManualBindings.cpp. */
bool BindCommand(
const AString & a_Command,
cPlugin * a_Plugin,
cCommandHandlerPtr a_Handler,
const AString & a_Permission,
const AString & a_HelpString
);
/** Calls a_Callback for each bound command, returns true if all commands were enumerated */
bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
/** Returns true if the command is in the command map */
bool IsCommandBound(const AString & a_Command); // tolua_export
/** Returns the permission needed for the specified command; empty string if command not found */
AString GetCommandPermission(const AString & a_Command); // tolua_export
/** Executes the command, as if it was requested by a_Player. Checks permissions first. Returns crExecuted if executed. */
CommandResult ExecuteCommand(cPlayer & a_Player, const AString & a_Command); // tolua_export
/** Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns crExecuted if executed. */
CommandResult ForceExecuteCommand(cPlayer & a_Player, const AString & a_Command); // tolua_export
/** Removes all console command bindings that the specified plugin has made */
void RemovePluginConsoleCommands(cPlugin * a_Plugin);
/** Binds a console command to the specified handler.
Returns true if successful, false if command already bound.
Exported in ManualBindings.cpp. */
bool BindConsoleCommand(
const AString & a_Command,
cPlugin * a_Plugin,
cCommandHandlerPtr a_Handler,
const AString & a_HelpString
);
/** Calls a_Callback for each bound console command, returns true if all commands were enumerated */
bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp
/** Returns true if the console command is in the command map */
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/** Executes the command split into a_Split, as if it was given on the console.
Returns true if executed. Output is sent to the a_Output callback
Exported in ManualBindings.cpp with a different signature. */
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_Command);
/** Appends all commands beginning with a_Text (case-insensitive) into a_Results.
If a_Player is not nullptr, only commands for which the player has permissions are added.
*/
void TabCompleteCommand(const AString & a_Text, AStringVector & a_Results, cPlayer * a_Player);
/** Returns true if the specified hook type is within the allowed range */
static bool IsValidHookType(int a_HookType);
/** Calls the specified callback with the plugin object of the specified plugin.
Returns false if plugin not found, otherwise returns the value that the callback has returned. */
bool DoWithPlugin(const AString & a_PluginName, cPluginCallback a_Callback);
/** Calls the specified callback for each plugin in m_Plugins.
Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */
bool ForEachPlugin(cPluginCallback a_Callback);
/** Returns the name of the folder (cPlugin::GetFolderName()) from which the specified plugin was loaded. */
AString GetPluginFolderName(const AString & a_PluginName); // tolua_export
/** Returns the path where individual plugins' folders are expected.
The path doesn't end in a slash. */
static AString GetPluginsPath(void) { return FILE_IO_PREFIX "Plugins"; } // tolua_export
private:
friend class cRoot;
class cCommandReg
{
public:
cPlugin * m_Plugin;
AString m_Permission; // Not used for console commands
AString m_HelpString;
cCommandHandlerPtr m_Handler;
} ;
typedef std::map<int, cPluginManager::PluginList> HookMap;
typedef std::map<AString, cCommandReg> CommandMap;
/** FolderNames of plugins that should be unloaded.
The plugins will be unloaded within the next call to Tick(), to avoid multithreading issues.
Protected against multithreaded access by m_CSPluginsToUnload. */
AStringVector m_PluginsToUnload;
/** Protects m_PluginsToUnload against multithreaded access. */
mutable cCriticalSection m_CSPluginsToUnload;
/** All plugins that have been found in the Plugins folder. */
cPluginPtrs m_Plugins;
HookMap m_Hooks;
CommandMap m_Commands;
CommandMap m_ConsoleCommands;
/** If set to true, all the plugins will be reloaded within the next call to Tick(). */
bool m_bReloadPlugins;
/** The deadlock detect in which all plugins should track their CSs. */
cDeadlockDetect & m_DeadlockDetect;
cPluginManager(cDeadlockDetect & a_DeadlockDetect);
virtual ~cPluginManager();
/** Reloads all plugins, defaulting to settings.ini for settings location */
void ReloadPluginsNow(void);
/** Reloads all plugins with a settings repo expected to be initialised to settings.ini */
void ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings);
/** Unloads all plugins */
void UnloadPluginsNow(void);
/** Handles writing default plugins if 'Plugins' key not found using a settings repo expected to be intialised to settings.ini */
void InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings);
/** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */
CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions);
/** Returns the folders that are specified in the settings ini to load plugins from. */
AStringVector GetFoldersToLoad(cSettingsRepositoryInterface & a_Settings);
/** Calls a_HookFunction on each plugin registered to the hook HookName.
Returns false if the action is to continue or true if the plugin wants to abort.
Accessible only from within PluginManager.cpp */
template <typename HookFunction>
bool GenericCallHook(PluginHook a_HookName, HookFunction a_HookFunction);
} ; // tolua_export