summaryrefslogtreecommitdiffstats
path: root/MCServer/Plugins/ProtectionAreas
diff options
context:
space:
mode:
Diffstat (limited to 'MCServer/Plugins/ProtectionAreas')
-rw-r--r--MCServer/Plugins/ProtectionAreas/CommandHandlers.lua644
-rw-r--r--MCServer/Plugins/ProtectionAreas/CommandState.lua242
-rw-r--r--MCServer/Plugins/ProtectionAreas/Config.lua108
-rw-r--r--MCServer/Plugins/ProtectionAreas/CurrentLng.lua152
-rw-r--r--MCServer/Plugins/ProtectionAreas/HookHandlers.lua278
-rw-r--r--MCServer/Plugins/ProtectionAreas/LICENSE.txt14
-rw-r--r--MCServer/Plugins/ProtectionAreas/PlayerAreas.lua218
-rw-r--r--MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua142
-rw-r--r--MCServer/Plugins/ProtectionAreas/Storage.lua1036
9 files changed, 1417 insertions, 1417 deletions
diff --git a/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua b/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua
index b943df060..26df73075 100644
--- a/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua
+++ b/MCServer/Plugins/ProtectionAreas/CommandHandlers.lua
@@ -1,322 +1,322 @@
-
--- 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
-
-
-
-
-
+
+-- 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
index 2911688a8..f6d33d356 100644
--- a/MCServer/Plugins/ProtectionAreas/CommandState.lua
+++ b/MCServer/Plugins/ProtectionAreas/CommandState.lua
@@ -1,121 +1,121 @@
-
--- 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;
-
-
-
-
+
+-- 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
index b6fe34535..b40be0c75 100644
--- a/MCServer/Plugins/ProtectionAreas/Config.lua
+++ b/MCServer/Plugins/ProtectionAreas/Config.lua
@@ -1,55 +1,55 @@
-
--- 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;
+
+-- 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
index b0ad3863c..37ff135c5 100644
--- a/MCServer/Plugins/ProtectionAreas/CurrentLng.lua
+++ b/MCServer/Plugins/ProtectionAreas/CurrentLng.lua
@@ -1,76 +1,76 @@
-
--- 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";
-} ;
-
-
-
-
-
+
+-- 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
index 18fd4fa03..ded64d298 100644
--- a/MCServer/Plugins/ProtectionAreas/HookHandlers.lua
+++ b/MCServer/Plugins/ProtectionAreas/HookHandlers.lua
@@ -1,139 +1,139 @@
-
--- 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
-
-
-
-
+
+-- 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
index c46949f66..86c9130cc 100644
--- a/MCServer/Plugins/ProtectionAreas/LICENSE.txt
+++ b/MCServer/Plugins/ProtectionAreas/LICENSE.txt
@@ -1,7 +1,7 @@
-
-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 .
+
+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
index 5627f4d5f..f6106ee77 100644
--- a/MCServer/Plugins/ProtectionAreas/PlayerAreas.lua
+++ b/MCServer/Plugins/ProtectionAreas/PlayerAreas.lua
@@ -1,109 +1,109 @@
-
--- 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
-
-
-
-
+
+-- 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.lua b/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua
index 8c2db1387..cbe3fa94d 100644
--- a/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua
+++ b/MCServer/Plugins/ProtectionAreas/ProtectionAreas.lua
@@ -1,71 +1,71 @@
-
--- 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
-
-
-
-
-
+
+-- 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
index 07825b172..a6cf564bf 100644
--- a/MCServer/Plugins/ProtectionAreas/Storage.lua
+++ b/MCServer/Plugins/ProtectionAreas/Storage.lua
@@ -1,518 +1,518 @@
-
--- 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
-
-
-
-
-
+
+-- 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
+
+
+
+
+