ProtectionAreas: Implemented all command handlers.

They still call unimplemented methods in the DB, and haven't been properly tested

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1561 0a769ca7-a7f5-676a-18bf-c427514a06d6
master
madmaxoft@gmail.com 2013-06-07 16:28:37 +00:00
parent 6cb76856ff
commit 9790a6817c
6 changed files with 335 additions and 84 deletions

View File

@ -33,15 +33,6 @@ function HandleAddArea(a_Split, a_Player)
end
local Cuboid = CmdState:GetCurrentCuboid();
if (Cuboid == nil) then
a_Player:SendMessage("Cannot add area, internal plugin error (Cuboid == nil)");
return true;
end
-- If the cuboid hasn't been assigned, give the player an error message and bail out
if (
(Cuboid.p1.x == 0) and (Cuboid.p1.y == 0) and (Cuboid.p1.z == 0) and
(Cuboid.p1.x == 0) and (Cuboid.p1.y == 0) and (Cuboid.p1.z == 0)
) then
a_Player:SendMessage("Cannot add area, no area has been selected. Use a ProtWand lclk / rclk to select area first");
return true;
end
@ -53,10 +44,11 @@ function HandleAddArea(a_Split, a_Player)
end
-- Add the area to the storage
g_Storage:AddArea(Cuboid, a_Player:GetName(), AllowedNames);
g_Storage:AddArea(Cuboid, a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames);
a_Player:SendMessage("Area added");
-- TODO: Reload all currently logged in players
-- Reload all currently logged in players
ReloadAllPlayersInWorld(a_Player:GetWorld():GetName());
return true;
end
@ -66,7 +58,36 @@ end
function HandleAddAreaCoords(a_Split, a_Player)
-- TODO
-- Command syntax: ProtAddCoords x1 z1 x2 z2 username1 [username2] [username3] ...
if (#a_Split < 6) then
a_Player:SendMessage("Not enough parameters. Expected <x1> <z1> <x2> <z2> coords and a list of usernames.");
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("Cannot parse coords.");
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
g_Storage:AddArea(Cuboid, a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames);
a_Player:SendMessage("Area added");
-- Reload all currently logged in players
ReloadAllPlayersInWorld(a_Player:GetWorld():GetName());
return true;
end
@ -74,7 +95,26 @@ end
function HandleAddAreaUser(a_Split, a_Player)
-- TODO
-- Command syntax: ProtAddUser AreaID username1 [username2] [username3] ...
if (#a_Split < 3) then
a_Player:SendMessage("Not enough parameters. Expected <AreaID> and a list of usernames.");
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
g_Storage:AddAreaUsers(tonumber(a_Split[2]), a_Player:GetWorld():GetName(), a_Player:GetName(), AllowedNames);
a_Player:SendMessage("Users added: " .. table.concat(AllowedNames, ", "));
-- Reload all currently logged in players
ReloadAllPlayersInWorld(a_Player:GetWorld():GetName());
return true;
end
@ -82,7 +122,26 @@ end
function HandleDelArea(a_Split, a_Player)
-- TODO
-- Command syntax: ProtDelArea AreaID
if (#a_Split ~= 2) then
a_Player:SendMessage("Parameter mismatch. Expected <AreaID>.");
return true;
end
-- Parse the AreaID
local AreaID = tonumber(a_Split[2]);
if (AreaID == nil) then
a_Player:SendMessage("Cannot parse <AreaID>.");
return true;
end
-- Delete the area
g_Storage:DelArea(a_Player:GetWorld():GetName(), AreaID);
-- Reload all currently logged in players
ReloadAllPlayersInWorld(a_Player:GetWorld():GetName());
return true;
end
@ -104,7 +163,61 @@ end
function HandleListAreas(a_Split, a_Player)
-- TODO
-- 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("Cannot list area, internal plugin error (CmdState == nil)");
return true;
end
x, z = CmdState:GetLastCoords();
if ((x == nil) or (z == nil)) then
a_Player:SendMessage("Cannot list areas, no query point has been selected. Use a ProtWand lclk / rclk to select a point first");
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("Cannot list areas, cannot parse coords in params");
return true;
end
else
-- Wrong number of params, report back to the user
a_Player:SendMessage("Cannot list areas, syntax error. Expected either no params or <x> <z>.");
return true;
end
a_Player:SendMessage("Listing protection areas intersecting block column {" .. x .. ", " .. z .. "}:");
-- List areas intersecting the coords
local Areas = g_PlayerAreas[a_Player:GetUniqueID()]
Areas:ForEachArea(
function(a_Cuboid, a_IsAllowed)
if (not(a_Cuboid:IsInside(x, 1, z))) then
-- This cuboid doesn't intersect the column
return;
end
-- Column intersected, send to the player
local Coords = "{" ..
a_Cuboid.p1.x .. ", " .. a_Cuboid.p1.z .. "} - {" ..
a_Cuboid.p2.x .. ", " .. a_Cuboid.p2.z .. "} ";
local Allowance;
if (a_IsAllowed) then
Allowance = "Allowed";
else
Allowance = "NOT allowed";
end
a_Player:SendMessage(" " .. Coords .. Allowance);
end
);
a_Player:SendMessage("Area list finished");
return true;
end
@ -112,7 +225,22 @@ end
function HandleRemoveUser(a_Split, a_Player)
-- TODO
-- Command syntax: ProtRemUser AreaID UserName
if (#a_Split ~= 3) then
a_Player:SendMessage("Parameter mismatch. Expected <AreaID> <UserName>.");
return true;
end
-- Parse the AreaID
local AreaID = tonumber(a_Split[2]);
if (AreaID == nil) then
a_Player:SendMessage("Cannot parse <AreaID>.");
return true;
end
-- Remove the user from the DB
g_Storage:RemoveUser(AreaID, a_Split[3], a_Player:GetWorld():GetName());
return true;
end
@ -120,7 +248,15 @@ end
function HandleRemoveUserAll(a_Split, a_Player)
-- TODO
-- Command syntax: ProtRemUserAll UserName
if (#a_Split ~= 2) then
a_Player:SendMessage("Parameter mismatch. Expected <UserName>.");
return true;
end
-- Remove the user from the DB
g_Storage.RemoveUserAll(a_Split[2], a_Player:GetWorld():GetName());
return true;
end

View File

@ -18,8 +18,10 @@ Also, a global table g_CommandStates is the map of PlayerEntityID -> cCommandSta
cCommandState = {
-- Default coords
m_Coords1 = {x = 0, y = 0, z = 0};
m_Coords2 = {x = 0, y = 0, z = 0};
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_HasCoords = 1; -- Set to 0, 1, 2 or 3, based on which set of coords has been set (bitmask)
};
g_CommandStates = {};
@ -41,6 +43,11 @@ end
--- Returns the current coord pair as a cCuboid object
function cCommandState:GetCurrentCuboid()
if (self.m_HasCoords ~= 3) then
-- Some of the coords haven't been set yet
return nil;
end
local res = cCuboid(
self.m_Coords1.x, self.m_Coords1.y, self.m_Coords1.z,
self.m_Coords2.x, self.m_Coords2.y, self.m_Coords2.z
@ -52,11 +59,33 @@ 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_BlockY, a_BlockZ)
function cCommandState:SetCoords1(a_BlockX, a_BlockZ)
self.m_Coords1.x = a_BlockX;
self.m_Coords1.y = a_BlockY;
self.m_Coords1.z = a_BlockZ;
self.m_LastCoords = 1;
end
@ -64,10 +93,10 @@ end
--- Sets the second set of coords (upon lclk with a wand)
function cCommandState:SetCoords2(a_BlockX, a_BlockY, a_BlockZ)
function cCommandState:SetCoords2(a_BlockX, a_BlockZ)
self.m_Coords2.x = a_BlockX;
self.m_Coords2.y = a_BlockY;
self.m_Coords2.z = a_BlockZ;
self.m_LastCoords = 2;
end

View File

@ -35,10 +35,8 @@ end;
function OnPlayerJoined(a_Player)
-- Create a new cPlayerAreas object for this player
local PlayerName = a_Player:GetName();
local PlayerID = a_Player:GetUniqueID();
if (g_PlayerAreas[PlayerID] == nil) then
g_PlayerAreas[PlayerID] = g_Storage:LoadPlayerAreas(PlayerName);
if (g_PlayerAreas[a_Player:GetUniqueID()] == nil) then
LoadPlayerAreas(a_Player);
end;
return false;
@ -60,8 +58,8 @@ function OnPlayerLeftClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
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_BlockY, a_BlockZ);
a_Player:SendMessage("Coords1 set as {" .. a_BlockX .. ", " .. a_BlockY .. ", " .. a_BlockZ .."}.");
GetCommandStateForPlayer(a_Player):SetCoords1(a_BlockX, a_BlockZ);
a_Player:SendMessage("Coords1 set as {" .. a_BlockX .. ", " .. a_BlockZ .."}.");
return true;
end;
@ -91,14 +89,14 @@ function OnPlayerRightClick(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
a_BlockX, a_BlockY, a_BlockZ = AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
-- Set the coords in the CommandState
GetCommandStateForPlayer(a_Player):SetCoords2(a_BlockX, a_BlockY, a_BlockZ);
a_Player:SendMessage("Coords2 set as {" .. a_BlockX .. ", " .. a_BlockY .. ", " .. a_BlockZ .."}.");
GetCommandStateForPlayer(a_Player):SetCoords2(a_BlockX, a_BlockZ);
a_Player:SendMessage("Coords2 set as {" .. 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_BlockY, a_BlockZ)) then
if not(Areas:CanInteractWithBlock(a_BlockX, a_BlockZ)) then
return true;
end

View File

@ -37,7 +37,7 @@ 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, {Cuboid = a_Cuboid, IsAllowed = a_IsAllowed});
table.insert(self, {m_Cuboid = a_Cuboid, m_IsAllowed = a_IsAllowed});
end
@ -45,12 +45,12 @@ end
--- returns true if the player owning this object can interact with the specified block
function cPlayerAreas:CanInteractWithBlock(a_BlockX, a_BlockY, a_BlockZ)
function cPlayerAreas:CanInteractWithBlock(a_BlockX, a_BlockZ)
-- iterate through all the stored areas:
local IsInsideAnyArea = false;
for idx, Area in ipairs(self) do
if (Area.Cuboid:IsInside(a_BlockX, a_BlockY, a_BlockZ)) then
if (Area.IsAllowed) then
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
@ -71,3 +71,19 @@ 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)
for idx, Area in ipairs(self) do
if (a_Callback(Area.m_Cuboid, Area.m_IsAllowed)) then
return false;
end
end
return true;
end

View File

@ -1,6 +1,6 @@
-- ProtectionAreas.lua
-- Defines the main plugin entrypoint
-- Defines the main plugin entrypoint, as well as some utility functions
@ -26,8 +26,38 @@ function Initialize(a_Plugin)
InitializeHooks(a_Plugin);
InitializeCommandHandlers();
-- TODO: We might be reloading, so there may be players already present in the server
-- Reload areas for all present players
-- We might be reloading, so there may be players already present in the server; reload all of them
local function ReloadPlayers(a_World)
ReloadAllPlayersInWorld(a_World:GetName());
end
cRoot:Get():ForEachWorld(ReloadPlayers);
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

View File

@ -47,20 +47,6 @@ end
--- Loads cPlayerAreas for the specified player from the DB. Returns a cPlayerAreas object
function cStorage:LoadPlayerAreas(PlayerName)
local res = cPlayerAreas:new();
-- TODO: Load the areas from the DB, based on the player's location
-- DEBUG: Insert a dummy area for testing purposes:
res:AddArea(cCuboid(10, 0, 10, 20, 255, 20), false);
return res;
end
--- Opens the DB and makes sure it has all the columns needed
-- Returns true if successful, false otherwise
function cStorage:OpenDB()
@ -72,7 +58,7 @@ function cStorage:OpenDB()
end
if (
not(self:CreateTable("Areas", {"ID INTEGER PRIMARY KEY AUTOINCREMENT", "MinX", "MaxX", "MinZ", "MaxZ", "CreatorUserName"})) or
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!");
@ -86,6 +72,24 @@ 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)
@ -93,9 +97,8 @@ function cStorage:CreateTable(a_TableName, a_Columns)
local sql = "CREATE TABLE IF NOT EXISTS '" .. a_TableName .. "' (";
sql = sql .. table.concat(a_Columns, ", ");
sql = sql .. ")";
local ErrCode = self.DB:exec(sql);
if (ErrCode ~= sqlite3.OK) then
LOGWARNING(PluginPrefix .. "Cannot create DB Table, error " .. ErrCode .. " (" .. self.DB:errmsg() .. ")");
if (not(self:DBExec(sql))) then
LOGWARNING(PluginPrefix .. "Cannot create DB Table " .. a_TableName);
return false;
end
@ -124,9 +127,8 @@ function cStorage:CreateTable(a_TableName, a_Columns)
end -- for i - Names[] / Values[]
return 0;
end
local ErrCode = self.DB:exec("PRAGMA table_info(" .. a_TableName .. ")", RemoveExistingColumn);
if (ErrCode ~= sqlite3.OK) then
LOGWARNING(PluginPrefix .. "Cannot query DB table structure, error " .. ErrCode .. " (" .. self.DB:errmsg() ..")");
if (not(self:DBExec("PRAGMA table_info(" .. a_TableName .. ")", RemoveExistingColumn))) then
LOGWARNING(PluginPrefix .. "Cannot query DB table structure");
return false;
end
@ -135,9 +137,8 @@ function cStorage:CreateTable(a_TableName, a_Columns)
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
local ErrCode = self.DB:exec("ALTER TABLE '" .. a_TableName .. "' ADD COLUMN " .. ColumnName);
if (ErrCode ~= sqlite3.OK) then
LOGWARNING(PluginPrefix .. "Cannot add DB table \"" .. a_TableName .. "\" column \"" .. ColumnName .. "\", error " .. ErrCode .. " (" .. self.DB:errmsg() ..")");
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
@ -151,8 +152,26 @@ 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)
local res = cPlayerAreas:new();
-- TODO: Load the areas from the DB, based on the player's location
-- local sql = "SELECT MinX, MaxX, MinZ, MaxZ FROM Areas WHERE";
LOGWARNING("cStorage:LoadPlayerAreas(): Not implemented yet!");
-- DEBUG: Insert a dummy area for testing purposes:
res:AddArea(cCuboid(10, 0, 10, 20, 255, 20), false);
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
function cStorage:AddArea(a_Cuboid, a_CreatorName, a_AllowedNames)
function cStorage:AddArea(a_Cuboid, a_WorldName, a_CreatorName, a_AllowedNames)
-- Store the area in the DB
local ID = -1;
local function RememberID(UserData, NumCols, Values, Names)
@ -164,8 +183,9 @@ function cStorage:AddArea(a_Cuboid, a_CreatorName, a_AllowedNames)
return 0;
end
local sql =
"INSERT INTO Areas (ID, MinX, MaxX, MinZ, MaxZ, CreatorUserName) VALUES (NULL, " ..
a_Cuboid.p1.x .. ", " .. a_Cuboid.p2.x .. ", " .. a_Cuboid.p1.z .. ", " .. a_Cuboid.p2.z .. ", '" .. a_CreatorName ..
"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 ..
", '" .. a_WorldName .. "', '" .. a_CreatorName ..
"'); SELECT last_insert_rowid() as ID";
if (not(self:DBExec(sql, RememberID))) then
LOGWARNING(PluginPrefix .. "SQL Error while inserting new area");
@ -181,24 +201,7 @@ function cStorage:AddArea(a_Cuboid, a_CreatorName, a_AllowedNames)
local sql = "INSERT INTO AllowedUsers (AreaID, UserName) VALUES (" .. ID .. ", '" .. Name .. "')";
if (not(self:DBExec(sql))) then
LOGWARNING(PluginPrefix .. "SQL Error while inserting new area's allowed player " .. Name);
end;
end
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
end
return true;
end
@ -207,3 +210,42 @@ end
function cStorage:DelArea(a_WorldName, a_AreaID)
-- 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 AllowedPlayers 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)
-- TODO
LOGWARNING("cStorage:RemoveUser(): Not implemented yet!");
end
--- Removes the user from all areas in the specified world
function cStorage:RemoveUserAll(a_UserName, a_WorldName)
-- TODO
LOGWARNING("cStorage:RemoveUserAll(): Not implemented yet!");
end