![]() |
|
Help with player control plugin - Printable Version +- Cuberite Forum (https://forum.cuberite.org) +-- Forum: Plugins (https://forum.cuberite.org/forum-1.html) +--- Forum: Plugin Discussion (https://forum.cuberite.org/forum-8.html) +--- Thread: Help with player control plugin (/thread-2985.html) |
Help with player control plugin - Mathias - 07-12-2017 I'm currently porting Bukkit's iControlU plugin to Cuberite. This plugin allows a player to control another player's movement and inventory, and chat as the player. The movement part of the plugin works just fine, but I'm having trouble with the inventory and chat. What I want to do is to send the inventory of the controller to the target whenever the inventories are different from each other, without causing a deadlock. The inventory is sent, but eventually (or sometimes immediately) the server stops responding. As for the chat, is there a way to chat as another player at the moment? Info.lua: g_PluginInfo = {
Name = "iControlU",
Version = "1",
Date = "2017-07-12",
SourceLocation = "https://github.com/mathiascode/iControlU",
Description = [[Plugin for Cuberite that allows players to control other players.]],
Commands =
{
["/icu"] =
{
HelpString = "Control another player's movement",
Permission = "icu.command",
Subcommands =
{
control =
{
Alias = "c",
Permission = "icu.control",
Handler = HandleControlCommand,
},
stop =
{
Alias = "s",
Permission = "icu.stop",
Handler = HandleStopCommand,
},
},
},
},
Permissions =
{
["icu.exempt"] =
{
Description = "Players with this permission cannot be controlled",
RecommendedGroups = "everyone",
},
},
}
main.lua: ControllerFor = {}
TargetFor = {}
Inventory = {}
GlobalTime = 0
function Initialize(Plugin)
Plugin:SetName(g_PluginInfo.Name)
Plugin:SetVersion(g_PluginInfo.Version)
cPluginManager:AddHook(cPluginManager.HOOK_CHAT, OnChat)
cPluginManager:AddHook(cPluginManager.HOOK_ENTITY_CHANGING_WORLD, OnEntityChangingWorld)
cPluginManager:AddHook(cPluginManager.HOOK_EXECUTE_COMMAND, OnExecuteCommand)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_ANIMATION, OnPlayerAnimation)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_DESTROYED, OnPlayerDestroyed)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_LEFT_CLICK, OnPlayerLeftClick)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_MOVING, OnPlayerMoving)
cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_TOSSING_ITEM, OnPlayerTossingItem)
cPluginManager:AddHook(cPluginManager.HOOK_TAKE_DAMAGE, OnTakeDamage)
cPluginManager:AddHook(cPluginManager.HOOK_WORLD_TICK, OnWorldTick)
dofile(cPluginManager:GetPluginsPath() .. "/InfoReg.lua")
RegisterPluginInfoCommands()
LOG("Initialised " .. Plugin:GetName() .. " v." .. Plugin:GetVersion())
return true
end
--for i=0,40 do
--local Item = Inventory[Controller:GetUUID()]:GetSlot(i)
--Controller:GetInventory():SetSlot(i, Item)
--end
--Inventory[Controller:GetUUID()] = nil
function HandleControlCommand(Split, Controller)
if Split[3] == nil then
Controller:SendMessageInfo("Usage: " .. Split[1] .. " " .. Split[2] .. " <player>")
else
local Control = function(Target)
if Target == Controller then
Controller:SendMessageFailure("You are already in control of yourself")
elseif Target:HasPermission("icu.exempt") then
Controller:SendMessageFailure("You can't control this player")
else
Controller:TeleportToEntity(Target)
Inventory[Controller:GetUUID()] = Controller:GetInventory()
for i=0,40 do
Controller:GetInventory():SetSlot(i, Target:GetInventory():GetSlot(i))
end
ControllerFor[Target:GetUUID()] = Controller
TargetFor[Controller:GetUUID()] = Target
Controller:SendMessageSuccess("You are now controlling \"" .. Target:GetName() .. "\"")
end
end
if Split[3] == "" or not cRoot:Get():FindAndDoWithPlayer(Split[3], Control) then
Controller:SendMessageFailure("Player \"" .. Split[3] .. "\" not found")
return true
end
end
return true
end
function HandleStopCommand(Split, Controller)
local Target = TargetFor[Controller:GetUUID()]
if Target then
ControllerFor[Target:GetUUID()] = nil
TargetFor[Controller:GetUUID()] = nil
Controller:GetWorld():ScheduleTask(200,
function()
Controller:SetVisible(true)
end
)
Target:Unfreeze()
Controller:SendMessageSuccess("You are no longer controlling \"" .. Target:GetName() .. "\". You are invisible for 10 seconds.")
else
Controller:SendMessageFailure("You are not controlling anyone at the moment")
end
return true
end
function OnChat(Target, Message)
if ControllerFor[Target:GetUUID()] then
Target:SendMessageInfo("You cannot chat while being controlled")
return true
end
end
function OnEntityChangingWorld(Controller, World)
if Controller:IsPlayer() then
local Target = TargetFor[Controller:GetUUID()]
if Target then
Target:MoveToWorld(Controller:GetWorld())
end
end
end
function OnExecuteCommand(Target, CommandSplit, EntireCommand)
if Target and ControllerFor[Target:GetUUID()] then
Target:SendMessageInfo("You cannot run commands while being controlled")
return true
end
end
function OnPlayerAnimation(Controller, Animation)
local Target = TargetFor[Controller:GetUUID()]
if Target then
Controller:GetClientHandle():SendEntityAnimation(Target, Animation)
end
end
function OnPlayerDestroyed(Player)
cRoot:Get():ForEachPlayer(
function(OtherPlayer)
if ControllerFor[Player:GetUUID()] == OtherPlayer then
ControllerFor[Player:GetUUID()] = nil
TargetFor[OtherPlayer:GetUUID()] = nil
OtherPlayer:GetWorld():ScheduleTask(200,
function()
OtherPlayer:SetVisible(true)
end
)
OtherPlayer:SendMessageInfo("The player you were controlling has disconnected. You are invisible for 10 seconds.")
end
if TargetFor[Player:GetUUID()] == OtherPlayer then
ControllerFor[OtherPlayer:GetUUID()] = nil
TargetFor[Player:GetUUID()] = nil
OtherPlayer:Unfreeze()
end
end
)
end
function OnPlayerLeftClick(Target, BlockX, BlockY, BlockZ, BlockFace, Action)
if ControllerFor[Target:GetUUID()] then
return true
end
end
function OnPlayerMoving(Target, OldPosition, NewPosition)
if ControllerFor[Target:GetUUID()] then
return true
end
end
function OnPlayerRightClick(Target, BlockX, BlockY, BlockZ, BlockFace, CursorX, CursorY, CursorZ)
if ControllerFor[Target:GetUUID()] then
return true
end
end
function OnPlayerTossingItem(Target)
if ControllerFor[Target:GetUUID()] then
return true
end
end
function OnTakeDamage(Controller, TDI)
if Controller:IsPlayer() then
local Target = TargetFor[Controller:GetUUID()]
if Target then
Target:TakeDamage(TDI.DamageType, Controller, TDI.RawDamage, TDI.FinalDamage, TDI.Knockback)
end
end
end
function OnWorldTick(World, TimeDelta)
cRoot:Get():ForEachPlayer(
function(Target)
local Controller = ControllerFor[Target:GetUUID()]
if Controller then
Target:TeleportToEntity(Controller)
Target:SetSpeed(0,0,0)
Target:Freeze(Target:GetPosition())
Target:SendRotation(Controller:GetYaw(), Controller:GetPitch())
Target:SetHeadYaw(Controller:GetHeadYaw())
Target:SetYaw(Controller:GetYaw())
Target:SetPitch(Controller:GetPitch())
Target:SetIsFireproof(Controller:IsFireproof())
Target:SetGameMode(Controller:GetGameMode())
Target:SetCrouch(Controller:IsCrouched())
Target:SetFlying(Controller:IsFlying())
Target:SetFoodLevel(Controller:GetFoodLevel())
Target:SetSprint(Controller:IsSprinting())
Controller:SetVisible(false)
if GlobalTime == 20 then
if Controller:GetInventory() ~= Target:GetInventory() then
for i=0,40 do
local Slot = Controller:GetInventory():GetSlot(i)
Target:GetInventory():SetSlot(i, Slot)
end
end
else
GlobalTime = GlobalTime + 1
end
end
end
)
end
function OnDisable()
cRoot:Get():ForEachPlayer(
function(Controller)
local Target = TargetFor[Controller:GetUUID()]
if Target then
Target:Unfreeze()
Controller:SetVisible(true)
Controller:SendMessageInfo("You are no longer controlling a player due to server reload")
end
end
)
LOG("Disabled " .. cPluginManager:GetCurrentPlugin():GetName() .. "!")
end
RE: Help with player control plugin - NiLSPACE - 07-12-2017 There already is a plugin to view a user's inventory: https://forum.cuberite.org/thread-2492.html I'm not sure if it's possible to force a player to chat. You could try cPluginManager:ExecuteCommand on a player without a slash before the command string, but I seriously doubt it would work. RE: Help with player control plugin - Mathias - 07-12-2017 I know, but I want to keep the inventories synced, without opening windows. Whenever the controller modifies his inventory, the inventory should be sent to the victim/target. If the victim modifies his inventory, the controller's inventory should be sent to them. Not sure what I'm doing wrong to cause deadlocks. RE: Help with player control plugin - NiLSPACE - 07-12-2017 We probably need a global OnSlotChanged event for that. Once that's available you can keep track of who has an inventory open of another player and update what they see accordingly. RE: Help with player control plugin - Mathias - 11-27-2017 I've fixed this issue by using ForEachPlayer with cWorld instead of cRoot in the tick hook. |