This plugin comes with a side effect, it seems that having the IsDeveloper flag set lets you edit any sign even if it's locked. I've tried overwriting this behaviour to no avail using this code:
Is there any way around this? I'm using this to give noclip to players on a creative mode server, as Faux Clip is too laggy to use, and some players are using this to grief the signs, pictures and banners of other players.
edit: I found this forum post which is the only useful reference I could find that shows where IsAdmin is used, and it shows that it's used directly in Signage.CanUpdateSign. I think IsDeveloper is used there as well for the same purpose. This can not be overwritten, right?
Merged post
Okay, this is the best solution I could come up with. I'm storing all signs transiently and check if a player is building blocked, if so and they update a sign, the sign is restored with the stored data, otherwise the data is updated. This is the code if anyone else needs to do the same thing:
I'm also not usre why I can not call restoreSign in the same frame as OnSignUpdate was called. I've tried a few other methods, like NextFrame but the only way it would definitely revert the sign all the time was to give it a large delay, like a second or so. Another downside of this code is that on a server with a ton of large signs the memory usage of this plugin will grow quickly and loading the plugin initially may take a few seconds but it's an acceptable workaround.
private bool CanUpdateSign(BasePlayer player, Signage sign)
{
return !(sign.IsLocked() && player.userID != sign.OwnerID);
}Even tho it returns false when trying to edit a locked sign that someone else made, the sign can still be edited.Is there any way around this? I'm using this to give noclip to players on a creative mode server, as Faux Clip is too laggy to use, and some players are using this to grief the signs, pictures and banners of other players.
edit: I found this forum post which is the only useful reference I could find that shows where IsAdmin is used, and it shows that it's used directly in Signage.CanUpdateSign. I think IsDeveloper is used there as well for the same purpose. This can not be overwritten, right?
Merged post
Okay, this is the best solution I could come up with. I'm storing all signs transiently and check if a player is building blocked, if so and they update a sign, the sign is restored with the stored data, otherwise the data is updated. This is the code if anyone else needs to do the same thing:
void Loaded()
{
// original code here ...
BaseEntity.FindObjectsOfType<Signage>().Where(s => s.textureID != 0).ToList().ForEach(s => this.storeSign(s));
Puts($"Stored {this.signs.Count} signs in transient storage.");
}
#region Signs
private Dictionary<uint, byte[]> signs = new Dictionary<uint, byte[]>();
private void OnSignUpdated(Signage sign, BasePlayer player)
{
if (player.IsBuildingBlocked())
{
// Revoke and restore
Puts($"Revoking sign edit of user {player.IPlayer.Name}, restoring texture");
timer.Once(1f, () => restoreSign(sign));
}
else
{
// Accept and store
Puts($"Accepting sign edit of user {player.IPlayer.Name}.");
this.storeSign(sign);
}
}
private void storeSign(Signage sign)
{
byte[] imageByte = FileStorage.server.Get(sign.textureID, FileStorage.Type.png, sign.net.ID);
if (sign.textureID > 0 && imageByte != null)
{
if (this.signs.ContainsKey(sign.net.ID))
this.signs.Remove(sign.net.ID);
this.signs.Add(sign.net.ID, imageByte);
}
else
Puts("Can not store sign, no texture or image data.");
}
private void restoreSign(Signage sign)
{
FileStorage.server.Remove(sign.textureID, FileStorage.Type.png, sign.net.ID);
sign.textureID = FileStorage.server.Store(this.signs[sign.net.ID], FileStorage.Type.png, sign.net.ID);
sign.SendNetworkUpdate();
}
#endregionThe reason I had to use building blocked instead of OwnerID of the sign is because a player with the IsDeveloper flag is able to unlock and lock a sign which changes the owner to the locking user, allowing evasion of the workaround. Instead players can only change a sign if they are not building blocked.I'm also not usre why I can not call restoreSign in the same frame as OnSignUpdate was called. I've tried a few other methods, like NextFrame but the only way it would definitely revert the sign all the time was to give it a large delay, like a second or so. Another downside of this code is that on a server with a ton of large signs the memory usage of this plugin will grow quickly and loading the plugin initially may take a few seconds but it's an acceptable workaround.