diff --git a/Info.lua b/Info.lua index 25223b7..c196d95 100644 --- a/Info.lua +++ b/Info.lua @@ -213,6 +213,13 @@ g_PluginInfo = HelpString = "Shows the message of the day.", }, + ["/numchunks"] = + { + Permission = "core.numchunks", + Handler = HandleNumChunksCommand, + HelpString = "Shows number of chunks currently loaded.", + }, + ["/plugins"] = { Alias = "/pl", @@ -230,6 +237,7 @@ g_PluginInfo = ["/portal"] = { + Alias = "/world", Permission = "core.portal", Handler = HandlePortalCommand, HelpString = "Moves your player to a different world.", @@ -256,6 +264,13 @@ g_PluginInfo = HelpString = "Add a player to the administrator rank.", }, + ["/players"] = + { + Permission = "core.players", + Handler = HandlePlayersCommand, + HelpString = "Shows a list of all connected players.", + }, + ["/regen"] = { Permission = "core.regen", @@ -492,6 +507,13 @@ g_PluginInfo = HelpString = "Unbans a player.", }, + ["/unloadchunks"] = + { + Permission = "core.unloadchunks", + Handler = HandleUnloadChunksCommand, + HelpString = "Unloads all unused chunks.", + }, + ["/unrank"] = { Permission = "core.unrank", @@ -828,6 +850,18 @@ g_PluginInfo = HelpString = "Add a player to the Admin rank.", }, + ["msg"] = + { + Handler = HandleConsoleTell, + HelpString = "Sends a private message to a player.", + }, + + ["r"] = + { + Handler = HandleConsoleR, + HelpString = "Replies to the latest private message you received.", + }, + ["ranks"] = { Handler = HandleConsoleListRanks, @@ -852,6 +886,12 @@ g_PluginInfo = }, }, -- regen + ["tell"] = + { + Handler = HandleConsoleTell, + HelpString = "Sends a private message to a player.", + }, + ["save-all"] = { Handler = HandleConsoleSaveAll, @@ -882,6 +922,12 @@ g_PluginInfo = HelpString = "Returns a player to the spawn point.", }, + ["summon"] = + { + Handler = HandleConsoleSummon, + HelpString = "Summons an entity in the world.", + }, + ["time"] = { HelpString = "Sets or displays the time.", @@ -1031,7 +1077,7 @@ g_PluginInfo = ["unloadchunks"] = { - Handler = HandleConsoleUnload, + Handler = HandleConsoleUnloadChunks, HelpString = "Unloads all unused chunks.", }, @@ -1068,7 +1114,7 @@ g_PluginInfo = ["weather"] = { - Handler = HandleConsoleWeather, + Handler = HandleConsoleWeather, HelpString = "Changes the world's weather.", ParameterCombinations = { @@ -1137,6 +1183,12 @@ g_PluginInfo = }, }, -- Subcommands }, -- whitelist + + ["worlds"] = + { + Handler = HandleConsoleWorlds, + HelpString = "Shows a list of all the worlds.", + }, }, -- ConsoleCommands Permissions = { diff --git a/cmd_numchunks.lua b/cmd_numchunks.lua new file mode 100644 index 0000000..819a1ab --- /dev/null +++ b/cmd_numchunks.lua @@ -0,0 +1,24 @@ +function HandleNumChunksCommand(Split, Player) + -- List each world's chunk count into a table, sum the total chunk count: + local Response = {} + local Total = 0 + + local GetWorldChunks = function(World) + local numchunks = World:GetNumChunks() + table.insert(Response, World:GetName() .. ": " .. numchunks .. " chunks") + Total = Total + numchunks + end + + table.insert(Response, "Number of loaded chunks:") + cRoot:Get():ForEachWorld(GetWorldChunks) + table.sort(Response) + + table.insert(Response, "Total: " .. Total .. " chunks") + + -- Return the complete report: + return true, SendMessage(Player, table.concat(Response, "\n")) +end + +function HandleConsoleNumChunks(Split) + return HandleNumChunksCommand(Split) +end diff --git a/cmd_op.lua b/cmd_op.lua index c89e535..43d61df 100644 --- a/cmd_op.lua +++ b/cmd_op.lua @@ -1,3 +1,15 @@ +local function GetAdminRankName() + local Ranks = cRankManager:GetAllRanks() + for _, Rank in ipairs(Ranks) do + local Permissions = cRankManager:GetRankPermissions(Rank) + for _, Permission in ipairs(Permissions) do + if Permission == "*" then + return Rank + end + end + end +end + function HandleOpCommand(Split, Player) local Response @@ -5,12 +17,12 @@ function HandleOpCommand(Split, Player) Response = SendMessage(Player, "Usage: " .. Split[1] .. " ") else local PlayerName = Split[2] - local AdminRank = GetAdminRank() + local AdminRankName = GetAdminRankName() - if not AdminRank then + if not AdminRankName then Response = SendMessage(Player, "No admin rank found, missing * permission") else - return HandleRankCommand({"rank", PlayerName, AdminRank}, Player) + return HandleRankCommand({"rank", PlayerName, AdminRankName}, Player) end end return true, Response diff --git a/cmd_players.lua b/cmd_players.lua new file mode 100644 index 0000000..6640faf --- /dev/null +++ b/cmd_players.lua @@ -0,0 +1,21 @@ +function HandlePlayersCommand(Split, Player) + local Response = {} + + local AddToResponse = function(OtherPlayer) + table.insert(Response, " " .. OtherPlayer:GetName() .. " @ " .. OtherPlayer:GetIP()) + end + + local ForEachPlayer = function(World) + table.insert(Response, "World " .. World:GetName() .. ":") + World:ForEachPlayer(AddToResponse) + end + + table.insert(Response, "Players online:") + cRoot:Get():ForEachWorld(ForEachPlayer) + + return true, SendMessage(Player, table.concat(Response, "\n")) +end + +function HandleConsolePlayers(Split) + return HandlePlayersCommand(Split) +end diff --git a/cmd_portal.lua b/cmd_portal.lua new file mode 100644 index 0000000..58ef64c --- /dev/null +++ b/cmd_portal.lua @@ -0,0 +1,19 @@ +function HandlePortalCommand(Split, Player) + local WorldName = Split[2] + + if Split[3] then + SendMessage(Player, "Usage: " .. Split[1] .. " [world]") + elseif not WorldName then + SendMessage(Player, "You are in world \"" .. Player:GetWorld():GetName() .. "\"") + elseif Player:GetWorld():GetName() == WorldName then + SendMessageFailure(Player, "You are already in world \"" .. Split[2] .. "\"!") + elseif not cRoot:Get():GetWorld(WorldName) then + SendMessageFailure(Player, "Could not find world \"" .. Split[2] .. "\"!") + elseif not Player:MoveToWorld(WorldName) then + SendMessageFailure(Player, "Could not move to world \"" .. Split[2] .. "\"!") + else + SendMessageSuccess(Player, "Successfully moved to world \"" .. Split[2] .. "\"! :D") + end + + return true +end diff --git a/summon.lua b/cmd_summon.lua similarity index 80% rename from summon.lua rename to cmd_summon.lua index 6d0eeaf..b59da95 100644 --- a/summon.lua +++ b/cmd_summon.lua @@ -44,6 +44,7 @@ local Mobs = ["villager"] = mtVillager, ["witch"] = mtWitch, ["wither"] = mtWither, + ["wither_skeleton"] = mtWitherSkeleton, ["wolf"] = mtWolf, ["zombie"] = mtZombie, ["zombie_pigman"] = mtZombiePigman, @@ -78,6 +79,7 @@ local Mobs = ["VillagerGolem"] = mtIronGolem, ["Witch"] = mtWitch, ["Wither"] = mtWither, + ["WitherSkeleton"] = mtWitherSkeleton, ["Wolf"] = mtWolf, ["Zombie"] = mtZombie, ["PigZombie"] = mtZombiePigman, @@ -141,46 +143,46 @@ local function SpawnEntity(EntityName, World, X, Y, Z, Player) end function HandleSummonCommand(Split, Player) - if not Split[2] then - Player:SendMessageInfo("Usage: " .. Split[1] .. " [x] [y] [z]") - else - local X = Player:GetPosX() - local Y = Player:GetPosY() - local Z = Player:GetPosZ() - local World = Player:GetWorld() - - if Split[3] then - X = RelativeCommandCoord(Split[3], X) - end + local Response - if Split[4] then - Y = RelativeCommandCoord(Split[4], Y) + if not Split[2] or (not Player and not Split[5]) then + Response = SendMessage(Player, "Usage: " .. Split[1] .. " [x] [y] [z]") + else + local X + local Y + local Z + local World = cRoot:Get():GetDefaultWorld() + + if Player then + X = Player:GetPosX() + Y = Player:GetPosY() + Z = Player:GetPosZ() + World = Player:GetWorld() end if Split[5] then + X = RelativeCommandCoord(Split[3], X) + Y = RelativeCommandCoord(Split[4], Y) Z = RelativeCommandCoord(Split[5], Z) - end - - if not X then - Player:SendMessageFailure("'" .. Split[3] .. "' is not a valid number") - return true - end - - if not Y then - Player:SendMessageFailure("'" .. Split[4] .. "' is not a valid number") - return true - end - if not Z then - Player:SendMessageFailure("'" .. Split[5] .. "' is not a valid number") - return true + if not X then + return true, SendMessageFailure(Player, "'" .. Split[3] .. "' is not a valid number") + elseif not Y then + return true, SendMessageFailure(Player, "'" .. Split[4] .. "' is not a valid number") + elseif not Z then + return true, SendMessageFailure(Player, "'" .. Split[5] .. "' is not a valid number") + end end if SpawnEntity(Split[2], World, X, Y, Z, Player) then - Player:SendMessageSuccess("Successfully summoned entity at [X:" .. math.floor(X) .. " Y:" .. math.floor(Y) .. " Z:" .. math.floor(Z) .. "]") + Response = SendMessageSuccess(Player, "Successfully summoned entity at [X:" .. math.floor(X) .. " Y:" .. math.floor(Y) .. " Z:" .. math.floor(Z) .. "]") else - Player:SendMessageFailure("Unknown entity '" .. Split[2] .. "'") + Response = SendMessageFailure(Player, "Unknown entity '" .. Split[2] .. "'") end end - return true + return true, Response +end + +function HandleConsoleSummon(Split) + return HandleSummonCommand(Split) end diff --git a/cmd_tell.lua b/cmd_tell.lua new file mode 100644 index 0000000..e1255d9 --- /dev/null +++ b/cmd_tell.lua @@ -0,0 +1,66 @@ +local LastSender = {} + +function HandleTellCommand(Split, Player) + local Response + + local SenderName = "Server" + local SenderUUID = "CuberiteServerConsoleSender" + + if Player then + SenderName = Player:GetName() + SenderUUID = Player:GetUUID() + end + + local SendPrivateMessage = function(OtherPlayer) + local Message = table.concat(Split, " ", 2) + + local ReceiverName = "Server" + local ReceiverUUID = "CuberiteServerConsoleSender" + + if OtherPlayer then + ReceiverName = OtherPlayer:GetName() + ReceiverUUID = OtherPlayer:GetUUID() + + OtherPlayer:SendMessagePrivateMsg(Message, SenderName) + else + LOG("[MSG:" .. SenderName .. "] " .. Message) + end + LastSender[ReceiverUUID] = SenderUUID + + Response = SendMessageSuccess(Player, "Message to \"" .. ReceiverName .. "\" sent!") + end + + if Split[1] == "r" or Split[1] == "/r" then + if not Split[2] then + Response = SendMessage(Player, "Usage: " .. Split[1] .. " ") + elseif not LastSender[SenderUUID] then + Response = SendMessageFailure(Player, "No last sender found") + elseif "CuberiteServerConsoleSender" == LastSender[SenderUUID] then + SendPrivateMessage() + else + local ReceiverName = cMojangAPI:GetPlayerNameFromUUID(LastSender[SenderUUID], true) + if not cRoot:Get():FindAndDoWithPlayer(ReceiverName, SendPrivateMessage) then + Response = SendMessageFailure(Player, "Player \"" .. ReceiverName .. "\" not found") + end + end + else + if not Split[3] then + Response = SendMessage(Player, "Usage: " .. Split[1] .. " ") + elseif Split[2] == "" or not cRoot:Get():FindAndDoWithPlayer(Split[2], SendPrivateMessage) then + Response = SendMessageFailure(Player, "Player \"" .. Split[2] .. "\" not found") + end + end + return true, Response +end + +function HandleConsoleTell(Split) + return HandleTellCommand(Split) +end + +function HandleRCommand(Split, Player) + return HandleTellCommand(Split, Player) +end + +function HandleConsoleR(Split) + return HandleRCommand(Split) +end diff --git a/cmd_tps.lua b/cmd_tps.lua index 42613e3..69c4718 100644 --- a/cmd_tps.lua +++ b/cmd_tps.lua @@ -34,11 +34,11 @@ end function HandleTpsCommand(Split, Player) local Response = {} - table.insert(Response, SendMessage(Player, "Global TPS: " .. GetAverageNum(GlobalTps))) + table.insert(Response, "Global TPS: " .. GetAverageNum(GlobalTps)) for WorldName, WorldTps in pairs(TpsCache) do - table.insert(Response, SendMessage(Player, "World \"" .. WorldName .. "\": " .. GetAverageNum(WorldTps) .. " TPS")) + table.insert(Response, "World \"" .. WorldName .. "\": " .. GetAverageNum(WorldTps) .. " TPS") end - return true, table.concat(Response, "") + return true, SendMessage(Player, table.concat(Response, "\n")) end function HandleConsoleTps(Split) diff --git a/cmd_unloadchunks.lua b/cmd_unloadchunks.lua new file mode 100644 index 0000000..6c9d24a --- /dev/null +++ b/cmd_unloadchunks.lua @@ -0,0 +1,12 @@ +function HandleUnloadChunksCommand(Split, Player) + local UnloadChunks = function(World) + World:QueueUnloadUnusedChunks() + end + + cRoot:Get():ForEachWorld(UnloadChunks) + return true, SendMessage(Player, "Successfully unloaded unused chunks") +end + +function HandleConsoleUnloadChunks(Split) + return HandleUnloadChunksCommand(Split) +end diff --git a/cmd_worlds.lua b/cmd_worlds.lua new file mode 100644 index 0000000..45c860d --- /dev/null +++ b/cmd_worlds.lua @@ -0,0 +1,26 @@ +function HandleWorldsCommand(Split, Player) + local NumWorlds = 0 + local Worlds = {} + local Response = {} + + local ListWorld = function(World) + NumWorlds = NumWorlds + 1 + Worlds[NumWorlds] = World:GetName() + end + + cRoot:Get():ForEachWorld(ListWorld) + + -- Start creating the actual response + table.insert(Response, SendMessage(Player, "There are " .. NumWorlds .. " worlds:")) + table.insert(Response, SendMessage(Player, table.concat(Worlds, ", "))) + + if Player then + SendMessage(Player, "You are in world " .. Player:GetWorld():GetName()) + end + + return true, table.concat(Response, "\n") +end + +function HandleConsoleWorlds(Split) + return HandleWorldsCommand(Split) +end diff --git a/console.lua b/console.lua index 44a69be..8a95b4d 100644 --- a/console.lua +++ b/console.lua @@ -2,53 +2,7 @@ -- console.lua -- Implements things related to console commands - - - - - -function HandleConsoleNumChunks(Split) - -- List each world's chunk count into a table, sum the total chunk count: - local Output = {} - local Total = 0 - cRoot:Get():ForEachWorld( - function(a_World) - table.insert(Output, a_World:GetName() .. ": " .. a_World:GetNumChunks() .. " chunks") - Total = Total + a_World:GetNumChunks() - end - ) - table.sort(Output) - - -- Return the complete report: - return true, table.concat(Output, "\n") .. "\nTotal: " .. Total .. " chunks\n" -end - - - - - -function HandleConsolePlayers(Split) - local PlayersInWorlds = {} -- "WorldName" => [players array] - local AddToTable = function(Player) - local WorldName = Player:GetWorld():GetName() - if (PlayersInWorlds[WorldName] == nil) then - PlayersInWorlds[WorldName] = {} - end - table.insert(PlayersInWorlds[WorldName], Player:GetName() .. " @ " .. Player:GetIP()) - end - - cRoot:Get():ForEachPlayer(AddToTable) - - local Out = "" - for WorldName, Players in pairs(PlayersInWorlds) do - Out = Out .. "World " .. WorldName .. ":\n" - for i, PlayerName in ipairs(Players) do - Out = Out .. " " .. PlayerName .. "\n" - end - end - - return true, Out -end +-- TODO: remove this file, migrate commands below to unified cmd_*.lua format @@ -162,17 +116,3 @@ function HandleConsoleTeleport(Split) return true, "Usage: '" .. Split[1] .. " ' or 'tp '" end end - - - - -function HandleConsoleUnload(Split) - local UnloadChunks = function(World) - World:QueueUnloadUnusedChunks() - end - - local Out = "Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount() .. "\n" - cRoot:Get():ForEachWorld(UnloadChunks) - Out = Out .. "Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount() - return true, Out -end diff --git a/functions.lua b/core_functions.lua similarity index 68% rename from functions.lua rename to core_functions.lua index eccf11f..58b00ec 100644 --- a/functions.lua +++ b/core_functions.lua @@ -39,18 +39,21 @@ function KickPlayer(PlayerName, Reason) end function RelativeCommandCoord(Split, Coord) - if string.sub(Split, 1, 1) == "~" then - local Relative = tonumber(string.sub(Split, 2, -1)) - - if Coord then - if Relative then - return Coord + Relative + if Split then + if string.sub(Split, 1, 1) == "~" then + local Relative = tonumber(string.sub(Split, 2, -1)) + + if Coord then + if Relative then + return Coord + Relative + end + return Coord end - return Coord + return Relative end - return Relative + return tonumber(Split) end - return tonumber(Split) + return Split end -- Safer method to find players @@ -94,6 +97,7 @@ function SendMessageFailure(Player, Message) end -- Teleports a_SrcPlayer to a player named a_DstPlayerName; if a_TellDst is true, will send a notice to the destination player +-- TODO: cleanup function TeleportToPlayer( a_SrcPlayer, a_DstPlayerName, a_TellDst ) local teleport = function(a_DstPlayerName) @@ -121,50 +125,3 @@ function TeleportToPlayer( a_SrcPlayer, a_DstPlayerName, a_TellDst ) end end - -function getSpawnProtectRadius(WorldName) - return WorldsSpawnProtect[WorldName] -end - -function GetWorldDifficulty(a_World) - local Difficulty = WorldsWorldDifficulty[a_World:GetName()] - if (Difficulty == nil) then - Difficulty = 1 - end - - return Clamp(Difficulty, 0, 3) -end - -function SetWorldDifficulty(a_World, a_Difficulty) - local Difficulty = Clamp(a_Difficulty, 0, 3) - WorldsWorldDifficulty[a_World:GetName()] = Difficulty - - -- Update world.ini - local WorldIni = cIniFile() - WorldIni:ReadFile(a_World:GetIniFileName()) - WorldIni:SetValueI("Difficulty", "WorldDifficulty", Difficulty) - WorldIni:WriteFile(a_World:GetIniFileName()) -end - -function LoadWorldSettings(a_World) - local WorldIni = cIniFile() - WorldIni:ReadFile(a_World:GetIniFileName()) - WorldsSpawnProtect[a_World:GetName()] = WorldIni:GetValueSetI("SpawnProtect", "ProtectRadius", 10) - WorldsWorldLimit[a_World:GetName()] = WorldIni:GetValueSetI("WorldLimit", "LimitRadius", 0) - WorldsWorldDifficulty[a_World:GetName()] = WorldIni:GetValueSetI("Difficulty", "WorldDifficulty", 1) - WorldIni:WriteFile(a_World:GetIniFileName()) -end - - -function GetAdminRank() - local AdminRank - local Ranks = cRankManager:GetAllRanks() - for _, Rank in ipairs(Ranks) do - local Permissions = cRankManager:GetRankPermissions(Rank) - for _, Permission in ipairs(Permissions) do - if Permission == "*" then - return Rank - end - end - end -end diff --git a/hardcore.lua b/core_hardcore.lua similarity index 100% rename from hardcore.lua rename to core_hardcore.lua diff --git a/itemrepair.lua b/core_itemrepair.lua similarity index 100% rename from itemrepair.lua rename to core_itemrepair.lua diff --git a/motd.lua b/core_motd.lua similarity index 86% rename from motd.lua rename to core_motd.lua index d30799f..65e8db9 100644 --- a/motd.lua +++ b/core_motd.lua @@ -19,6 +19,11 @@ function ShowMOTD(Player) end end +function OnPlayerJoined(Player) + -- Send the MOTD to the player: + ShowMOTD(Player) +end + function HandleMOTDCommand(Split, Player) ShowMOTD(Player) return true diff --git a/core_worlds.lua b/core_worlds.lua new file mode 100644 index 0000000..d7d5d46 --- /dev/null +++ b/core_worlds.lua @@ -0,0 +1,220 @@ +-- General world-related core functionality, including difficulty, spawn protection and world limit +-- TODO: Move difficulty and world limit (border) functionality to main server + +local WorldsSpawnProtect = {} +local WorldsWorldDifficulty = {} +local WorldsWorldLimit = {} + +function LoadWorldSettings(World) + local WorldIni = cIniFile() + WorldIni:ReadFile(World:GetIniFileName()) + + WorldsSpawnProtect[World:GetName()] = WorldIni:GetValueSetI("SpawnProtect", "ProtectRadius", 10) + WorldsWorldDifficulty[World:GetName()] = WorldIni:GetValueSetI("Difficulty", "WorldDifficulty", 1) + WorldsWorldLimit[World:GetName()] = WorldIni:GetValueSetI("WorldLimit", "LimitRadius", 0) + + WorldIni:WriteFile(World:GetIniFileName()) +end + + +-- Implements server difficulty for Cuberite + +local MobDamages = +{ + ["cBlaze"] = { 4, 6, 9 }, + ["cCaveSpider"] = { 2, 2, 3 }, + ["cEnderDragon"] = { 6, 10, 15 }, + ["cEnderman"] = { 4, 7, 10 }, + ["cGiant"] = { 26, 50, 75 }, + ["cGuardian"] = { 5, 6, 9 }, + ["cIronGolem"] = { 4, 7, 10 }, + ["cSkeleton"] = { 2, 2, 3 }, + ["cSpider"] = { 2, 2, 3 }, + ["cWitherSkeleton"] = { 5, 8, 12 }, + ["cZombie"] = { 2, 3, 4 }, + ["cZombiePigman"] = { 5, 9, 13 }, + ["cZombieVillager"] = { 2, 3, 4 }, +} + +function GetWorldDifficulty(World) + local Difficulty = WorldsWorldDifficulty[World:GetName()] + if not Difficulty then + Difficulty = 1 + end + + return Clamp(Difficulty, 0, 3) +end + +function SetWorldDifficulty(World, Difficulty) + local Difficulty = Clamp(Difficulty, 0, 3) + WorldsWorldDifficulty[World:GetName()] = Difficulty + + -- Update world.ini + local WorldIni = cIniFile() + WorldIni:ReadFile(World:GetIniFileName()) + + WorldIni:SetValueI("Difficulty", "WorldDifficulty", Difficulty) + + WorldIni:WriteFile(World:GetIniFileName()) +end + +function OnTakeDamage(Receiver, TDI) + local Attacker + + if TDI.Attacker then + Attacker = TDI.Attacker + local WorldDifficulty = GetWorldDifficulty(Attacker:GetWorld()) + local Damages = MobDamages[Attacker:GetClass()] + if Damages then + TDI.FinalDamage = Damages[WorldDifficulty] + end + end + + -- Apply armor protection + local ArmorCover = Receiver:GetArmorCoverAgainst(Attacker, TDI.DamageType, TDI.FinalDamage) + local EnchantmentCover = Receiver:GetEnchantmentCoverAgainst(Attacker, TDI.DamageType, TDI.FinalDamage) + TDI.FinalDamage = TDI.FinalDamage - ArmorCover - EnchantmentCover +end + +function OnSpawningMonster(World, Monster) + if GetWorldDifficulty(World) == 0 and Monster:GetMobFamily() == cMonster.mfHostile then + -- Don't spawn hostile mobs in peaceful mode + return true + end + return false +end + + +-- Implements spawn protection for Cuberite + +function GetSpawnProtectRadius(WorldName) + return WorldsSpawnProtect[WorldName] +end + +local function IsInSpawn(X, Y, Z, WorldName) + local ProtectRadius = WorldsSpawnProtect[WorldName] + + if ProtectRadius > 0 then + local World = cRoot:Get():GetWorld(WorldName) + local SpawnArea = cBoundingBox(Vector3d(World:GetSpawnX() - ProtectRadius, -1000, World:GetSpawnZ() - ProtectRadius), Vector3d(World:GetSpawnX() + ProtectRadius, 1000, World:GetSpawnZ() + ProtectRadius)) + local PlayerLocation = Vector3d(X, Y, Z) + + if SpawnArea:IsInside(PlayerLocation) then + return true + end + end +end + +local function CheckBlockModification(Player, BlockX, BlockY, BlockZ) + if not Player:HasPermission("core.build") then + SendMessageFailure(Player, "You do not have the \"core.build\" permission, thou cannot build") + return true + end + + if not Player:HasPermission("core.spawnprotect.bypass") and IsInSpawn(BlockX, BlockY, BlockZ, Player:GetWorld():GetName()) then + SendMessageFailure(Player, "Go further from spawn to build") + return true + end +end + +function OnBlockSpread(World, BlockX, BlockY, BlockZ, Source) + if Source == ssFireSpread and IsInSpawn(BlockX, BlockY, BlockZ, World:GetName()) then + return true + end + +end + +function OnExploding(World, ExplosionSize, CanCauseFire, X, Y, Z, Source, SourceData) + if IsInSpawn(X, Y, Z, World:GetName()) then + return true + end +end + +function OnPlayerBreakingBlock(Player, BlockX, BlockY, BlockZ, BlockFace, Status, OldBlockType, OldBlockMeta) + return CheckBlockModification(Player, BlockX, BlockY, BlockZ) +end + +function OnPlayerPlacingBlock(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ, BlockType) + return CheckBlockModification(Player, BlockX, BlockY, BlockZ) +end + +function OnPlayerRightClick(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ) + if not Player:HasPermission("core.spawnprotect.bypass") and IsInSpawn(BlockX, BlockY, BlockZ, Player:GetWorld():GetName()) then + if Player:GetWorld():GetBlock(BlockX, BlockY, BlockZ) == E_BLOCK_GRASS or + Player:GetWorld():GetBlock(BlockX, BlockY, BlockZ) == E_BLOCK_DIRT then + if Player:GetEquippedItem():IsEmpty() then + return false + end + end + + if Player:GetEquippedItem().m_ItemType ~= E_ITEM_FLINT_AND_STEEL and + Player:GetEquippedItem().m_ItemType ~= E_ITEM_FIRE_CHARGE then + return false + end + + SendMessageFailure(Player, "Go further from spawn to build") + return true + end +end + + +-- Implements world limiter for Cuberite + +local WorldLimiter_Flag = false -- True when teleportation is about to occur, false otherwise +local WorldLimiter_LastMessage = -100 -- The last time the player was sent a message about reaching the border +function OnPlayerMoving(Player) + if (WorldLimiter_Flag == true) then + return + end + + local LimitChunks = WorldsWorldLimit[Player:GetWorld():GetName()] + + -- The world probably was created by an external plugin. Let's load the settings. + if not LimitChunks then + LoadWorldSettings(Player:GetWorld()) + LimitChunks = WorldsWorldLimit[Player:GetWorld():GetName()] + end + + if (LimitChunks > 0) then + local World = Player:GetWorld() + local Limit = LimitChunks * 16 - 1 + local SpawnX = math.floor(World:GetSpawnX()) + local SpawnZ = math.floor(World:GetSpawnZ()) + local X = math.floor(Player:GetPosX()) + local Z = math.floor(Player:GetPosZ()) + local NewX = X + local NewZ = Z + + if (X > SpawnX + Limit) then + NewX = SpawnX + Limit + elseif (X < SpawnX - Limit) then + NewX = SpawnX - Limit + end + + if (Z > SpawnZ + Limit) then + NewZ = SpawnZ + Limit + elseif (Z < SpawnZ - Limit) then + NewZ = SpawnZ - Limit + end + + if (X ~= NewX) or (Z ~= NewZ) then + WorldLimiter_Flag = true + + local UpTime = cRoot:Get():GetServerUpTime() + if UpTime - WorldLimiter_LastMessage > 30 then + WorldLimiter_LastMessage = UpTime + Player:SendMessageInfo("You have reached the world border") + end + + local UUID = Player:GetUUID() + World:ScheduleTask(3, function(World) + World:DoWithPlayerByUUID(UUID, function(Player) + Player:TeleportToCoords(NewX, Player:GetPosY(), NewZ) + WorldLimiter_Flag = false + end) + end) + end + + + end +end diff --git a/difficulties.lua b/difficulties.lua index 665b4cc..4bba8a6 100644 --- a/difficulties.lua +++ b/difficulties.lua @@ -1,35 +1,3 @@ -local MobDamages = -{ - ["cSpider"] = { 2, 2, 3 }, - ["cEnderman"] = { 4, 7, 10 }, - ["cZombie"] = { }, -- Handled in OnTakeDamage() - ["cSlime"] = { 4, 4, 4 }, - ["cCaveSpider"] = { 2, 2, 3 }, - ["cZombiePigman"] = { 5, 9, 13 }, - ["cSkeleton"] = { 2, 2, 3 }, - ["cBlaze"] = { 4, 6, 9 } -} - -local IsEntityBlockedInPeaceful = -{ - ["cZombie"] = true, - ["cZombiePigman"] = true, - ["cSpider"] = true, - ["cCaveSpider"] = true, - ["cEnderman"] = true, - ["cEnderDragon"] = true, - ["cSkeleton"] = true, - ["cGhast"] = true, - ["cCreeper"] = true, - ["cSilverfish"] = true, - ["cBlaze"] = true, - ["cSlime"] = true, - ["cWitch"] = true, - ["cWither"] = true, - ["cSilverfish"] = true, - ["cGuardian"] = true, -} - function HandleDifficultyCommand ( Split, Player ) if (Split[2] == nil) then if (#Split == 1) then @@ -71,49 +39,3 @@ function HandleDifficultyCommand ( Split, Player ) return true end - -function OnTakeDamage(Receiver, TDI) - local Attacker - - if TDI.Attacker then - Attacker = TDI.Attacker - local WorldDifficulty = GetWorldDifficulty(Attacker:GetWorld()) - - if Attacker:IsA("cZombie") then - -- The damage value from the zombie is computed from the zombie health. See http://minecraft.gamepedia.com/Zombie - if (WorldDifficulty == 1) then - if (Attacker:GetHealth() >= 16) then TDI.FinalDamage = 2 - elseif (Attacker:GetHealth() >= 11) then TDI.FinalDamage = 3 - elseif (Attacker:GetHealth() >= 6) then TDI.FinalDamage = 3 - else TDI.FinalDamage = 4 end - elseif (WorldDifficulty == 2) then - if (Attacker:GetHealth() >= 16) then TDI.FinalDamage = 3 - elseif (Attacker:GetHealth() >= 11) then TDI.FinalDamage = 4 - elseif (Attacker:GetHealth() >= 6) then TDI.FinalDamage = 5 - else TDI.FinalDamage = 6 end - elseif (WorldDifficulty == 3) then - if (Attacker:GetHealth() >= 16) then TDI.FinalDamage = 4 - elseif (Attacker:GetHealth() >= 11) then TDI.FinalDamage = 6 - elseif (Attacker:GetHealth() >= 6) then TDI.FinalDamage = 7 - else TDI.FinalDamage = 9 end - end - else - local Damages = MobDamages[Attacker:GetClass()] - if Damages then - TDI.FinalDamage = Damages[WorldDifficulty] - end - end - end - - -- Apply armor protection - local ArmorCover = Receiver:GetArmorCoverAgainst(Attacker, TDI.DamageType, TDI.FinalDamage) - local EnchantmentCover = Receiver:GetEnchantmentCoverAgainst(Attacker, TDI.DamageType, TDI.FinalDamage) - TDI.FinalDamage = TDI.FinalDamage - ArmorCover - EnchantmentCover -end - -function OnSpawningEntity(World, Entity) - if GetWorldDifficulty(World) == 0 then - return IsEntityBlockedInPeaceful[Entity:GetClass()] - end - return false -end diff --git a/main.lua b/main.lua index 33d234a..6d4462c 100644 --- a/main.lua +++ b/main.lua @@ -1,32 +1,8 @@ -- main.lua - -- Implements the main plugin entrypoint - - - - --- Configuration --- Use prefixes or not. --- If set to true, messages are prefixed, e. g. "[FATAL]". If false, messages are colored. -g_UsePrefixes = true - - - - - --- Global variables -WorldsSpawnProtect = {} -WorldsWorldLimit = {} -WorldsWorldDifficulty = {} -lastsender = {} - - - - --- Called by Cuberite on plugin start to initialize the plugin function Initialize(Plugin) - Plugin:SetName("Core") + Plugin:SetName(g_PluginInfo.Name) -- Register for all hooks needed cPluginManager:AddHook(cPluginManager.HOOK_BLOCK_SPREAD, OnBlockSpread) @@ -40,26 +16,20 @@ function Initialize(Plugin) cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving) cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_PLACING_BLOCK, OnPlayerPlacingBlock) cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICK, OnPlayerRightClick) - cPluginManager:AddHook(cPluginManager.HOOK_SPAWNING_ENTITY, OnSpawningEntity) + cPluginManager:AddHook(cPluginManager.HOOK_SPAWNING_MONSTER, OnSpawningMonster) cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage) cPluginManager:AddHook(cPluginManager.HOOK_TICK, OnTick) cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick) -- Bind ingame commands: - - -- Load the InfoReg shared library: dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua") - - -- Bind all the commands: RegisterPluginInfoCommands() - - -- Bind all the console commands: RegisterPluginInfoConsoleCommands() -- Load SpawnProtection and WorldLimit settings for individual worlds: cRoot:Get():ForEachWorld( - function (a_World) - LoadWorldSettings(a_World) + function (World) + LoadWorldSettings(World) end ) @@ -69,8 +39,8 @@ function Initialize(Plugin) -- Initialize the whitelist, load its DB, do whatever processing it needs on startup: InitializeWhitelist() - -- Initialize the Item Blacklist (the list of items that cannot be obtained using the give command) - IntializeItemBlacklist( Plugin ) + -- Initialize the Item Blacklist (the list of items that cannot be obtained using the give command): + IntializeItemBlacklist(Plugin) -- Add webadmin tabs: Plugin:AddWebTab("Manage Server", HandleRequest_ManageServer) @@ -84,14 +54,15 @@ function Initialize(Plugin) Plugin:AddWebTab("Ranks", HandleRequest_Ranks) Plugin:AddWebTab("Player Ranks", HandleRequest_PlayerRanks) + -- Load the message of the day from file, and cache it: LoadMOTD() - WEBLOGINFO("Core is initialized") + WEBLOGINFO("Core is initialised") LOG("Initialised " .. Plugin:GetName()) return true end function OnDisable() - LOG( "Disabled Core!" ) + LOG("Disabled " .. cPluginManager:GetCurrentPlugin():GetName() .. "!") end diff --git a/playerjoin.lua b/playerjoin.lua deleted file mode 100644 index a0d3d7a..0000000 --- a/playerjoin.lua +++ /dev/null @@ -1,7 +0,0 @@ -function OnPlayerJoined(Player) - -- Send the MOTD to the player: - ShowMOTD(Player) - - -- Add a message to the webadmin chat: - WEBLOGINFO(Player:GetName() .. " has joined the game") -end diff --git a/portal-worlds.lua b/portal-worlds.lua deleted file mode 100644 index 59a866d..0000000 --- a/portal-worlds.lua +++ /dev/null @@ -1,47 +0,0 @@ -function HandlePortalCommand(Split, Player) - local NumParams = #Split - if (NumParams == 1) then - SendMessage(Player, "You are in world " .. Player:GetWorld():GetName()) - return true - elseif (NumParams ~= 2) then - SendMessage(Player, "Usage: " .. Split[1] .. " [world]") - return true - end - local World = cRoot:Get():GetWorld(Split[2]) - if (Player:GetWorld():GetName() == Split[2]) then - SendMessageFailure( Player, "You are in " .. Split[2] .. "!" ) - return true - elseif( World == nil ) then - SendMessageFailure( Player, "Could not find world " .. Split[2] .. "!" ) - return true - elseif( Player:MoveToWorld(World, true, Vector3d(World:GetSpawnX(), World:GetSpawnY(), World:GetSpawnZ())) == false ) then - SendMessageFailure( Player, "Could not move to world " .. Split[2] .. "!" ) - return true - end - - SendMessageSuccess( Player, "Moved successfully to '" .. Split[2] .. "'! :D" ) - return true -end - - - - - - -function HandleWorldsCommand(Split, Player) - local NumWorlds = 0 - local Worlds = {} - cRoot:Get():ForEachWorld(function(World) - NumWorlds = NumWorlds + 1 - Worlds[NumWorlds] = World:GetName() - end) - - SendMessage(Player, "There are " .. NumWorlds .. " worlds:") - SendMessage(Player, table.concat(Worlds, ", ")) - SendMessage(Player, "You are in world " .. Player:GetWorld():GetName()) - return true -end - - - - diff --git a/spawnprotection.lua b/spawnprotection.lua deleted file mode 100644 index ae2a6f4..0000000 --- a/spawnprotection.lua +++ /dev/null @@ -1,68 +0,0 @@ - --- Implements spawn protection for Cuberite - -local function IsInSpawn(X, Y, Z, WorldName) - local ProtectRadius = WorldsSpawnProtect[WorldName] - - if ProtectRadius > 0 then - local World = cRoot:Get():GetWorld(WorldName) - local SpawnArea = cBoundingBox(Vector3d(World:GetSpawnX() - ProtectRadius, -1000, World:GetSpawnZ() - ProtectRadius), Vector3d(World:GetSpawnX() + ProtectRadius, 1000, World:GetSpawnZ() + ProtectRadius)) - local PlayerLocation = Vector3d(X, Y, Z) - - if SpawnArea:IsInside(PlayerLocation) then - return true - end - end -end - -local function CheckBlockModification(Player, BlockX, BlockY, BlockZ) - if not Player:HasPermission("core.build") then - SendMessageFailure(Player, "You do not have the \"core.build\" permission, thou cannot build") - return true - end - - if not Player:HasPermission("core.spawnprotect.bypass") and IsInSpawn(BlockX, BlockY, BlockZ, Player:GetWorld():GetName()) then - SendMessageFailure(Player, "Go further from spawn to build") - return true - end -end - -function OnBlockSpread(World, BlockX, BlockY, BlockZ, Source) - if Source == ssFireSpread and IsInSpawn(BlockX, BlockY, BlockZ, World:GetName()) then - return true - end - -end - -function OnExploding(World, ExplosionSize, CanCauseFire, X, Y, Z, Source, SourceData) - if IsInSpawn(X, Y, Z, World:GetName()) then - return true - end -end - -function OnPlayerBreakingBlock(Player, BlockX, BlockY, BlockZ, BlockFace, Status, OldBlockType, OldBlockMeta) - return CheckBlockModification(Player, BlockX, BlockY, BlockZ) -end - -function OnPlayerPlacingBlock(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ, BlockType) - return CheckBlockModification(Player, BlockX, BlockY, BlockZ) -end - -function OnPlayerRightClick(Player, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ) - if not Player:HasPermission("core.spawnprotect.bypass") and IsInSpawn(BlockX, BlockY, BlockZ, Player:GetWorld():GetName()) then - if Player:GetWorld():GetBlock(BlockX, BlockY, BlockZ) == E_BLOCK_GRASS or - Player:GetWorld():GetBlock(BlockX, BlockY, BlockZ) == E_BLOCK_DIRT then - if Player:GetEquippedItem():IsEmpty() then - return false - end - end - - if Player:GetEquippedItem().m_ItemType ~= E_ITEM_FLINT_AND_STEEL and - Player:GetEquippedItem().m_ItemType ~= E_ITEM_FIRE_CHARGE then - return false - end - - SendMessageFailure(Player, "Go further from spawn to build") - return true - end -end diff --git a/tell.lua b/tell.lua deleted file mode 100644 index b6668d3..0000000 --- a/tell.lua +++ /dev/null @@ -1,55 +0,0 @@ -function HandleTellCommand(Split, Player) - if (Split[2] == nil) or (Split[3] == nil) then - SendMessage( Player, "Usage: "..Split[1].." ") - return true - end - - local FoundPlayer = false - - local SendMessage = function(OtherPlayer) - - if (OtherPlayer:GetName() == Split[2]) then - local newSplit = table.concat( Split, " ", 3 ) - - SendMessageSuccess( Player, "Message to player " .. Split[2] .. " sent!" ) - OtherPlayer:SendMessagePrivateMsg(newSplit, Player:GetName()) - - lastsender[OtherPlayer:GetName()] = Player:GetName() - - FoundPlayer = true - end - end - - cRoot:Get():ForEachPlayer(SendMessage) - - if not FoundPlayer then - SendMessageFailure( Player, 'Player "' ..Split[2].. '" not found') - end - - return true -end - - -function HandleRCommand(Split,Player) - if Split[2] == nil then - Player:SendMessageInfo("Usage: "..Split[1].." ") - else - local SendMessage = function(OtherPlayer) - if (OtherPlayer:GetName() == lastsender[Player:GetName()]) then - local newSplit = table.concat( Split, " ", 2 ) - Player:SendMessageSuccess( "Message to player " .. lastsender[Player:GetName()] .. " sent!" ) - OtherPlayer:SendMessagePrivateMsg(newSplit, Player:GetName()) - lastsender[OtherPlayer:GetName()] = Player:GetName() - return true - end - end - if lastsender[Player:GetName()] == nil then - Player:SendMessageFailure("No last sender found") - else - if (not(cRoot:Get():FindAndDoWithPlayer(lastsender[Player:GetName()], SendMessage))) then - Player:SendMessageFailure("Player not found") - end - end - end - return true -end diff --git a/web_chat.lua b/web_chat.lua index 010e232..d5de066 100644 --- a/web_chat.lua +++ b/web_chat.lua @@ -260,6 +260,14 @@ end +function OnPlayerJoined(Player) + WEBLOGINFO(Player:GetName() .. " has joined the game") +end + + + + + function OnDisconnect(a_Player) WEBLOGINFO(a_Player:GetName() .. " has left the game") end diff --git a/worldlimiter.lua b/worldlimiter.lua deleted file mode 100644 index 26883ff..0000000 --- a/worldlimiter.lua +++ /dev/null @@ -1,58 +0,0 @@ -local WorldLimiter_Flag = false -- True when teleportation is about to occur, false otherwise -local WorldLimiter_LastMessage = -100 -- The last time the player was sent a message about reaching the border -function OnPlayerMoving(Player) - if (WorldLimiter_Flag == true) then - return - end - - local LimitChunks = WorldsWorldLimit[Player:GetWorld():GetName()] - - -- The world probably was created by an external plugin. Let's load the settings. - if not LimitChunks then - LoadWorldSettings(Player:GetWorld()) - LimitChunks = WorldsWorldLimit[Player:GetWorld():GetName()] - end - - if (LimitChunks > 0) then - local World = Player:GetWorld() - local Limit = LimitChunks * 16 - 1 - local SpawnX = math.floor(World:GetSpawnX()) - local SpawnZ = math.floor(World:GetSpawnZ()) - local X = math.floor(Player:GetPosX()) - local Z = math.floor(Player:GetPosZ()) - local NewX = X - local NewZ = Z - - if (X > SpawnX + Limit) then - NewX = SpawnX + Limit - elseif (X < SpawnX - Limit) then - NewX = SpawnX - Limit - end - - if (Z > SpawnZ + Limit) then - NewZ = SpawnZ + Limit - elseif (Z < SpawnZ - Limit) then - NewZ = SpawnZ - Limit - end - - if (X ~= NewX) or (Z ~= NewZ) then - WorldLimiter_Flag = true - - local UpTime = cRoot:Get():GetServerUpTime() - if UpTime - WorldLimiter_LastMessage > 30 then - WorldLimiter_LastMessage = UpTime - Player:SendMessageInfo("You have reached the world border") - end - - local UUID = Player:GetUUID() - World:ScheduleTask(3, function(World) - World:DoWithPlayerByUUID(UUID, function(Player) - Player:TeleportToCoords(NewX, Player:GetPosY(), NewZ) - WorldLimiter_Flag = false - end) - end) - end - - - end -end