Individual Signs

Sign

Description

This plugin displays the text on signs for each player individual differently.
Right now, it only replaces "[PLAYER]" on the sign with the name of the player looking at it.
However, it also includes an API for developers of other plugins to easily add own, custom player-specific sign content.
So when two players look at a sign at the same time with "[PLAYER]" somewhere on it they will see a different text.
You don't believe me? Then test it out for yourself!
This could for example be used for individual greeting signs at your spawn. Your players will be amazed to see their own name on a sign!
Permission to create a sign with "[PLAYER]" on it: insigns.create.player

How this works

To achieve this the plugin manipulates the sign packet that is sent to a player.
But keep in mind, that each line can still only hold 15 characters. So if the text is longer, it will automatically cut it at the 15's character. If the next lines of the sign are empty, InSigns will try to continue there.

IMPORTANT

This plugin needs ProtocolLib to work.
Make sure you have installed the right version of ProtocolLib.
This plugin should stay compatible with further versions of minecraft and bukkit, as long as ProtocolLib doesn't have to change anything on it's API which InSigns is relying on.

Quick Presentation by VariationVault


For plugin developers: easy-to-use API

Here is a small example of how you can use this in your own plugin to display player-specific values on signs.
1.) First of all: add the Individual_Signs jar to your build path (just like you do it with the bukkit.jar)
2.) You can then create a listener which listens for the SignSendEvent (just like you create listeners for bukkit events): this event gets called every time the server is about to send a player the text of a sign.
3.) The event provides easy methods to get the current sign text and to change it:

  • getPlayer() - Gets the player which receives the sign packet.
  • getLocation() - Gets the location of the sign which text is being sent.
  • getLine(int index) - Gets the line of text at the specified index (0-3).
  • setLine(int index, String line) - Sets the line of text at the specified index.
  • Note that lines longer than the allowed 15 characters will be either cut or continued in the next lines (if those are empty) AFTER the event is over.
  • isModified() - Whether or not this event was already modified by some plugin.
  • setCancelled(boolean cancelled) - If the event is cancelled the sign packet will not be sent to the player, leaving the sign blank.
  • isCancelled() - Checks whether or not some plugin cancelled this event already.

Example usage of the event to replace "[PLAYER]" with the player's name:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class SignSendListener implements Listener {

    public SignSendListener(Plugin plugin) {
        Bukkit.getServer().getPluginManager().registerEvents(this, plugin);
    }

    @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
    public void onSignSend(SignSendEvent event) {
        for (int i = 0; i < 4; i++) {
            String line = event.getLine(i);
            if (line.contains("[PLAYER]")) {
                event.setLine(i, line.replace("[PLAYER]", event.getPlayer().getName()));
            }
        }
    }
}

That's it. The InSigns-Plugin will handle all the needed packet manipulation for you.

Some other useful utilities provided by InSigns are:

  • the static method InSigns.sendSignChange(Player player, Sign sign) - Sends a SignUpdate-Packet to the specified player. Useful if you want to update certain signs periodically.
  • the SimpleChanger class, which can be used to easily create a listener for simple key->value replacements and permission checks during sign creation. Example for the built-in [PLAYER] -> playerName replacement:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Override
public void onEnable() {
    Plugin insignsPlugin = getServer().getPluginManager().getPlugin("InSigns");
    if ((insignsPlugin != null) && insignsPlugin.isEnabled()) {
        // replaces "[PLAYER]" with the player's name on signs and checks for the 'insigns.create.player' permission
        // whenever a player tries to create a sign with "[PLAYER]" on it
        new SimpleChanger(this, "[PLAYER]", "insigns.create.player") {
            @Override
            public String getValue(Player player, Location location, String originalLine) {
                return player.getName();
            }
        };
        System.out.println("Plugin 'InSigns' found. Using it now.");
    } else {
        System.out.println("Plugin 'InSigns' not found. Additional sign features disabled.");
    }
}


Plugins using InSigns

Let me know if your plugin uses InSigns and you want to be mentioned here.

Changelog

changelog

Notice

This plugin uses Hidendra's Metrics class to report usage stats to mcstats.org. This can be disable by setting 'metrics-stats' to false in the config.

Similar plugins

Are you looking for individual player heads? Then IndividualHeads might be the right plugin for you!

You must login to post a comment. Don't have an account? Register to get one!

  • Avatar of PyroKraft PyroKraft May 17, 2014 at 16:56 UTC - 0 likes

    How can i use this plugin for my paintball plugin, what do i need to write in the signs (PLEASE HELP !!!)

  • Avatar of rsod rsod May 02, 2014 at 19:51 UTC - 1 like

    Well, thanks a lot for that API, I was just about to write own method to show individual signs, and I found that plugin, you saved a lot of my time =)

  • Avatar of blablubbabc blablubbabc Apr 20, 2014 at 00:17 UTC - 0 likes

    @xXBadeye: Go

    The error with the + is very likly an issue of your String.matches() method which checks for regexes and the + has a special meaning for regexes.. So you will have to escape that character.

  • Avatar of xXBadeye xXBadeye Apr 19, 2014 at 23:34 UTC - 0 likes

    @blablubbabc: Go

    Well, i hate spaming your comments but i got strange stack traces now. I have now (finaly) got it working, problem was that the plugin loaded BEFORE protocolLib and InSigns and taht i forgot the onEnable part :D Well, one more, strange packet error. The class is still the same as in the commend underneath.

    Stack Trace aaa is being printed out (see class in my last comment), so the getValue is called. Something seems to be wrong with the "+" on the sign, but it should be okay since it is just a string, right?

    Edit: Didn't refreshed the page, your comment came to late, but you figured the problems i had :)

    Edit 2: Well, it worked with removing the "+" from the string check... this is realy weird. So the text got replaced with the correct value, but there are no chat colors yet :D &4 did not work, have you implemented something like that?

    Edit 3: Tryed return (ChatColor.GREEN + "My text"); and it worked :> only problem are the god damm "+" :D

    Last edited Apr 19, 2014 by xXBadeye

    BFAK:100480,49aa403a2fb1dc2e65f6f3e43bf52c5ce23aab7229f6e7208a221b482655a1cf

  • Avatar of blablubbabc blablubbabc Apr 19, 2014 at 23:10 UTC - 0 likes

    @xXBadeye: Go

    Well, you have to call this class inside your onEnable() and you have to softdepend InSigns in your plugin.yml.

    Between, if you have some sort of list of all your possible weapons you could replace your repeating code lines with a loop.

  • Avatar of xXBadeye xXBadeye Apr 19, 2014 at 21:41 UTC - 0 likes

    @blablubbabc: Go

    Wrote my code, it should work but even my debug statements are not being called. Do i have to register the event in my onEnable main class?

    Sign class, i have not modified anything in other classes after using this:

    package me.badeye.plugins.lapuaz;
    
    import de.blablubbabc.insigns.*;
    
    import org.bukkit.Location;
    import org.bukkit.Material;
    import org.bukkit.block.Block;
    import org.bukkit.block.Sign;
    import org.bukkit.entity.Player;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.plugin.Plugin;
    
    public class SignSendListener {
        
        public SignSendListener(Plugin insignsPlugin) {
        	System.out.println("vvvv");
            InSigns insigns = (InSigns) insignsPlugin;
            insigns.addChanger(new Changer("[BUY]", "p") {
            	
            @Override
                public String getValue(Player p, Location signLocation) {
            		System.out.println("aaa");
            		Block locBlock = p.getWorld().getBlockAt(signLocation);
            		Sign sign = (Sign) locBlock.getState();
            		String line_0 = sign.getLine(0);
          		      	String line_1 = sign.getLine(1);
          		      	String line_2 = sign.getLine(2);
          		      	String line_3 = sign.getLine(3);
          		      	
          		      	//if item was bought already
          		      	if(p.hasPermission("LapuaZ.kit.makarov1") && line_2.matches("+1 Makarov Mag") && line_3.matches(""))	return ("&aPayed 100 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.makarov2") && line_2.matches("+1 Makarov Mag") && line_3.matches(" ")) return ("&aPayed 150 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.shot1") && line_2.matches("+16 Pellets") && line_3.matches("")) 		return ("&aPayed 200 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.shot2") && line_2.matches("+16 Pellets") && line_3.matches(" ")) 		return ("&aPayed 300 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.bandage1") && line_2.matches("+1 Bandage") && line_3.matches("")) 	return ("&aPayed 100 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.bandage2") && line_2.matches("+1 Bandage") && line_3.matches(" ")) 	return ("&aPayed 150 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.steak1") && line_2.matches("+1 Steak") && line_3.matches("")) 			return ("&aPayed 100 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.steak2") && line_2.matches("+1 Steak") && line_3.matches(" ")) 			return ("&aPayed 150 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.morphine1") && line_2.matches("+1 Morphine") && line_3.matches("")) 	return ("&aPayed 200 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.morphine2") && line_2.matches("+1 Morphine") && line_3.matches(" ")) 	return ("&aPayed 300 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.remington") && line_2.matches("Remington") && line_3.matches("")) 		return ("&aPayed 1000 BD");
          		      	else if(p.hasPermission("LapuaZ.kit.crowbar") && line_2.matches("Crowbar") && line_3.matches("")) 		return ("&aPayed 600 BD");
          		      	
          		      	//not bought yet
          		      	else if(!p.hasPermission("LapuaZ.kit.makarov1") && line_2.matches("+1 Makarov Mag") && line_3.matches("")) return ("&4Buy 100 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.makarov2") && line_2.matches("+1 Makarov Mag") && line_3.matches(" ")) return ("&4Buy 150 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.shot1") && line_2.matches("+16 Pellets") && line_3.matches("")) 		return ("&4Buy 200 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.shot2") && line_2.matches("+16 Pellets") && line_3.matches(" ")) 		return ("&4Buy 300 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.bandage1") && line_2.matches("+1 Bandage") && line_3.matches("")) 	return ("&4Buy 100 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.bandage2") && line_2.matches("+1 Bandage") && line_3.matches(" ")) 	return ("&4Buy 150 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.steak1") && line_2.matches("+1 Steak") && line_3.matches("")) 			return ("&4Buy 100 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.steak2") && line_2.matches("+1 Steak") && line_3.matches(" ")) 		return ("&4Buy 150 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.morphine1") && line_2.matches("+1 Morphine") && line_3.matches("")) 	return ("&4Buy 200 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.morphine2") && line_2.matches("+1 Morphine") && line_3.matches(" ")) 	return ("&4Buy 300 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.remington") && line_2.matches("Remington") && line_3.matches("")) 	return ("&4Buy 1000 BD");
        		      	else if(!p.hasPermission("LapuaZ.kit.crowbar") && line_2.matches("Crowbar") && line_3.matches("")) 		return ("&4Buy 600 BD");
            		
                    return "contact admin";
                }
            });
        }
    }
    

    i know awful if statement chain, but sadly it is needed :/ Working with those permissions when executing commands and giving players kits.

    Pastebin code, the statements are that long that they are messing up the formating :D

    Last edited Apr 19, 2014 by xXBadeye
  • Avatar of xXBadeye xXBadeye Apr 19, 2014 at 19:36 UTC - 0 likes

    @blablubbabc: Go

    Hah okay thank you, i assumed that aswell but i had no idea how the changer works :) Nice api, then, perfectly suits my needs!

  • Avatar of blablubbabc blablubbabc Apr 19, 2014 at 19:14 UTC - 1 like

    @xXBadeye: Go

    That's exactly what the Changer is for: you simple have to replace the "[PLAYER]" from the example to your own key (which gets replaced), and change the permissions node from 'insigns.create.player' to your own as well, and inside the getValue() method you insert your own logic and return the string which shall be displayed to the player.

    import me.blablubbabc.insigns.Changer;
    import me.blablubbabc.insigns.InSigns;
    import org.bukkit.plugin.Plugin;
    
    public class InSignsFeature {
        
        public InSignsFeature(Plugin insignsPlugin) {
            
            InSigns insigns = (InSigns) insignsPlugin;
            insigns.addChanger(new Changer("[HAS_BOUGHT]", "myplugin.createsign.hasbought") {
    
                @Override
                public String getValue(Player player, Location signLocation) {
                    if (myConfig.getStringList("thesePlayerHaveBought.itemX").contains(player.getName())) return "yeay!";
                    else "ney!";
                }
    
            });
        }
    }
    
    Last edited Apr 19, 2014 by blablubbabc
  • Avatar of xXBadeye xXBadeye Apr 19, 2014 at 18:58 UTC - 0 likes

    @blablubbabc: Go Thanks alot :>

    Ah, one last question: Let's say i want to modify [PLAYER] to a custom variable, lets say to the String "hi". How would i do that? That way i could for example use my self written sign shop component of my plugin, get a value from the config/hashmap if the player has bought the item and depending on if the palyer has bought the item or not the [PLAYER] (or something else) on the sign gets changed to "Already bought" or "Buy this"

    Last edited Apr 19, 2014 by xXBadeye
  • Avatar of blablubbabc blablubbabc Apr 19, 2014 at 18:51 UTC - 1 like

    @xXBadeye: Go

    Ah, I now know why it suggest that import: actually I already included a first draft of a possible SignSendEvent class in that version, but it wasn't yet used (that's why I moved it into the 'unused' package..)

    I updated the example in the comment below: You basicly have to get the InSigns plugin instance just like in the example with the 'SimpleChanger' above, but instead of creating a new 'SimpleChanger' you cast the plugin to 'InSigns' and use the 'addChanger' method to add a new 'Changer' object and fill in the getValue() method for that Changer object.

    InSigns will then check the permission you provide it whenever a player tries to create a sign with the key on it (the key is the first string argument, the one which gets replaced with what your custom getValue() method returns).

    Edit: Here is the old project page, which has the example for the old API: http://dev.bukkit.org/bukkit-plugins/individual-signs/pages/main/r65/

    Last edited Apr 19, 2014 by blablubbabc

Facts

Date created
Sep 18, 2012
Category
Last update
Jan 14, 2014
Development stage
Release
Language
  • deDE
  • enUS
License
All Rights Reserved
Curse link
Individual Signs
Downloads
15,014
Recent files

Authors

Relationships

Required dependency
ProtocolLib