diff options
Diffstat (limited to '')
-rw-r--r-- | MCServer/Plugins/.gitignore | 3 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/CommandHandlers.lua | 322 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/CommandState.lua | 121 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/Config.lua | 55 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/CurrentLng.lua | 76 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/HookHandlers.lua | 139 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/LICENSE.txt | 7 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/PlayerAreas.lua | 109 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/ProtectionAreas.deproj | 27 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua | 71 | ||||
-rw-r--r-- | MCServer/Plugins/ProtectionAreas/Storage.lua | 518 |
11 files changed, 2 insertions, 1446 deletions
diff --git a/MCServer/Plugins/.gitignore b/MCServer/Plugins/.gitignore index a9f9bd175..010351de2 100644 --- a/MCServer/Plugins/.gitignore +++ b/MCServer/Plugins/.gitignore @@ -1 +1,2 @@ -Core
\ No newline at end of file +Core +ProtectionAreas
\ No newline at end of file diff --git a/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua b/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua deleted file mode 100644 index 26df73075..000000000 --- a/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua +++ /dev/null @@ -1,322 +0,0 @@ - --- CommandHandlers.lua --- Defines the individual command handlers - - - - - -function InitializeCommandHandlers() - local PlgMgr = cRoot:Get():GetPluginManager(); - for idx, Cmd in ipairs(CommandReg()) do - PlgMgr:BindCommand(Cmd[2], Cmd[3], Cmd[1], Cmd[4]); - end -end - - - - - ---- Handles the ProtAdd command -function HandleAddArea(a_Split, a_Player) - -- Command syntax: ProtAdd username1 [username2] [username3] ... - if (#a_Split < 2) then - a_Player:SendMessage(g_Msgs.ErrExpectedListOfUsernames); - return true; - end - - -- Get the cuboid that the player had selected - local CmdState = GetCommandStateForPlayer(a_Player); - if (CmdState == nil) then - a_Player:SendMessage(g_Msgs.ErrCmdStateNilAddArea); - return true; - end - local Cuboid = CmdState:GetCurrentCuboid(); - if (Cuboid == nil) then - a_Player:SendMessage(g_Msgs.ErrNoAreaWanded); - return true; - end - - -- Put all allowed players into a table: - AllowedNames = {}; - for i = 2, #a_Split do - table.insert(AllowedNames, a_Split[i]); - end - - -- Add the area to the storage - local AreaID = g_Storage:AddArea(Cuboid, a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames); - a_Player:SendMessage(string.format(g_Msgs.AreaAdded, AreaID)); - - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - -function HandleAddAreaCoords(a_Split, a_Player) - -- Command syntax: ProtAddCoords x1 z1 x2 z2 username1 [username2] [username3] ... - if (#a_Split < 6) then - a_Player:SendMessage(g_Msgs.ErrExpectedCoordsUsernames); - return true; - end - - -- Convert the coords to a cCuboid - local x1, z1 = tonumber(a_Split[2]), tonumber(a_Split[3]); - local x2, z2 = tonumber(a_Split[4]), tonumber(a_Split[5]); - if ((x1 == nil) or (z1 == nil) or (x2 == nil) or (z2 == nil)) then - a_Player:SendMessage(g_Msgs.ErrParseCoords); - return true; - end - local Cuboid = cCuboid(x1, 0, z1, x2, 255, z1); - Cuboid:Sort(); - - -- Put all allowed players into a table: - AllowedNames = {}; - for i = 6, #a_Split do - table.insert(AllowedNames, a_Split[i]); - end - - -- Add the area to the storage - local AreaID = g_Storage:AddArea(Cuboid, a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames); - a_Player:SendMessage(string.format(g_Msgs.AreaAdded, AreaID)); - - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - -function HandleAddAreaUser(a_Split, a_Player) - -- Command syntax: ProtAddUser AreaID username1 [username2] [username3] ... - if (#a_Split < 3) then - a_Player:SendMessage(g_Msgs.ErrExpectedAreaIDUsernames); - return true; - end - - -- Put all allowed players into a table: - AllowedNames = {}; - for i = 3, #a_Split do - table.insert(AllowedNames, a_Split[i]); - end - - -- Add the area to the storage - if (not(g_Storage:AddAreaUsers( - tonumber(a_Split[2]), a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames)) - ) then - LOGWARNING("g_Storage:AddAreaUsers failed"); - a_Player:SendMessage(g_Msgs.ErrDBFailAddUsers); - return true; - end - if (#AllowedNames == 0) then - a_Player:SendMessage(g_Msgs.AllUsersAlreadyAllowed); - else - a_Player:SendMessage(string.format(g_Msgs.UsersAdded, table.concat(AllowedNames, ", "))); - end - - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - -function HandleDelArea(a_Split, a_Player) - -- Command syntax: ProtDelArea AreaID - if (#a_Split ~= 2) then - a_Player:SendMessage(g_Msgs.ErrExpectedAreaID); - return true; - end - - -- Parse the AreaID - local AreaID = tonumber(a_Split[2]); - if (AreaID == nil) then - a_Player:SendMessage(g_Msgs.ErrParseAreaID); - return true; - end - - -- Delete the area - g_Storage:DelArea(a_Player:GetWorld():GetName(), AreaID); - - a_Player:SendMessage(string.format(g_Msgs.AreaDeleted, AreaID)); - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - -function HandleGiveWand(a_Split, a_Player) - local NumGiven = a_Player:GetInventory():AddItem(cConfig:GetWandItem()); - if (NumGiven == 1) then - a_Player:SendMessage(g_Msgs.WandGiven); - else - a_Player:SendMessage(g_Msgs.ErrNoSpaceForWand); - end - return true; -end - - - - - -function HandleListAreas(a_Split, a_Player) - -- Command syntax: ProtListAreas [x, z] - - local x, z; - if (#a_Split == 1) then - -- Get the last "wanded" coord - local CmdState = GetCommandStateForPlayer(a_Player); - if (CmdState == nil) then - a_Player:SendMessage(g_Msgs.ErrCmdStateNilListAreas); - return true; - end - x, z = CmdState:GetLastCoords(); - if ((x == nil) or (z == nil)) then - a_Player:SendMessage(g_Msgs.ErrListNotWanded); - return true; - end - elseif (#a_Split == 3) then - -- Parse the coords from the command params - x = tonumber(a_Split[2]); - z = tonumber(a_Split[3]); - if ((x == nil) or (z == nil)) then - a_Player:SendMessage(g_Msgs.ErrParseCoordsListAreas); - return true; - end - else - -- Wrong number of params, report back to the user - a_Player:SendMessage(g_Msgs.ErrSyntaxErrorListAreas); - return true; - end - - a_Player:SendMessage(string.format(g_Msgs.ListAreasHeader, x, z)); - - -- List areas intersecting the coords - local PlayerName = a_Player:GetName(); - local WorldName = a_Player:GetWorld():GetName(); - g_Storage:ForEachArea(x, z, WorldName, - function(AreaID, MinX, MinZ, MaxX, MaxZ, CreatorName) - local Coords = string.format("%s: {%d, %d} - {%d, %d} ", AreaID, MinX, MinZ, MaxX, MaxZ); - local Allowance; - if (g_Storage:IsAreaAllowed(AreaID, PlayerName, WorldName)) then - Allowance = g_Msgs.AreaAllowed; - else - Allowance = g_Msgs.AreaNotAllowed; - end - a_Player:SendMessage(string.format(g_Msgs.ListAreasRow, Coords, Allowance, CreatorName)); - end - ); - - a_Player:SendMessage(g_Msgs.ListAreasFooter); - return true; -end - - - - ---- Lists all allowed users for a particular area -function HandleListUsers(a_Split, a_Player) - -- Command syntax: ProtListUsers AreaID - if (#a_Split ~= 2) then - a_Player:SendMessage(g_Msgs.ErrExpectedAreaID); - end - - -- Get the general info about the area - local AreaID = a_Split[2]; - local WorldName = a_Player:GetWorld():GetName(); - local MinX, MinZ, MaxX, MaxZ, CreatorName = g_Storage:GetArea(AreaID, WorldName); - if (MinX == nil) then - a_Player:SendMessage(string.format(g_Msgs.ErrNoSuchArea, AreaID)); - return true; - end - - -- Send the header - a_Player:SendMessage(string.format(g_Msgs.ListUsersHeader, AreaID, MinX, MinZ, MaxX, MaxZ, CreatorName)); - - -- List and count the allowed users - local NumUsers = 0; - g_Storage:ForEachUserInArea(AreaID, WorldName, - function(UserName) - a_Player:SendMessage(string.format(g_Msgs.ListUsersRow, UserName)); - NumUsers = NumUsers + 1; - end - ); - - -- Send the footer - a_Player:SendMessage(string.format(g_Msgs.ListUsersFooter, AreaID, NumUsers)); - - return true; -end - - - - - -function HandleRemoveUser(a_Split, a_Player) - -- Command syntax: ProtRemUser AreaID UserName - if (#a_Split ~= 3) then - a_Player:SendMessage(g_Msgs.ErrExpectedAreaIDUserName); - return true; - end - - -- Parse the AreaID - local AreaID = tonumber(a_Split[2]); - if (AreaID == nil) then - a_Player:SendMessage(g_Msgs.ErrParseAreaID); - return true; - end - - -- Remove the user from the DB - local UserName = a_Split[3]; - g_Storage:RemoveUser(AreaID, UserName, a_Player:GetWorld():GetName()); - - -- Send confirmation - a_Player:SendMessage(string.format(g_Msgs.RemovedUser, UserName, AreaID)); - - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - -function HandleRemoveUserAll(a_Split, a_Player) - -- Command syntax: ProtRemUserAll UserName - if (#a_Split ~= 2) then - a_Player:SendMessage(g_Msgs.ErrExpectedUserName); - return true; - end - - -- Remove the user from the DB - g_Storage:RemoveUserAll(a_Split[2], a_Player:GetWorld():GetName()); - - -- Send confirmation - a_Player:SendMessage(string.format(g_Msgs.RemovedUserAll, UserName)); - - -- Reload all currently logged in players - ReloadAllPlayersInWorld(a_Player:GetWorld():GetName()); - - return true; -end - - - - - diff --git a/MCServer/Plugins/ProtectionAreas/CommandState.lua b/MCServer/Plugins/ProtectionAreas/CommandState.lua deleted file mode 100644 index f6d33d356..000000000 --- a/MCServer/Plugins/ProtectionAreas/CommandState.lua +++ /dev/null @@ -1,121 +0,0 @@ - --- CommandState.lua - --- Implements the cCommandState class representing a command state for each VIP player - ---[[ -The command state holds internal info, such as the coords they selected using the wand -The command state needs to be held in a per-entity manner, so that we can support multiple logins -from the same account (just for the fun of it) -The OOP class implementation follows the PiL 16.1 - -Also, a global table g_CommandStates is the map of PlayerEntityID -> cCommandState ---]] - - - - - -cCommandState = { - -- Default coords - m_Coords1 = {x = 0, z = 0}; -- lclk coords - m_Coords2 = {x = 0, z = 0}; -- rclk coords - m_LastCoords = 0; -- When Coords1 or Coords2 is set, this gets set to 1 or 2, signifying the last changed set of coords - m_HasCoords1 = false; -- Set to true when m_Coords1 has been set by the user - m_HasCoords2 = false; -- Set to true when m_Coords2 has been set by the user -}; - -g_CommandStates = {}; - - - - - -function cCommandState:new(obj) - obj = obj or {}; - setmetatable(obj, self); - self.__index = self; - return obj; -end - - - - - ---- Returns the current coord pair as a cCuboid object -function cCommandState:GetCurrentCuboid() - if (not(self.m_HasCoords1) or not(self.m_HasCoords2)) then - -- Some of the coords haven't been set yet - return nil; - end - - local res = cCuboid( - self.m_Coords1.x, 0, self.m_Coords1.z, - self.m_Coords2.x, 255, self.m_Coords2.z - ); - res:Sort(); - return res; -end - - - - - ---- Returns the x, z coords that were the set last, --- That is, either m_Coords1 or m_Coords2, based on m_LastCoords member --- Returns nothing if no coords were set yet -function cCommandState:GetLastCoords() - if (self.m_LastCoords == 0) then - -- No coords have been set yet - return; - elseif (self.m_LastCoords == 1) then - return self.m_Coords1.x, self.m_Coords1.z; - elseif (self.m_LastCoords == 2) then - return self.m_Coords2.x, self.m_Coords2.z; - else - LOGWARNING(PluginPrefix .. "cCommandState is in an unexpected state, m_LastCoords == " .. self.m_LastCoords); - return; - end -end - - - - - ---- Sets the first set of coords (upon rclk with a wand) -function cCommandState:SetCoords1(a_BlockX, a_BlockZ) - self.m_Coords1.x = a_BlockX; - self.m_Coords1.z = a_BlockZ; - self.m_LastCoords = 1; - self.m_HasCoords1 = true; -end - - - - - ---- Sets the second set of coords (upon lclk with a wand) -function cCommandState:SetCoords2(a_BlockX, a_BlockZ) - self.m_Coords2.x = a_BlockX; - self.m_Coords2.z = a_BlockZ; - self.m_LastCoords = 2; - self.m_HasCoords2 = true; -end - - - - - ---- Returns the cCommandState for the specified player; creates one if not existant -function GetCommandStateForPlayer(a_Player) - local res = g_CommandStates[a_Player:GetUniqueID()]; - if (res == nil) then - res = cCommandState:new(); - g_CommandStates[a_Player:GetUniqueID()] = res; - end - return res; -end; - - - - diff --git a/MCServer/Plugins/ProtectionAreas/Config.lua b/MCServer/Plugins/ProtectionAreas/Config.lua deleted file mode 100644 index b40be0c75..000000000 --- a/MCServer/Plugins/ProtectionAreas/Config.lua +++ /dev/null @@ -1,55 +0,0 @@ - --- Config.lua - --- Implements the cConfig class that holds the general plugin configuration - - - - - -cConfig = { - m_Wand = cItem(E_ITEM_STICK, 1, 1); -- The item to be used as the selection wand - m_AllowInteractNoArea = true; -- If there's no area, is a player allowed to build / dig? -}; - - - - - ---- Initializes the cConfig object, loads the configuration from an INI file -function InitializeConfig() - local ini = cIniFile("ProtectionAreas.ini"); - if (not(ini:ReadFile())) then - LOGINFO(PluginPrefix .. "Cannot read ProtectionAreas.ini, all plugin configuration is set to defaults"); - end - local WandItem = cItem(); - if ( - StringToItem(ini:GetValueSet("ProtectionAreas", "WandItem", ItemToString(cConfig.m_Wand)), WandItem) and - IsValidItem(WandItem.m_ItemType) - ) then - cConfig.m_Wand = WandItem; - end - cConfig.m_AllowInteractNoArea = ini:GetValueSetB("ProtectionAreas", "AllowInteractNoArea", cConfig.m_AllowInteractNoArea); - ini:WriteFile(); -end - - - - - ---- Returns true if a_Item is the wand tool item -function cConfig:IsWand(a_Item) - return ( - (a_Item.m_ItemType == self.m_Wand.m_ItemType) and - (a_Item.m_ItemDamage == self.m_Wand.m_ItemDamage) - ); -end - - - - - ---- Returns the wand tool item as a cItem object -function cConfig:GetWandItem() - return self.m_Wand; -end
\ No newline at end of file diff --git a/MCServer/Plugins/ProtectionAreas/CurrentLng.lua b/MCServer/Plugins/ProtectionAreas/CurrentLng.lua deleted file mode 100644 index 37ff135c5..000000000 --- a/MCServer/Plugins/ProtectionAreas/CurrentLng.lua +++ /dev/null @@ -1,76 +0,0 @@ - --- CurrentLng.lua --- This file provides all the translatable strings --- The expectation is that the translators will create copies of this file, translate the texts and then the users will overwrite this file with a specific language version --- Note that the individual languages must not have ".lua" extension, otherwise MCServer will load them and the plugin won't work! - - - - --- Individual commands, and their help strings. Don't touch the first symbol on each line! --- This needs to be implemented as a function, because it references other functions which might not yet be loaded while Lua is processing the globals - -function CommandReg() - return { - -- Handler function | Command | Permission | Help text - {HandleAddArea, "/ProtAdd", "Prot.Add", "<UserNames> - Adds a new protected area"}, - {HandleAddAreaCoords, "/ProtAddCoords", "Prot.Add", "<x1> <z1> <x2> <z2> <UserNames> - Adds a new protected area by coords"}, - {HandleAddAreaUser, "/ProtAddUser", "Prot.AddUser", "<AreaID> <UserNames> - Adds new users to an existing protected area"}, - {HandleDelArea, "/ProtDelID", "Prot.Del", "<AreaID> - Deletes a protected area by ID"}, - {HandleGiveWand, "/ProtWand", "Prot.Wand", " - Gives you the wand used for protection"}, - {HandleListAreas, "/ProtList", "Prot.List", "[<x> <z>] - Lists all areas for the marked block or given coords"}, - {HandleListUsers, "/ProtUsers", "Prot.List", "<AreaID> - Lists all allowed users for a given area ID"}, - {HandleRemoveUser, "/ProtRemUser", "Prot.RemUser", "<AreaID> <UserName> - Removes a user from the protected area"}, - {HandleRemoveUserAll, "/ProtRemUserAll", "Prot.RemUser", "<UserName> - Removes a user from all protected areas"}, - }; -end; - - - - - ---- Messages sent to players -g_Msgs = -{ - AllUsersAlreadyAllowed = "All the specified users were already allowed."; - AreaAdded = "Area added, ID %s"; - AreaAllowed = "Allowed"; - AreaDeleted = "Area ID %s deleted"; - AreaNotAllowed = "NOT allowed"; - Coords1Set = "Coords1 set as {%d, %d}"; - Coords2Set = "Coords2 set as {%d, %d}"; - ErrCmdStateNilAddArea = "Cannot add area, internal plugin error (CmdState == nil)"; - ErrCmdStateNilListAreas = "Cannot list areas, internal plugin error (CmdState == nil)"; - ErrDBFailAddUsers = "Cannot add users, DB failure"; - ErrExpectedAreaID = "Parameter mismatch. Expected <AreaID>."; - ErrExpectedAreaIDUserName = "Parameter mismatch. Expected <AreaID> <UserName>."; - ErrExpectedAreaIDUsernames = "Not enough parameters. Expected <AreaID> and a list of usernames."; - ErrExpectedCoordsUsernames = "Not enough parameters. Expected <x1> <z1> <x2> <z2> coords and a list of usernames."; - ErrExpectedListOfUsernames = "Not enough parameters. Expected a list of usernames."; - ErrExpectedUserName = "Parameter mismatch. Expected <UserName>."; - ErrListNotWanded = "Cannot list areas, no query point has been selected. Use a ProtWand lclk / rclk to select a point first"; - ErrNoAreaWanded = "Cannot add area, no area has been selected. Use a ProtWand lclk / rclk to select area first"; - ErrNoSpaceForWand = "Cannot give wand, no space in your inventory"; - ErrNoSuchArea = "No such area: %s"; - ErrParseAreaID = "Cannot parse <AreaID>."; - ErrParseCoords = "Cannot parse coords."; - ErrParseCoordsListAreas = "Cannot list areas, cannot parse coords in params"; - ErrSyntaxErrorListAreas = "Cannot list areas, syntax error. Expected either no params or <x> <z>."; - ListAreasFooter = "Area list finished"; - ListAreasHeader = "Listing protection areas intersecting block column {%d, %d}:"; - ListAreasRow = " %s, %s, created by %s"; - ListUsersFooter = "End of area %s user list, total %d users"; - ListUsersHeader = "Area ID %s: {%d, %d} - {%d, %d}, created by %s; allowed users:"; - ListUsersRow = " %s"; - NotAllowedToBuild = "You are not allowed to build here!"; - NotAllowedToDig = "You are not allowed to dig here!"; - RemovedUser = "Removed %s from area %d"; - RemovedUserAll = "Removed %s from all areas"; - UsersAdded = "Users added: %s"; - WandGiven = "Wand given"; -} ; - - - - - diff --git a/MCServer/Plugins/ProtectionAreas/HookHandlers.lua b/MCServer/Plugins/ProtectionAreas/HookHandlers.lua deleted file mode 100644 index ded64d298..000000000 --- a/MCServer/Plugins/ProtectionAreas/HookHandlers.lua +++ /dev/null @@ -1,139 +0,0 @@ - --- HookHandlers.lua --- Implements the handlers for individual hooks - - - - - ---- Registers all the hooks that the plugin needs to know about -function InitializeHooks(a_Plugin) - local PlgMgr = cRoot:Get():GetPluginManager(); - PlgMgr:AddHook(a_Plugin, cPluginManager.HOOK_DISCONNECT); - PlgMgr:AddHook(a_Plugin, cPluginManager.HOOK_PLAYER_LEFT_CLICK); - PlgMgr:AddHook(a_Plugin, cPluginManager.HOOK_PLAYER_MOVING); - PlgMgr:AddHook(a_Plugin, cPluginManager.HOOK_PLAYER_RIGHT_CLICK); - PlgMgr:AddHook(a_Plugin, cPluginManager.HOOK_PLAYER_SPAWNED); -end - - - - - ---- Called by MCS when a player's connectino is lost - either they disconnected or timed out -function OnDisconnect(a_Player, a_Reason) - -- Remove the player's cProtectionArea object - g_PlayerAreas[a_Player:GetUniqueID()] = nil; - - -- If the player is a VIP, they had a command state, remove that as well - g_CommandStates[a_Player:GetUniqueID()] = nil; - - return false; -end; - - - - - ---- Called by MCS whenever a player enters a world (is spawned) -function OnPlayerSpawned(a_Player) - -- Create a new cPlayerAreas object for this player - if (g_PlayerAreas[a_Player:GetUniqueID()] == nil) then - LoadPlayerAreas(a_Player); - end; - - return false; -end - - - - - ---- Called by MCS whenever a player is moving (at most once every tick) -function OnPlayerMoving(a_Player) - local PlayerID = a_Player:GetUniqueID(); - - -- If for some reason we don't have a cPlayerAreas object for this player, load it up - local PlayerAreas = g_PlayerAreas[PlayerID]; - if (PlayerAreas == nil) then - LoadPlayerAreas(a_Player); - return false; - end; - - -- If the player is outside their areas' safe space, reload - if (not(PlayerAreas:IsInSafe(a_Player:GetPosX(), a_Player:GetPosZ()))) then - LoadPlayerAreas(a_Player); - end - return false; -end - - - - - ---- Called by MCS when a player left-clicks -function OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status) - -- If the player has lclked with the wand; regardless of their permissions, let's set the coords: - if (cConfig:IsWand(a_Player:GetEquippedItem())) then - -- BlockFace < 0 means "use item", for which the coords are not given by the client - if (a_BlockFace < 0) then - return true; - end - - -- Convert the clicked coords into the block space - a_BlockX, a_BlockY, a_BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - -- Set the coords in the CommandState - GetCommandStateForPlayer(a_Player):SetCoords1(a_BlockX, a_BlockZ); - a_Player:SendMessage(string.format(g_Msgs.Coords1Set, a_BlockX, a_BlockZ)); - return true; - end; - - -- Check the player areas to see whether to disable this action - local Areas = g_PlayerAreas[a_Player:GetUniqueID()]; - if not(Areas:CanInteractWithBlock(a_BlockX, a_BlockZ)) then - a_Player:SendMessage(g_Msgs.NotAllowedToDig); - return true; - end - - -- Allow interaction - return false; -end - - - - - ---- Called by MCS when a player right-clicks -function OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_Status) - - -- BlockFace < 0 means "use item", for which the coords are not given by the client - if (a_BlockFace < 0) then - return true; - end - - -- Convert the clicked coords into the block space - a_BlockX, a_BlockY, a_BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); - - -- If the player has rclked with the wand; regardless of their permissions, let's set the coords - if (cConfig:IsWand(a_Player:GetEquippedItem())) then - -- Set the coords in the CommandState - GetCommandStateForPlayer(a_Player):SetCoords2(a_BlockX, a_BlockZ); - a_Player:SendMessage(string.format(g_Msgs.Coords2Set, a_BlockX, a_BlockZ)); - return true; - end; - - -- Check the player areas to see whether to disable this action - local Areas = g_PlayerAreas[a_Player:GetUniqueID()]; - if not(Areas:CanInteractWithBlock(a_BlockX, a_BlockZ)) then - a_Player:SendMessage(g_Msgs.NotAllowedToBuild); - return true; - end - - -- Allow interaction - return false; -end - - - - diff --git a/MCServer/Plugins/ProtectionAreas/LICENSE.txt b/MCServer/Plugins/ProtectionAreas/LICENSE.txt deleted file mode 100644 index 86c9130cc..000000000 --- a/MCServer/Plugins/ProtectionAreas/LICENSE.txt +++ /dev/null @@ -1,7 +0,0 @@ - -ProtectionAreas license -======================= - -The ProtectionAreas plugin is written by _Xoft(o) / Mattes and is hereby released as public domain. - -If you like it, I'd really appreciate a postcard, or something tiny typical from your country :) The current snailmail address is at my personal web, http://xoft.cz . diff --git a/MCServer/Plugins/ProtectionAreas/PlayerAreas.lua b/MCServer/Plugins/ProtectionAreas/PlayerAreas.lua deleted file mode 100644 index f6106ee77..000000000 --- a/MCServer/Plugins/ProtectionAreas/PlayerAreas.lua +++ /dev/null @@ -1,109 +0,0 @@ - --- PlayerAreas.lua --- Implements the cPlayerAreas class representing the per-player area storage object - ---[[ -Each player instance is expected to have a separate object of type cPlayerAreas. -Each object has an array of {cuboid, IsAllowed} tables, one for each area that is "within reach" -The code can then ask each object, whether the player can interact with a certain block or not. -A player can interact with a block if either one of these is true: -1, There are no areas covering the block -2, There is at least one area covering the block with IsAllowed set to true -The object also has a m_SafeCuboid object that specified the area within which the player may move -without the PlayerAreas needing a re-query. - -Also, a global table g_PlayerAreas is the actual map of PlayerID -> cPlayerAreas ---]] - - - - -cPlayerAreas = {}; - -g_PlayerAreas = {}; - - - - - -function cPlayerAreas:new(a_SafeMinX, a_SafeMinZ, a_SafeMaxX, a_SafeMaxZ) - assert(a_SafeMinX); - assert(a_SafeMinZ); - assert(a_SafeMaxX); - assert(a_SafeMaxZ); - - local obj = {}; - setmetatable(obj, self); - self.__index = self; - self.m_SafeCuboid = cCuboid(a_SafeMinX, 0, a_SafeMinZ, a_SafeMaxX, 255, a_SafeMaxZ); - return obj; -end - - - - --- Adds a new cuboid to the area list, where the player is either allowed or not, depending on the IsAllowed param -function cPlayerAreas:AddArea(a_Cuboid, a_IsAllowed) - table.insert(self, {m_Cuboid = a_Cuboid, m_IsAllowed = a_IsAllowed}); -end - - - - - ---- returns true if the player owning this object can interact with the specified block -function cPlayerAreas:CanInteractWithBlock(a_BlockX, a_BlockZ) - assert(self); - - -- iterate through all the stored areas: - local IsInsideAnyArea = false; - for idx, Area in ipairs(self) do - if (Area.m_Cuboid:IsInside(a_BlockX, 1, a_BlockZ)) then -- We don't care about Y coords, so use a dummy value - if (Area.m_IsAllowed) then - return true; - end - -- The coords are inside a cuboid for which the player doesn't have access, take a note of it - IsInsideAnyArea = true; - end - end - - if (IsInsideAnyArea) then - -- The specified coords are inside at least one area, but none of them allow the player to interact - return false; - end - - -- The coords are not inside any area - return cConfig.m_AllowInteractNoArea; -end - - - - - ---- Calls the specified callback for each area contained within --- a_Callback has a signature: function(a_Cuboid, a_IsAllowed) --- Returns true if all areas have been enumerated, false if the callback has aborted by returning true -function cPlayerAreas:ForEachArea(a_Callback) - assert(self); - - for idx, Area in ipairs(self) do - if (a_Callback(Area.m_Cuboid, Area.m_IsAllowed)) then - return false; - end - end - return true; -end - - - - - ---- Returns true if the player is withing the safe cuboid (no need to re-query the areas) -function cPlayerAreas:IsInSafe(a_BlockX, a_BlockZ) - assert(self); - return self.m_SafeCuboid:IsInside(a_BlockX, 0, a_BlockZ); -end - - - - diff --git a/MCServer/Plugins/ProtectionAreas/ProtectionAreas.deproj b/MCServer/Plugins/ProtectionAreas/ProtectionAreas.deproj deleted file mode 100644 index d1a2188f7..000000000 --- a/MCServer/Plugins/ProtectionAreas/ProtectionAreas.deproj +++ /dev/null @@ -1,27 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<project> - <file> - <filename>CommandHandlers.lua</filename> - </file> - <file> - <filename>CommandState.lua</filename> - </file> - <file> - <filename>Config.lua</filename> - </file> - <file> - <filename>CurrentLng.lua</filename> - </file> - <file> - <filename>HookHandlers.lua</filename> - </file> - <file> - <filename>PlayerAreas.lua</filename> - </file> - <file> - <filename>ProtectionAreas.lua</filename> - </file> - <file> - <filename>Storage.lua</filename> - </file> -</project> diff --git a/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua b/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua deleted file mode 100644 index cbe3fa94d..000000000 --- a/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua +++ /dev/null @@ -1,71 +0,0 @@ - --- ProtectionAreas.lua --- Defines the main plugin entrypoint, as well as some utility functions - - - - - ---- Prefix for all messages logged to the server console -PluginPrefix = "ProtectionAreas: "; - ---- Bounds for the area loading. Areas less this far in any direction from the player will be loaded into cPlayerAreas -g_AreaBounds = 48; - ---- If a player moves this close to the PlayerAreas bounds, the PlayerAreas will be re-queried -g_AreaSafeEdge = 12; - - - - - ---- Called by MCS when the plugin loads --- Returns true if initialization successful, false otherwise -function Initialize(a_Plugin) - a_Plugin:SetName("ProtectionAreas"); - a_Plugin:SetVersion(1); - - InitializeConfig(); - if (not(InitializeStorage())) then - LOGWARNING(PluginPrefix .. "failed to initialize Storage, plugin is disabled"); - return false; - end - InitializeHooks(a_Plugin); - InitializeCommandHandlers(); - - -- We might be reloading, so there may be players already present in the server; reload all of them - cRoot:Get():ForEachWorld( - function(a_World) - ReloadAllPlayersInWorld(a_World:GetName()); - end - ); - - return true; -end - - - - - ---- Loads a cPlayerAreas object from the DB for the player, and assigns it to the player map -function LoadPlayerAreas(a_Player) - local PlayerID = a_Player:GetUniqueID(); - local PlayerX = math.floor(a_Player:GetPosX()); - local PlayerZ = math.floor(a_Player:GetPosZ()); - local WorldName = a_Player:GetWorld():GetName(); - g_PlayerAreas[PlayerID] = g_Storage:LoadPlayerAreas(a_Player:GetName(), PlayerX, PlayerZ, WorldName); -end - - - - - -function ReloadAllPlayersInWorld(a_WorldName) - local World = cRoot:Get():GetWorld(a_WorldName); - World:ForEachPlayer(LoadPlayerAreas); -end - - - - - diff --git a/MCServer/Plugins/ProtectionAreas/Storage.lua b/MCServer/Plugins/ProtectionAreas/Storage.lua deleted file mode 100644 index a6cf564bf..000000000 --- a/MCServer/Plugins/ProtectionAreas/Storage.lua +++ /dev/null @@ -1,518 +0,0 @@ - --- Storage.lua --- Implements the storage access object, shielding the rest of the code away from the DB - ---[[ -The cStorage class is the interface to the underlying storage, the SQLite database. -This class knows how to load player areas from the DB, how to add or remove areas in the DB -and other such operations. - -Also, a g_Storage global variable is declared, it holds the single instance of the storage. ---]] - - - - - -cStorage = {}; - -g_Storage = {}; - - - - - ---- Initializes the storage subsystem, creates the g_Storage object --- Returns true if successful, false if not -function InitializeStorage() - g_Storage = cStorage:new(); - if (not(g_Storage:OpenDB())) then - return false; - end - - return true; -end - - - - - -function cStorage:new(obj) - obj = obj or {}; - setmetatable(obj, self); - self.__index = self; - return obj; -end - - - - ---- Opens the DB and makes sure it has all the columns needed --- Returns true if successful, false otherwise -function cStorage:OpenDB() - local ErrCode, ErrMsg; - self.DB, ErrCode, ErrMsg = sqlite3.open("ProtectionAreas.sqlite"); - if (self.DB == nil) then - LOGWARNING(PluginPrefix .. "Cannot open ProtectionAreas.sqlite, error " .. ErrCode .. " (" .. ErrMsg ..")"); - return false; - end - - if ( - not(self:CreateTable("Areas", {"ID INTEGER PRIMARY KEY AUTOINCREMENT", "MinX", "MaxX", "MinZ", "MaxZ", "WorldName", "CreatorUserName"})) or - not(self:CreateTable("AllowedUsers", {"AreaID", "UserName"})) - ) then - LOGWARNING(PluginPrefix .. "Cannot create DB tables!"); - return false; - end - - return true; -end - - - - - ---- Executes the SQL command given, calling the a_Callback for each result --- If the SQL command fails, prints it out on the server console and returns false --- Returns true on success -function cStorage:DBExec(a_SQL, a_Callback, a_CallbackParam) - local ErrCode = self.DB:exec(a_SQL, a_Callback, a_CallbackParam); - if (ErrCode ~= sqlite3.OK) then - LOGWARNING(PluginPrefix .. "Error " .. ErrCode .. " (" .. self.DB:errmsg() .. - ") while processing SQL command >>" .. a_SQL .. "<<" - ); - return false; - end - return true; -end - - - - - ---- Creates the table of the specified name and columns[] --- If the table exists, any columns missing are added; existing data is kept -function cStorage:CreateTable(a_TableName, a_Columns) - -- Try to create the table first - local sql = "CREATE TABLE IF NOT EXISTS '" .. a_TableName .. "' ("; - sql = sql .. table.concat(a_Columns, ", "); - sql = sql .. ")"; - if (not(self:DBExec(sql))) then - LOGWARNING(PluginPrefix .. "Cannot create DB Table " .. a_TableName); - return false; - end - -- SQLite doesn't inform us if it created the table or not, so we have to continue anyway - - -- Check each column whether it exists - -- Remove all the existing columns from a_Columns: - local RemoveExistingColumn = function(UserData, NumCols, Values, Names) - -- Remove the received column from a_Columns. Search for column name in the Names[] / Values[] pairs - for i = 1, NumCols do - if (Names[i] == "name") then - local ColumnName = Values[i]:lower(); - -- Search the a_Columns if they have that column: - for j = 1, #a_Columns do - -- Cut away all column specifiers (after the first space), if any: - local SpaceIdx = string.find(a_Columns[j], " "); - if (SpaceIdx ~= nil) then - SpaceIdx = SpaceIdx - 1; - end - local ColumnTemplate = string.lower(string.sub(a_Columns[j], 1, SpaceIdx)); - -- If it is a match, remove from a_Columns: - if (ColumnTemplate == ColumnName) then - table.remove(a_Columns, j); - break; -- for j - end - end -- for j - a_Columns[] - end - end -- for i - Names[] / Values[] - return 0; - end - if (not(self:DBExec("PRAGMA table_info(" .. a_TableName .. ")", RemoveExistingColumn))) then - LOGWARNING(PluginPrefix .. "Cannot query DB table structure"); - return false; - end - - -- Create the missing columns - -- a_Columns now contains only those columns that are missing in the DB - if (#a_Columns > 0) then - LOGINFO(PluginPrefix .. "Database table \"" .. a_TableName .. "\" is missing " .. #a_Columns .. " columns, fixing now."); - for idx, ColumnName in ipairs(a_Columns) do - if (not(self:DBExec("ALTER TABLE '" .. a_TableName .. "' ADD COLUMN " .. ColumnName))) then - LOGWARNING(PluginPrefix .. "Cannot add DB table \"" .. a_TableName .. "\" column \"" .. ColumnName .. "\""); - return false; - end - end - LOGINFO(PluginPrefix .. "Database table \"" .. a_TableName .. "\" columns fixed."); - end - - return true; -end - - - - - ---- Returns true if the specified area is allowed for the specified player -function cStorage:IsAreaAllowed(a_AreaID, a_PlayerName, a_WorldName) - assert(a_AreaID); - assert(a_PlayerName); - assert(a_WorldName); - assert(self); - - local lcPlayerName = string.lower(a_PlayerName); - local res = false; - local sql = "SELECT COUNT(*) FROM AllowedUsers WHERE (AreaID = " .. a_AreaID .. - ") AND (UserName ='" .. lcPlayerName .. "')"; - local function SetResTrue(UserData, NumValues, Values, Names) - res = (tonumber(Values[1]) > 0); - return 0; - end - if (not(self:DBExec(sql, SetResTrue))) then - LOGWARNING("SQL error while determining area allowance"); - return false; - end - return res; -end - - - - - ---- Loads cPlayerAreas for the specified player from the DB. Returns a cPlayerAreas object -function cStorage:LoadPlayerAreas(a_PlayerName, a_PlayerX, a_PlayerZ, a_WorldName) - assert(a_PlayerName); - assert(a_PlayerX); - assert(a_PlayerZ); - assert(a_WorldName); - assert(self); - - -- Bounds for which the areas are loaded - local BoundsMinX = a_PlayerX - g_AreaBounds; - local BoundsMaxX = a_PlayerX + g_AreaBounds; - local BoundsMinZ = a_PlayerZ - g_AreaBounds; - local BoundsMaxZ = a_PlayerZ + g_AreaBounds; - - local res = cPlayerAreas:new( - BoundsMinX + g_AreaSafeEdge, BoundsMinZ + g_AreaSafeEdge, - BoundsMaxX - g_AreaSafeEdge, BoundsMaxZ - g_AreaSafeEdge - ); - - --[[ - LOG("Loading protection areas for player " .. a_PlayerName .. " centered around {" .. a_PlayerX .. ", " .. a_PlayerZ .. - "}, bounds are {" .. BoundsMinX .. ", " .. BoundsMinZ .. "} - {" .. - BoundsMaxX .. ", " .. BoundsMaxZ .. "}" - ); - --]] - - -- Load the areas from the DB, based on the player's location - local lcWorldName = string.lower(a_WorldName); - local sql = - "SELECT ID, MinX, MaxX, MinZ, MaxZ FROM Areas WHERE " .. - "MinX < " .. BoundsMaxX .. " AND MaxX > " .. BoundsMinX .. " AND " .. - "MinZ < " .. BoundsMaxZ .. " AND MaxZ > " .. BoundsMinZ .. " AND " .. - "WorldName = '" .. lcWorldName .."'"; - - local function AddAreas(UserData, NumValues, Values, Names) - if ((NumValues < 5) or ((Values[1] and Values[2] and Values[3] and Values[4] and Values[5]) == nil)) then - LOGWARNING("SQL query didn't return all data"); - return 0; - end - res:AddArea(cCuboid(Values[2], 0, Values[4], Values[3], 255, Values[5]), self:IsAreaAllowed(Values[1], a_PlayerName, a_WorldName)); - return 0; - end - - if (not(self:DBExec(sql, AddAreas))) then - LOGWARNING("SQL error while querying areas"); - return res; - end - - return res; -end - - - - - ---- Adds a new area into the DB. a_AllowedNames is a table listing all the players that are allowed in the area --- Returns the ID of the new area, or -1 on failure -function cStorage:AddArea(a_Cuboid, a_WorldName, a_CreatorName, a_AllowedNames) - assert(a_Cuboid); - assert(a_WorldName); - assert(a_CreatorName); - assert(a_AllowedNames); - assert(self); - - -- Store the area in the DB - local ID = -1; - local function RememberID(UserData, NumCols, Values, Names) - for i = 1, NumCols do - if (Names[i] == "ID") then - ID = Values[i]; - end - end - return 0; - end - local lcWorldName = string.lower(a_WorldName); - local lcCreatorName = string.lower(a_CreatorName); - local sql = - "INSERT INTO Areas (ID, MinX, MaxX, MinZ, MaxZ, WorldName, CreatorUserName) VALUES (NULL, " .. - a_Cuboid.p1.x .. ", " .. a_Cuboid.p2.x .. ", " .. a_Cuboid.p1.z .. ", " .. a_Cuboid.p2.z .. - ", '" .. lcWorldName .. "', '" .. lcCreatorName .. - "'); SELECT last_insert_rowid() AS ID"; - if (not(self:DBExec(sql, RememberID))) then - LOGWARNING(PluginPrefix .. "SQL Error while inserting new area"); - return -1; - end - if (ID == -1) then - LOGWARNING(PluginPrefix .. "SQL Error while retrieving INSERTion ID"); - return -1; - end - - -- Store each allowed player in the DB - for idx, Name in ipairs(a_AllowedNames) do - local lcName = string.lower(Name); - local sql = "INSERT INTO AllowedUsers (AreaID, UserName) VALUES (" .. ID .. ", '" .. lcName .. "')"; - if (not(self:DBExec(sql))) then - LOGWARNING(PluginPrefix .. "SQL Error while inserting new area's allowed player " .. Name); - end - end - return ID; -end - - - - - -function cStorage:DelArea(a_WorldName, a_AreaID) - assert(a_WorldName); - assert(a_AreaID); - assert(self); - - -- Since all areas are stored in a single DB (for now), the worldname parameter isn't used at all - -- Later if we change to a per-world DB, we'll need the world name - - -- Delete from both tables simultaneously - local sql = - "DELETE FROM Areas WHERE ID = " .. a_AreaID .. ";" .. - "DELETE FROM AllowedUsers WHERE AreaID = " .. a_AreaID; - if (not(self:DBExec(sql))) then - LOGWARNING(PluginPrefix .. "SQL error while deleting area " .. a_AreaID .. " from world \"" .. a_WorldName .. "\""); - return false; - end - - return true; -end - - - - - ---- Removes the user from the specified area -function cStorage:RemoveUser(a_AreaID, a_UserName, a_WorldName) - assert(a_AreaID); - assert(a_UserName); - assert(a_WorldName); - assert(self); - - -- WorldName is not used yet, because all the worlds share the same DB in this version - - local lcUserName = string.lower(a_UserName); - local sql = "DELETE FROM AllowedUsers WHERE " .. - "AreaID = " .. a_AreaID .. " AND UserName = '" .. lcUserName .. "'"; - if (not(self:DBExec(sql))) then - LOGWARNING("SQL error while removing user " .. a_UserName .. " from area ID " .. a_AreaID); - return false; - end - return true; -end - - - - - ---- Removes the user from all areas in the specified world -function cStorage:RemoveUserAll(a_UserName, a_WorldName) - assert(a_UserName); - assert(a_WorldName); - assert(self); - - local lcUserName = string.lower(a_UserName); - local sql = "DELETE FROM AllowedUsers WHERE UserName = '" .. lcUserName .."'"; - if (not(self:DBExec(sql))) then - LOGWARNING("SQL error while removing user " .. a_UserName .. " from all areas"); - return false; - end - return true; -end - - - - - ---- Calls the callback for each area intersecting the specified coords --- Callback signature: function(ID, MinX, MinZ, MaxX, MaxZ, CreatorName) -function cStorage:ForEachArea(a_BlockX, a_BlockZ, a_WorldName, a_Callback) - assert(a_BlockX); - assert(a_BlockZ); - assert(a_WorldName); - assert(a_Callback); - assert(self); - - -- SQL callback that parses the values and calls our callback - function CallCallback(UserData, NumValues, Values, Names) - if (NumValues ~= 6) then - -- Not enough values returned, skip this row - return 0; - end - local ID = Values[1]; - local MinX = Values[2]; - local MinZ = Values[3]; - local MaxX = Values[4]; - local MaxZ = Values[5]; - local CreatorName = Values[6]; - a_Callback(ID, MinX, MinZ, MaxX, MaxZ, CreatorName); - return 0; - end - - local lcWorldName = string.lower(a_WorldName); - local sql = "SELECT ID, MinX, MinZ, MaxX, MaxZ, CreatorUserName FROM Areas WHERE " .. - "MinX <= " .. a_BlockX .. " AND MaxX >= " .. a_BlockX .. " AND " .. - "MinZ <= " .. a_BlockZ .. " AND MaxZ >= " .. a_BlockZ .. " AND " .. - "WorldName = '" .. lcWorldName .. "'"; - if (not(self:DBExec(sql, CallCallback))) then - LOGWARNING("SQL Error while iterating through areas (cStorage:ForEachArea())"); - return false; - end - return true; -end - - - - - ---- Returns the info on the specified area --- Returns MinX, MinZ, MaxX, MaxZ, CreatorName on success, or nothing on failure -function cStorage:GetArea(a_AreaID, a_WorldName) - assert(a_AreaID); - assert(a_WorldName); - assert(self); - - local MinX, MinZ, MaxX, MaxZ, CreatorName; - local HasValues = false; - - -- SQL callback that parses the values and remembers them in variables - function RememberValues(UserData, NumValues, Values, Names) - if (NumValues ~= 5) then - -- Not enough values returned, skip this row - return 0; - end - MinX = Values[1]; - MinZ = Values[2]; - MaxX = Values[3]; - MaxZ = Values[4]; - CreatorName = Values[5]; - HasValues = true; - return 0; - end - - local lcWorldName = string.lower(a_WorldName); - local sql = "SELECT MinX, MinZ, MaxX, MaxZ, CreatorUserName FROM Areas WHERE " .. - "ID = " .. a_AreaID .. " AND WorldName = '" .. lcWorldName .. "'"; - if (not(self:DBExec(sql, RememberValues))) then - LOGWARNING("SQL Error while getting area info (cStorage:ForEachArea())"); - return; - end - - -- If no data has been retrieved, return nothing - if (not(HasValues)) then - return; - end - - return MinX, MinZ, MaxX, MaxZ, CreatorName; -end - - - - - ---- Calls the callback for each allowed user for the specified area --- Callback signature: function(UserName) -function cStorage:ForEachUserInArea(a_AreaID, a_WorldName, a_Callback) - assert(a_AreaID); - assert(a_WorldName); - assert(a_Callback); - assert(self); - - -- Since in this version all the worlds share a single DB, the a_WorldName parameter is not actually used - -- But this may change in the future, when we have a per-world DB - - local function CallCallback(UserData, NumValues, Values) - if (NumValues ~= 1) then - return 0; - end - a_Callback(Values[1]); - return 0; - end - local sql = "SELECT UserName FROM AllowedUsers WHERE AreaID = " .. a_AreaID; - if (not(self:DBExec(sql, CallCallback))) then - LOGWARNING("SQL error while iterating area users for AreaID" .. a_AreaID); - return false; - end - return true; -end - - - - - ---- Adds the specified usernames to the specified area, if not already present --- a_Users is an array table of usernames to add -function cStorage:AddAreaUsers(a_AreaID, a_WorldName, a_AddedBy, a_Users) - assert(a_AreaID); - assert(a_WorldName); - assert(a_Users); - assert(self); - - -- Convert all usernames to lowercase - for idx, Name in ipairs(a_Users) do - a_Users[idx] = string.lower(Name); - end - - -- Remove from a_Users the usernames already present in the area - local sql = "SELECT UserName FROM AllowedUsers WHERE AreaID = " .. a_AreaID; - local function RemovePresent(UserData, NumValues, Values, Names) - if (NumValues ~= 1) then - -- Invalid response format - return 0; - end - local DBName = Values[1]; - -- Remove the name from a_Users, if exists - for idx, Name in ipairs(a_Users) do - if (Name == DBName) then - table.remove(a_Users, idx); - return 0; - end - end - return 0; - end - if (not(self:DBExec(sql, RemovePresent))) then - LOGWARNING("SQL error while iterating through users"); - return false; - end - - -- Add the users - for idx, Name in ipairs(a_Users) do - local sql = "INSERT INTO AllowedUsers (AreaID, UserName) VALUES (" .. a_AreaID .. ", '" .. Name .. "')"; - if (not(self:DBExec(sql))) then - LOGWARNING("SQL error while adding user " .. Name .. " to area " .. a_AreaID); - end - end - - return true; -end - - - - - |