diff options
Diffstat (limited to 'MCServer/Plugins')
-rw-r--r-- | MCServer/Plugins/APIDump/APIDesc.lua | 44 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua | 24 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua | 33 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua | 27 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnPlayerUsedBlock.lua | 2 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua | 5 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua | 11 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/InfoFile.html | 246 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main.css | 3 | ||||
-rw-r--r-- | MCServer/Plugins/APIDump/main_APIDump.lua | 6 | ||||
-rw-r--r-- | MCServer/Plugins/ChunkWorx/chunkworx_web.lua | 2 | ||||
-rw-r--r-- | MCServer/Plugins/Debuggers/Debuggers.lua | 90 | ||||
-rw-r--r-- | MCServer/Plugins/DiamondMover/DiamondMover.lua | 8 | ||||
-rw-r--r-- | MCServer/Plugins/Handy/handy_functions.lua | 4 |
14 files changed, 471 insertions, 34 deletions
diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 1423d64bc..e65da1d16 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -114,6 +114,7 @@ g_APIDesc = GetBlockSkyLight = { Params = "BlockX, BlockY, BlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified absolute coords" }, GetBlockType = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified absolute coords" }, GetBlockTypeMeta = { Params = "BlockX, BlockY, BlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified absolute coords" }, + GetCoordRange = {Params = "", Return = "MaxX, MaxY, MaxZ", Notes = "Returns the maximum relative coords in all 3 axes. See also GetSize()." }, GetDataTypes = { Params = "", Return = "number", Notes = "Returns the mask of datatypes that the object is currently holding" }, GetOrigin = { Params = "", Return = "OriginX, OriginY, OriginZ", Notes = "Returns the origin coords of where the area was read from." }, GetOriginX = { Params = "", Return = "number", Notes = "Returns the origin x-coord" }, @@ -124,7 +125,7 @@ g_APIDesc = GetRelBlockSkyLight = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "NIBBLETYPE", Notes = "Returns the skylight at the specified relative coords" }, GetRelBlockType = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE", Notes = "Returns the block type at the specified relative coords" }, GetRelBlockTypeMeta = { Params = "RelBlockX, RelBlockY, RelBlockZ", Return = "BLOCKTYPE, NIBBLETYPE", Notes = "Returns the block type and meta at the specified relative coords" }, - GetSize = { Params = "", Return = "SizeX, SizeY, SizeZ", Notes = "Returns the size of the area in all 3 axes." }, + GetSize = { Params = "", Return = "SizeX, SizeY, SizeZ", Notes = "Returns the size of the area in all 3 axes. See also GetCoordRange()." }, GetSizeX = { Params = "", Return = "number", Notes = "Returns the size of the held data in the x-axis" }, GetSizeY = { Params = "", Return = "number", Notes = "Returns the size of the held data in the y-axis" }, GetSizeZ = { Params = "", Return = "number", Notes = "Returns the size of the held data in the z-axis" }, @@ -522,13 +523,16 @@ end Functions = { + GenerateOfflineUUID = { Params = "Username", Return = "string", Notes = "(STATIC) Generates an UUID based on the player name provided. This is used for the offline (non-auth) mode, when there's no UUID source. Each username generates a unique and constant UUID, so that when the player reconnects with the same name, their UUID is the same. Returns a 36-char UUID (with dashes)." }, GetLocale = { Params = "", Return = "Locale", Notes = "Returns the locale string that the client sends as part of the protocol handshake. Can be used to provide localized strings." }, GetPing = { Params = "", Return = "number", Notes = "Returns the ping time, in ms" }, GetPlayer = { Params = "", Return = "{{cPlayer|cPlayer}}", Notes = "Returns the player object connected to this client. Note that this may be nil, for example if the player object is not yet spawned." }, GetUniqueID = { Params = "", Return = "number", Notes = "Returns the UniqueID of the client used to identify the client in the server" }, + GetUUID = { Params = "", Return = "string", Notes = "Returns the authentication-based UUID of the client. This UUID should be used to identify the player when persisting any player-related data." }, GetUsername = { Params = "", Return = "string", Notes = "Returns the username that the client has provided" }, GetViewDistance = { Params = "", Return = "number", Notes = "Returns the viewdistance (number of chunks loaded for the player in each direction)" }, HasPluginChannel = { Params = "ChannelName", Return = "bool", Notes = "Returns true if the client has registered to receive messages on the specified plugin channel." }, + IsUUIDOnline = { Params = "UUID", Return = "bool", Notes = "(STATIC) Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID. We use Version-3 UUIDs for offline UUIDs, online UUIDs are Version-4, thus we can tell them apart. Accepts both 32-char and 36-char UUIDs (with and without dashes). If the string given is not a valid UUID, returns false."}, Kick = { Params = "Reason", Return = "", Notes = "Kicks the user with the specified reason" }, SendPluginMessage = { Params = "Channel, Message", Return = "", Notes = "Sends the plugin message on the specified channel." }, SetLocale = { Params = "Locale", Return = "", Notes = "Sets the locale that MCServer keeps on record. Initially the locale is initialized in protocol handshake, this function allows plugins to override the stored value (but only server-side and only until the user disconnects)." }, @@ -1151,6 +1155,7 @@ These ItemGrids are available in the API and can be manipulated by the plugins, HasItems = { Params = "{{cItem|cItem}}", Return = "bool", Notes = "Returns true if there are at least as many items of the specified type as in the parameter" }, HowManyCanFit = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that can fit in the storage, including empty slots" }, HowManyItems = { Params = "{{cItem|cItem}}", Return = "number", Notes = "Returns the number of the specified items that are currently stored" }, + RemoveItem = { Params = "{{cItem}}", Return = "number", Notes = "Removes the specified item from the inventory, as many as possible, up to the item's m_ItemCount. Returns the number of items that were removed." }, RemoveOneEquippedItem = { Params = "", Return = "", Notes = "Removes one item from the hotbar's currently selected slot" }, SetArmorSlot = { Params = "ArmorSlotNum, {{cItem|cItem}}", Return = "", Notes = "Sets the specified armor slot contents" }, SetEquippedSlotNum = { Params = "EquippedSlotNum", Return = "", Notes = "Sets the currently selected hotbar slot number" }, @@ -1380,6 +1385,7 @@ local Item5 = cItem(E_ITEM_DIAMOND_CHESTPLATE, 1, 0, "thorns=1;unbreaking=3"); { Params = "SlotNum", Return = "bool", Notes = "Returns true if the specified slot is empty, or an invalid slot is specified" }, { Params = "X, Y", Return = "bool", Notes = "Returns true if the specified slot is empty, or an invalid slot is specified" }, }, + RemoveItem = { Params = "{{cItem}}", Return = "number", Notes = "Removes the specified item from the grid, as many as possible, up to the item's m_ItemCount. Returns the number of items that were removed." }, RemoveOneItem = { { Params = "SlotNum", Return = "{{cItem|cItem}}", Notes = "Removes one item from the stack in the specified slot and returns it as a single cItem. Empty slots are skipped and an empty item is returned" }, @@ -1687,6 +1693,9 @@ a_Player:OpenWindow(Window); TakeDamage = { Return = "" }, KilledBy = { Return = "" }, GetHealth = { Return = "number" }, + AddEntityEffect = { Params = "EffectType, {{cEntityEffect}}", Return = "", Notes = "Applies an entity effect" }, + RemoveEntityEffect = { Params = "EffectType", Return = "", Notes = "Removes a currently applied entity effect" }, + ClearEntityEffects = { Return = "", Notes = "Removes all currently applied entity effects" }, }, Inherits = "cEntity", }, -- cPawn @@ -1871,9 +1880,9 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); }, CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." }, DisablePlugin = { Params = "PluginName", Return = "bool", Notes = "Disables a plugin specified by its name. Returns true if the plugin was disabled, false if it wasn't found or wasn't active." }, - ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Executes the command as if given by the specified Player. Checks permissions. Returns true if executed." }, + ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." }, FindPlugins = { Params = "", Return = "", Notes = "Refreshes the list of plugins to include all folders inside the Plugins folder (potentially new disabled plugins)" }, - ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "bool", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, + ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function(Command, Permission, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature: <pre class=\"prettyprint lang-lua\">function (Command, HelpString)</pre>. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" }, @@ -1889,8 +1898,23 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); LogStackTrace = { Params = "", Return = "", Notes = "(STATIC) Logs a current stack trace of the Lua engine to the server console log. Same format as is used when the plugin fails." }, ReloadPlugins = { Params = "", Return = "", Notes = "Reloads all active plugins" }, }, + ConstantGroups= + { + CommandResult = + { + Include = "^cr.*", + TextBefore = [[ + Results that the (Force)ExecuteCommand return. This gives information if the command is executed or not and the reason. + ]], + }, + }, Constants = { + crBlocked = { Notes = "When a plugin stopped the command using the OnExecuteCommand hook" }, + crError = { Notes = "When the command handler for the given command results in an error" }, + crExecuted = { Notes = "When the command is successfully executed." }, + crNoPermission = { Notes = "When the player doesn't have permission to execute the given command." }, + crUnknownCommand = { Notes = "When the given command doesn't exist." }, HOOK_BLOCK_SPREAD = { Notes = "Called when a block spreads based on world conditions" }, HOOK_BLOCK_TO_PICKUPS = { Notes = "Called when a block has been dug and is being converted to pickups. The server has provided the default pickups and the plugins may modify them." }, HOOK_CHAT = { Notes = "Called when a client sends a chat message that is not a command. The plugin may modify the chat message" }, @@ -1969,7 +1993,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); BroadcastChatSuccess = { Params = "Message", Return = "", Notes = "Prepends Green [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For success messages." }, BroadcastChatWarning = { Params = "Message", Return = "", Notes = "Prepends Rose [WARN] / colours entire text (depending on ShouldUseChatPrefixes()) and broadcasts message. For concerning events, such as plugin reload etc." }, CreateAndInitializeWorld = { Params = "WorldName", Return = "{{cWorld|cWorld}}", Notes = "Creates a new world and initializes it. If there is a world whith the same name it returns nil." }, - FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for the given player." }, + FindAndDoWithPlayer = { Params = "PlayerName, CallbackFunction", Return = "", Notes = "Calls the given callback function for all players with names partially (or fully) matching the name string provided." }, ForEachPlayer = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each player. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cPlayer|cPlayer}})</pre>" }, ForEachWorld = { Params = "CallbackFunction", Return = "", Notes = "Calls the given callback function for each world. The callback function has the following signature: <pre class=\"prettyprint lang-lua\">function Callback({{cWorld|cWorld}})</pre>" }, GetCraftingRecipes = { Params = "", Return = "{{cCraftingRecipe|cCraftingRecipe}}", Notes = "Returns the CraftingRecipes object" }, @@ -2294,10 +2318,14 @@ end IsGameModeCreative = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmCreative." }, IsGameModeSurvival = { Params = "", Return = "bool", Notes = "Returns true if the current gamemode is gmSurvival." }, IsPVPEnabled = { Params = "", Return = "bool", Notes = "Returns whether PVP is enabled in the world settings." }, - IsWeatherRain = { Params = "", Return = "bool", Notes = "Returns true if the current weather is rain." }, - IsWeatherStorm = { Params = "", Return = "bool", Notes = "Returns true if the current weather is a storm." }, + IsWeatherRain = { Params = "", Return = "bool", Notes = "Returns true if the current world is raining." }, + IsWeatherRainAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location is raining (takes into account biomes)." }, + IsWeatherStorm = { Params = "", Return = "bool", Notes = "Returns true if the current world is stormy." }, + IsWeatherStormAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location is stormy (takes into account biomes)." }, IsWeatherSunny = { Params = "", Return = "bool", Notes = "Returns true if the current weather is sunny." }, - IsWeatherWet = { Params = "", Return = "bool", Notes = "Returns true if the current weather has any precipitation (rain or storm)." }, + IsWeatherSunnyAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the current weather is sunny at the specified location (takes into account biomes)." }, + IsWeatherWet = { Params = "", Return = "bool", Notes = "Returns true if the current world has any precipitation (rain or storm)." }, + IsWeatherWetAt = { Params = "BlockX, BlockZ", Return = "bool", Notes = "Returns true if the specified location has any precipitation (rain or storm) (takes into account biomes)." }, QueueBlockForTick = { Params = "BlockX, BlockY, BlockZ, TicksToWait", Return = "", Notes = "Queues the specified block to be ticked after the specified number of gameticks." }, QueueSaveAllChunks = { Params = "", Return = "", Notes = "Queues all chunks to be saved in the world storage thread" }, QueueSetBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta, TickDelay", Return = "", Notes = "Queues the block to be set to the specified blocktype and meta after the specified amount of game ticks. Uses SetBlock() for the actual setting, so simulators are woken up and block entities are handled correctly." }, @@ -2317,6 +2345,7 @@ end { Params = "BlockX, BlockY, BlockZ, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, { Params = "{{Vector3i|BlockCoords}}, BlockMeta", Return = "", Notes = "Sets the meta for the block at the specified coords." }, }, + SetChunkAlwaysTicked = { Params = "ChunkX, ChunkZ, IsAlwaysTicked", Return = "", Notes = "Sets the chunk to always be ticked even when it doesn't contain any clients. IsAlwaysTicked set to true turns forced ticking on, set to false turns it off. Every call with 'true' should be paired with a later call with 'false', otherwise the ticking won't stop. Multiple actions can request ticking independently, the ticking will continue until the last call with 'false'. Note that when the chunk unloads, it loses the value of this flag." }, SetNextBlockTick = { Params = "BlockX, BlockY, BlockZ", Return = "", Notes = "Sets the blockticking to start at the specified block in the next tick." }, SetCommandBlockCommand = { Params = "BlockX, BlockY, BlockZ, Command", Return = "bool", Notes = "Sets the command to be executed in a command block at the specified coordinates. Returns if command was changed." }, SetCommandBlocksEnabled = { Params = "IsEnabled (bool)", Return = "", Notes = "Sets whether command blocks should be enabled on the (entire) server." }, @@ -2929,6 +2958,7 @@ end { -- No sorting is provided for these, they will be output in the same order as defined here { FileName = "Writing-a-MCServer-plugin.html", Title = "Writing a MCServer plugin" }, + { FileName = "InfoFile.html", Title = "Using the Info.lua file" }, { FileName = "SettingUpDecoda.html", Title = "Setting up the Decoda Lua IDE" }, { FileName = "SettingUpZeroBrane.html", Title = "Setting up the ZeroBrane Studio Lua IDE" }, { FileName = "UsingChunkStays.html", Title = "Using ChunkStays" }, diff --git a/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua b/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua index a3301a8c6..204cb63d2 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnDisconnect.lua @@ -2,23 +2,33 @@ return { HOOK_DISCONNECT = { - CalledWhen = "A player has explicitly disconnected.", + CalledWhen = [[ + A client has disconnected, either by explicitly sending the disconnect packet (in older protocols) or + their connection was terminated + ]], DefaultFnName = "OnDisconnect", -- also used as pagename Desc = [[ - This hook is called when a client is about to be disconnected from the server, for whatever reason. - - <p><b>Note that this hook will be removed after <1.7 protocol support is removed, as it was originally a hook for - the client sending the server a disconnect packet, which no longer happens.</b></p> + This hook is called when a client has disconnected from the server, for whatever reason. It is also + called when the client sends the Disconnect packet (only in pre-1.7 protocols). This hook is not called + for server ping connections.</p> + <p> + Note that the hook is called even for connections to players who failed to auth. In such a case there's + no {{cPlayer}} object associated with the client.</p> + <p> + See also the {{OnHandshake|HOOK_HANDSHAKE}} hook which is called when the client connects (and presents + a handshake message, so that they are not just status-pinging). If you need to store a per-player + object, use the {{OnPlayerJoined|HOOK_PLAYER_JOINED}} and {{OnPlayerDestroyed|HOOK_PLAYER_DESTROYED}} + hooks instead, those are guaranteed to have the {{cPlayer}} object associated. ]], Params = { - { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who has disconnected" }, + { Name = "Client", Type = "{{cClientHandle}}", Notes = "The client who has disconnected" }, { Name = "Reason", Type = "string", Notes = "The reason that the client has sent in the disconnect packet" }, }, Returns = [[ If the function returns false or no value, MCServer calls other plugins' callbacks for this event. If the function returns true, no other plugins are called for this event. In either case, - the player is disconnected. + the client is disconnected. ]], }, -- HOOK_DISCONNECT } diff --git a/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua b/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua new file mode 100644 index 000000000..1d1658a6f --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnEntityAddEffect.lua @@ -0,0 +1,33 @@ +return +{ + HOOK_ENTITY_ADD_EFFECT = + { + CalledWhen = "An entity effect is about to get added to an entity.", + DefaultFnName = "OnEntityAddEffect", -- also used as pagename + Desc = [[ + This hook is called whenever an entity effect is about to be added to an entity. The plugin may + disallow the addition by returning true.</p> + <p>Note that this hook only fires for adding the effect, but not for the actual effect application. See + also the {{OnEntityRemoveEffect|HOOK_ENTITY_REMOVE_EFFECT}} for notification about effects expiring / + removing, and {{OnEntityApplyEffect|HOOK_ENTITY_APPLY_EFFECT}} for the actual effect application to the + entity. + ]], + Params = + { + { Name = "Entity", Type = "{{cEntity}}", Notes = "The entity to which the effect is about to be added" }, + { Name = "EffectType", Type = "number", Notes = "The type of the effect to be added. One of the effXXX constants." }, + { Name = "EffectDuration", Type = "number", Notes = "The duration of the effect to be added, in ticks." }, + { Name = "EffectIntensity", Type = "number", Notes = "The intensity (level) of the effect to be added. " }, + { Name = "DistanceModifier", Type = "number", Notes = "The modifier for the effect intensity, based on distance. Used mainly for splash potions." }, + }, + Returns = [[ + If the plugin returns true, the effect will not be added and none of the remaining hook handlers will + be called. If the plugin returns false, MCServer calls all the remaining hook handlers and finally + the effect is added to the entity. + ]], + }, -- HOOK_EXECUTE_COMMAND +} + + + + diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua new file mode 100644 index 000000000..53637d5f1 --- /dev/null +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerFoodLevelChange.lua @@ -0,0 +1,27 @@ +return +{ + HOOK_PLAYER_FOOD_LEVEL_CHANGE = + { + CalledWhen = "Called before the player food level changed. Plugin may override", + DefaultFnName = "OnPlayerFoodLevelChange", -- also used as pagename + Desc = [[ + This hook is called before the food level changes. + The food level is not changed yet, plugins may choose + to refuse the change. + ]], + Params = + { + { Name = "Player", Type = "{{cPlayer}}", Notes = "The player who changes the food level." }, + { Name = "NewFoodLevel", Type = "number", Notes = "The new food level." }, + }, + Returns = [[ + If the function returns false or no value, the next plugin's callback is called. Afterwards, the + server changes the food level of the player. If the function returns true, no + other callback is called for this event and the player's food level doesn't change. + ]], + }, -- HOOK_PLAYER_FOOD_LEVEL_CHANGE +}; + + + + diff --git a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsedBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsedBlock.lua index 4c91ea89e..9a0e036b9 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnPlayerUsedBlock.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnPlayerUsedBlock.lua @@ -2,7 +2,7 @@ return { HOOK_PLAYER_USED_BLOCK = { - CalledWhen = "A player has just used a block (chest, furnace…). Notification only.", + CalledWhen = "A player has just used a block (chest, furnace...). Notification only.", DefaultFnName = "OnPlayerUsedBlock", -- also used as pagename Desc = [[ This hook is called after a {{cPlayer|player}} has right-clicked a block that can be used, such as a diff --git a/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua index 1588d420c..72cf85821 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnProjectileHitBlock.lua @@ -10,6 +10,11 @@ return Params = { { Name = "ProjectileEntity", Type = "{{cProjectileEntity}}", Notes = "The projectile that hit an entity." }, + { Name = "BlockX", Type = "number", Notes = "The X-coord where the projectile hit." }, + { Name = "BlockY", Type = "number", Notes = "The Y-coord where the projectile hit." }, + { Name = "BlockZ", Type = "number", Notes = "The Z-coord where the projectile hit." }, + { Name = "BlockFace", Type = "number", Notes = "The side of the block where the projectile hit." }, + { Name = "BlockHitPos", Type = "Vector3d", Notes = "The exact position where the projectile hit." }, }, Returns = [[ If the function returns false or no value, the next plugin's callback is called. If the function diff --git a/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua b/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua index d36164e8e..bb809af11 100644 --- a/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua +++ b/MCServer/Plugins/APIDump/Hooks/OnWeatherChanging.lua @@ -6,7 +6,7 @@ return DefaultFnName = "OnWeatherChanging", -- also used as pagename Desc = [[ This hook is called when the current weather has expired and a new weather is selected. Plugins may - override the new weather setting.</p> + override the new weather being set.</p> <p> The new weather setting is sent to the clients only after this hook has been processed.</p> <p> @@ -19,9 +19,12 @@ return { Name = "Weather", Type = "number", Notes = "The newly selected weather. One of wSunny, wRain, wStorm" }, }, Returns = [[ - If the function returns false or no value, the server calls other plugins' callbacks and finally - sets the weather. If the function returns true, the server takes the second returned value (wSunny - by default) and sets it as the new weather. No other plugins' callbacks are called in this case. + The hook handler can return up to two values. If the first value is false or not present, the server + calls other plugins' callbacks and finally sets the weather. If it is true, the server doesn't call any + more callbacks for this hook. The second value returned is used as the new weather. If no value is + given, the weather from the parameters is used as the weather. Returning false as the first value and a + specific weather constant as the second value makes the server call the rest of the hook handlers with + the new weather value. ]], }, -- HOOK_WEATHER_CHANGING } diff --git a/MCServer/Plugins/APIDump/InfoFile.html b/MCServer/Plugins/APIDump/InfoFile.html new file mode 100644 index 000000000..3fff06d20 --- /dev/null +++ b/MCServer/Plugins/APIDump/InfoFile.html @@ -0,0 +1,246 @@ +<!DOCTYPE html> +<html> + <head> + <title>MCServer - Info.lua file</title> + <link rel="stylesheet" type="text/css" href="main.css" /> + <link rel="stylesheet" type="text/css" href="prettify.css" /> + <script src="prettify.js"></script> + <script src="lang-lua.js"></script> + <meta charset="UTF-8"> + </head> + <body> + <div id="content"> + <h1>Info.lua file</h1> + <h2>Contents</h2> + <ul> + <li><a href="#Introduction">Introduction</a></li> + <li><a href="#Overall">The overall structure</a></li> + <li><a href="#AdditionalInformation">AdditionalInformation table</a></li> + <li><a href="#Commands">Commands table</a></li> + <li><a href="#ConsoleCommands">ConsoleCommands table</a></li> + <li><a href="#Permissions">Permissions table</a></li> + <li><a href="#Using">Using the file in code</a></li> + <li><a href="#Examples">Examples</a></li> + </ul> + + + <hr /> + <a name="Introduction"><h2>Introduction</h2></a> + + <p>For a long time MCServer plugins were plagued by poor documentation. The plugins worked, people who wrote them knew how to use them, but for anyone new to the plugin it was a terrible ordeal learning how to use it. Most of the times, the plugin authors only wrote what commands the plugin supported, sometimes not even that. Then, there was a call to action to put an end to this, to make documenting the plugins easy and at the same time centralized. Thus, the Info.lua file was born.</p> + + <p>Most plugins have some parts that are the same across all the plugins. These are commands, console commands and their permissions. If a plugin implemented a command, it would practically copy & paste the same code over and over again. So it makes sense to extract only unique information, centralize it and automate all the parts around it. This was another reason for the Info.lua file - it is a central hub of commands, console commands and their permissions.</p> + + <p>Last, but not least, we want to make a plugin repository on the web in the future, a repository that would store plugins, their descriptions, comments. It makes sense that the centralized information can be parsed by the repository automatically, so that advanced things, such as searching for a plugin based on a command, or determining whether two plugins collide command-wise, are possible.</p> + + <p>After this file format has been devised, a tool has been written that allows for an easy generation of the documentation for the plugin in various formats. It outputs the documentation in a format that is perfect for pasting into the forum. It generates documentation in a Markup format to use in README.md on GitHub and similar sites. The clever thing is that you don't need to keep all those formats in sync manually - you edit the Info.lua file and this tool will re-generate the documentation for you.</p> + + <p>So to sum up, the Info.lua file contains the plugins' commands, console commands, their permissions and possibly the overall plugin documentation, in a structured manner that can be parsed by a program, yet is human readable and editable.</p> + + + <hr /> + <a name="Overall"><h2>The overall structure</h2></a> + + <p>The file consist of a declaration of a single Lua table, g_PluginInfo. This table contains all the information, structured, as its members. Each member can be a structure by itself. The entire file is a valid Lua source file, so any tool that syntax-checks Lua source can syntax-check this file. The file is somewhat forward- and backward- compatible, in the sense that it can be extended in any way without breaking.</p> + <p>Here's a skeleton of the file:</p> +<pre class="prettyprint lang-lua"> +g_PluginInfo = +{ + Name = "Example Plugin", + Date = "2014-06-12", + Description = "This is an example plugin that shows how to use the Info.lua file", + + -- The following members will be documented in greater detail later: + AdditionalInformation = {}, + Commands = {}, + ConsoleCommands = {}, + Permissions = {}, +} +</pre> + <p>As you can see, the structure is pretty straightforward. Note that the order of the elements inside the table is not important (Lua property).</p> + + <p>The first few elements are for book-keeping. They declare the plugin's name, the date in ISO-format, representing the version of the plugin, and the description. The idea is that the description sums up what the plugin is all about, within some two or three sentences.</p> + + + <hr /> + <a name="AdditionalInformation"><h2>AdditionalInformation table</h2></a> + + <p>This table is used for more detailed description of the plugin. If there is any non-trivial setup process, dependencies, describe them here. This is where the description should get detailed. Don't worry about using several paragraphs of text here, if it makes the plugin easier to understand.</p> + + <p>The table should have the following layout:</p> +<pre class="prettyprint lang-lua"> +AdditionalInformation = +{ + { + Title = "Chapter 1", + Contents = "Describe one big aspect of the plugin here", + }, + { + Title = "Chapter 2", + Contents = "Describe another big topic", + }, +} +</pre> + <p>The idea here is that the tool that is used to generate the documentation from the Info.lua file will create a linkified table of contents and then each of the information elements' contents. This information should be all that is needed to successfully configure, run and manage the plugin.</p> + + + <hr /> + <a name="Commands"><h2>Commands table</h2></a> + + <p>The commands table lists all the commands that the plugin implements, together with their handler functions, required permissions, help strings and further information. The table supports recursion, which allows plugins to create multi-word commands easily (such as "//schematic load" and "//schematic save"), each having its own separate handler.</p> + + <p>The table uses structure similar to the following:</p> +<pre class="prettyprint lang-lua"> +Commands = +{ + ["/cmd1"] = + { + HelpString = "Performs the first action", + Permission = "firstplugin.cmds.1", + Alias = "/c1", + Handler = HandleCmd1, + ParameterCombinations = + { + { + Params = "x y z", + Help = "Performs the first action at the specified coordinates", + }, + { + Params = "-p", + Help = "Performs the first action at the player's coordinates", + } + }, + }, + ["/cmd2"] = + { + Alias = {"/c2", "//c2" }, + Subcommands = + { + sub1 = -- This declares a "/cmd2 sub1" command + { + HelpString = "Performs the second action's first subcommand", + Permission = "firstplugin.cmds.2.1", + Alias = "1", + Handler = HandleCmd2Sub1, + ParameterCombinations = + { + { + Params = "x y z", + Help = "Performs the second action's first subcommand at the specified coordinates", + }, + { + Params = "-p", + Help = "Performs the second action's first subcommand at the player's coordinates", + } + }, + }, + sub2 = -- Declares a "/cmd2 sub2" command + { + HelpString = "Performs the second action's second subcommand", + Permission = "firstplugin.cmds.2.2", + Handler = HandleCmd2Sub2, + }, + }, + }, +} +</pre> + + <p>Although it may seem overwhelming at first, there is a "method to this madness". Each element of the Commands table defines one command. Most commands start with a slash, so the special Lua syntax for table elements with non-standard names needs to be applied (<code>["/cmd1"] =</code>). The command can either specify subcommands, or a handler function (specifying both is UndefinedBehavior). Subcommands uses the same structure as the entire Commands table, recursively.</p> + + <p>The permission element specifies that the command is only available with the specified permission. Note that the permission for subcommand's parent isn't checked when the subcommand is called. This means that specifying the permission for a command that has subcommands has no effect whatsoever, but is discouraged because we may add processing for that in the future.</p> + + <p>The ParameterCombinations table is used only for generating the documentation, it lists the various combinations of parameters that the command supports. It's worth specifying even if the command supports only one combination, because that combination will get documented this way.</p> + + <p>The Alias member specifies any possible aliases for the command. Each alias is registered separately and if there is a subcommand table, it is applied to all aliases, just as one would expect. You can specify either a single string as the value (if there's only one alias), or a table of strings for multiple aliases. Commands with no aliases do not need to specify this member at all.</p> + + + <hr /> + <a name="ConsoleCommands"><h2>ConsoleCommands table</h2> + + <p>This table serves a purpose similar to that of the Commands table, only these commands are provided for the server console. Therefore, there are no permissions specified for these commands. Since most console commands don't use a leading slash, the command names don't need the special syntax. Also, the handler function doesn't receive the Player parameter.</p> + + <p>Here's an example of a ConsoleCommands table:</p> +<pre class="prettyprint lang-lua"> +ConsoleCommands = +{ + concmd = + { + HelpString = "Performs the console action", + Subcommands = + { + sub1 = + { + HelpString = "Performs the console action's first subcommand", + Handler = HandleConCmdSub1, + ParameterCombinations = + { + { + Params = "x y z", + Help = "Performs the console action's first subcommand at the specified coordinates", + }, + }, + }, + sub2 = + { + HelpString = "Performs the console action's second subcommand", + Handler = HandleConCmdSub2, + }, + }, + }, +} +</pre> + + + <hr /> + <a name="Permissions"><h2>Permissions table</h2></a> + + <p>The purpose of this table is to document permissions that the plugin uses. The documentation generator automatically collects the permissions specified in the Command table; the Permissions table adds a description for these permissions and may declare other permissions that aren't specifically included in the Command table.</p> + +<pre class="prettyprint lang-lua"> +Permissions = +{ + ["firstplugin.cmd.1.1"] = + { + Description = "Allows the players to build high towers using the first action.", + RecommendedGroups = "players", + }, + ["firstplugin.cmd.2.1"] = + { + Description = "Allows the players to kill entities using the second action. Note that this may be misused to kill other players, too.", + RecommendedGroups = "admins, mods", + }, +} +</pre> + + <p>The RecommendedGroup element lists, in plain English, the intended groups for which the permission should be enabled on a typical server. Plugin authors are advised to create reasonable defaults, prefering security to openness, so that admins using these settings blindly don't expose their servers to malicious users.</p> + + + <hr /> + <a name="Using"><h2>Using the file in code</h2></a> + + <p>Just writing the Info.lua file and saving it to the plugin folder is not enough for it to actually be used. Your plugin needs to include the following boilerplate code, preferably in its Initialize() function:</p> +<pre class="prettyprint lang-lua"> +-- Use the InfoReg shared library to process the Info.lua file: +dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua") +RegisterPluginInfoCommands() +RegisterPluginInfoConsoleCommands() +</pre> + + <p>Of course, if your plugin doesn't have any console commands, it doesn't need to call the RegisterPluginInfoConsoleCommands() function, and similarly if it doesn't have any in-game commands, it doesn't need to call the RegisterPluginInfoCommands() function.</p> + + + <hr /> + <a name="Examples"><h2>Examples</h2></a> + + <p>There are several plugins that already implement this approach. You can visit them for inspiration and to see what the generated documentation looks like:</p> + <ul> + <li>Gallery plugin: <a href="https://github.com/mc-server/Gallery/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=1306">Forum</a> documentation</li> + <li>WorldEdit plugin: <a href="https://github.com/mc-server/WorldEdit/blob/master/Info.lua">Info.lua</a>, <a href="http://forum.mc-server.org/showthread.php?tid=870">Forum</a> and <a href="https://github.com/mc-server/WorldEdit">MarkDown</a> documentation</li> + </ul> + + <script> + prettyPrint(); + </script> + </div> + </body> +</html> diff --git a/MCServer/Plugins/APIDump/main.css b/MCServer/Plugins/APIDump/main.css index aa26bd186..8041e0d01 100644 --- a/MCServer/Plugins/APIDump/main.css +++ b/MCServer/Plugins/APIDump/main.css @@ -39,7 +39,8 @@ pre body { - min-width: 800px; + min-width: 400px; + max-width: 1200px; width: 95%; margin: 10px auto; background-color: white; diff --git a/MCServer/Plugins/APIDump/main_APIDump.lua b/MCServer/Plugins/APIDump/main_APIDump.lua index 52199740b..a25bab9cf 100644 --- a/MCServer/Plugins/APIDump/main_APIDump.lua +++ b/MCServer/Plugins/APIDump/main_APIDump.lua @@ -27,10 +27,14 @@ local function LoadAPIFiles(a_Folder, a_DstTable) -- We only want .lua files from the folder: if (cFile:IsFile(FileName) and fnam:match(".*%.lua$")) then local TablesFn, Err = loadfile(FileName); - if (TablesFn == nil) then + if (type(TablesFn) ~= "function") then LOGWARNING("Cannot load API descriptions from " .. FileName .. ", Lua error '" .. Err .. "'."); else local Tables = TablesFn(); + if (type(Tables) ~= "table") then + LOGWARNING("Cannot load API descriptions from " .. FileName .. ", returned object is not a table (" .. type(Tables) .. ")."); + break + end for k, cls in pairs(Tables) do a_DstTable[k] = cls; end diff --git a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua index 6c5eab676..9aec38b12 100644 --- a/MCServer/Plugins/ChunkWorx/chunkworx_web.lua +++ b/MCServer/Plugins/ChunkWorx/chunkworx_web.lua @@ -43,7 +43,7 @@ function HandleRequest_Generation( Request ) local Content = "" if (Request.PostParams["AGHRRRR"] ~= nil) then GENERATION_STATE = 0 - WW_instance:SaveAllChunks() + WW_instance:QueueSaveAllChunks() WW_instance:QueueUnloadUnusedChunks() LOGERROR("" .. PLUGIN:GetName() .. " v" .. PLUGIN:GetVersion() .. ": works ABORTED by admin") end diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 064d5d772..b402c1867 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -29,7 +29,10 @@ function Initialize(Plugin) PM:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick); PM:AddHook(cPluginManager.HOOK_PLUGINS_LOADED, OnPluginsLoaded); PM:AddHook(cPluginManager.HOOK_PLUGIN_MESSAGE, OnPluginMessage); - PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined) + PM:AddHook(cPluginManager.HOOK_PLAYER_JOINED, OnPlayerJoined); + PM:AddHook(cPluginManager.HOOK_PROJECTILE_HIT_BLOCK, OnProjectileHitBlock); + PM:AddHook(cPluginManager.HOOK_CHUNK_UNLOADING, OnChunkUnloading); + PM:AddHook(cPluginManager.HOOK_WORLD_STARTED, OnWorldStarted); -- _X: Disabled so that the normal operation doesn't interfere with anything -- PM:AddHook(cPluginManager.HOOK_CHUNK_GENERATED, OnChunkGenerated); @@ -57,9 +60,10 @@ function Initialize(Plugin) PM:BindCommand("/ff", "debuggers", HandleFurnaceFuel, "- Shows how long the currently held item would burn in a furnace"); PM:BindCommand("/sched", "debuggers", HandleSched, "- Schedules a simple countdown using cWorld:ScheduleTask()"); PM:BindCommand("/cs", "debuggers", HandleChunkStay, "- Tests the ChunkStay Lua integration for the specified chunk coords"); - PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings") - PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one") - PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z") + PM:BindCommand("/compo", "debuggers", HandleCompo, "- Tests the cCompositeChat bindings"); + PM:BindCommand("/sb", "debuggers", HandleSetBiome, "- Sets the biome around you to the specified one"); + PM:BindCommand("/wesel", "debuggers", HandleWESel, "- Expands the current WE selection by 1 block in X/Z"); + PM:BindCommand("/rmitem", "debuggers", HandleRMItem, "- Remove the specified item from the inventory."); Plugin:AddWebTab("Debuggers", HandleRequest_Debuggers) Plugin:AddWebTab("StressTest", HandleRequest_StressTest) @@ -345,7 +349,7 @@ end function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ) -- Magic rod of query: show block types and metas for both neighbors of the pointed face - local Type, Meta, Valid = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ, Type, Meta); + local Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(BlockX, BlockY, BlockZ); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. BlockX .. ", " .. BlockY .. ", " .. BlockZ .. "}: air:" .. Meta); @@ -355,7 +359,7 @@ function OnUsingBlazeRod(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cur end local X, Y, Z = AddFaceDirection(BlockX, BlockY, BlockZ, BlockFace); - Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z, Type, Meta); + Valid, Type, Meta = Player:GetWorld():GetBlockTypeMeta(X, Y, Z); if (Type == E_BLOCK_AIR) then Player:SendMessage(cChatColor.LightGray .. "Block {" .. X .. ", " .. Y .. ", " .. Z .. "}: air:" .. Meta); else @@ -530,7 +534,7 @@ function OnTakeDamage(Receiver, TDI) -- Receiver is cPawn -- TDI is TakeDamageInfo - LOG(Receiver:GetClass() .. " was dealt " .. DamageTypeToString(TDI.DamageType) .. " damage: Raw " .. TDI.RawDamage .. ", Final " .. TDI.FinalDamage .. " (" .. (TDI.RawDamage - TDI.FinalDamage) .. " covered by armor)"); + -- LOG(Receiver:GetClass() .. " was dealt " .. DamageTypeToString(TDI.DamageType) .. " damage: Raw " .. TDI.RawDamage .. ", Final " .. TDI.FinalDamage .. " (" .. (TDI.RawDamage - TDI.FinalDamage) .. " covered by armor)"); return false; end @@ -1102,6 +1106,41 @@ end +function HandleRMItem(a_Split, a_Player) + -- Check params: + if (a_Split[2] == nil) then + a_Player:SendMessage("Usage: /rmitem <Item> [Count]") + return true + end + + -- Parse the item type: + local Item = cItem() + if (not StringToItem(a_Split[2], Item)) then + a_Player:SendMessageFailure(a_Split[2] .. " isn't a valid item") + return true + end + + -- Parse the optional item count + if (a_Split[3] ~= nil) then + local Count = tonumber(a_Split[3]) + if (Count == nil) then + a_Player:SendMessageFailure(a_Split[3] .. " isn't a valid number") + return true + end + + Item.m_ItemCount = Count + end + + -- Remove the item: + local NumRemovedItems = a_Player:GetInventory():RemoveItem(Item) + a_Player:SendMessageSuccess("Removed " .. NumRemovedItems .. " Items!") + return true +end + + + + + function HandleRequest_Debuggers(a_Request) local FolderContents = cFile:GetFolderContents("./"); return "<p>The following objects have been returned by cFile:GetFolderContents():<ul><li>" .. table.concat(FolderContents, "</li><li>") .. "</li></ul></p>"; @@ -1379,3 +1418,40 @@ end + +function OnProjectileHitBlock(a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockHitPos) + -- Test projectile hooks by setting the blocks they hit on fire: + local BlockX, BlockY, BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace) + local World = a_Projectile:GetWorld() + + World:SetBlock(BlockX, BlockY, BlockZ, E_BLOCK_FIRE, 0) +end + + + + + +function OnChunkUnloading(a_World, a_ChunkX, a_ChunkZ) + -- Do not let chunk [0, 0] unload, so that it continues ticking [cWorld:SetChunkAlwaysTicked() test] + if ((a_ChunkX == 0) and (a_ChunkZ == 0)) then + return true + end +end + + + + + +function OnWorldStarted(a_World) + -- Make the chunk [0, 0] in every world keep ticking [cWorld:SetChunkAlwaysTicked() test] + a_World:ChunkStay({{0, 0}}, nil, + function() + -- The chunk is loaded, make it always tick: + a_World:SetChunkAlwaysTicked(0, 0, true) + end + ) +end + + + + diff --git a/MCServer/Plugins/DiamondMover/DiamondMover.lua b/MCServer/Plugins/DiamondMover/DiamondMover.lua index 0fdd32250..d3e70acfc 100644 --- a/MCServer/Plugins/DiamondMover/DiamondMover.lua +++ b/MCServer/Plugins/DiamondMover/DiamondMover.lua @@ -22,6 +22,8 @@ function Initialize(Plugin) Plugin:SetVersion(1); cPluginManager.AddHook(cPluginManager.HOOK_PLAYER_USED_ITEM, OnPlayerUsedItem); + + LOG("Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion()); return true; end @@ -36,8 +38,8 @@ function OnPlayerUsedItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cu return false; end; - if (Player:HasPermission("diamondmover.move") == false) then - return true; + if (not Player:HasPermission("diamondmover.move")) then + return false; end; -- Rclk with a diamond to push in the direction the player is facing @@ -56,7 +58,7 @@ function OnPlayerUsedItem(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, Cu if (PlayerPitch > 70) then -- looking down BlockY = BlockY - 1; else - local PlayerRot = Player:GetRotation() + 180; -- Convert [-180, 180] into [0, 360] for simpler conditions + local PlayerRot = Player:GetYaw() + 180; -- Convert [-180, 180] into [0, 360] for simpler conditions if ((PlayerRot < 45) or (PlayerRot > 315)) then BlockZ = BlockZ - 1; else diff --git a/MCServer/Plugins/Handy/handy_functions.lua b/MCServer/Plugins/Handy/handy_functions.lua index c142ffd08..af43f663a 100644 --- a/MCServer/Plugins/Handy/handy_functions.lua +++ b/MCServer/Plugins/Handy/handy_functions.lua @@ -6,7 +6,7 @@ function GetHandyVersion() return HANDY_VERSION end -- Checks if handy is in proper version -function CheckForRequiedVersion( inVersion ) +function CheckForRequiredVersion( inVersion ) if( inVersion > HANDY_VERSION ) then return false end return true end @@ -213,4 +213,4 @@ end function BoolToString( inValue ) if( inValue == true ) then return 1 end return 0 -end
\ No newline at end of file +end |