Thread: [Elvarg/Any] OO Commands

Results 1 to 9 of 9
  1. #1 [Elvarg/Any] OO Commands 
    What's a sundial in the shade?

    Lumiere's Avatar
    Join Date
    May 2013
    Age
    27
    Posts
    543
    Thanks given
    224
    Thanks received
    100
    Rep Power
    113
    Spoiler for Command:

    Code:
    /**
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    @CommandConfig(
    		validCallArray = {}, toggleCommand = false 
    )
    public abstract class Command {
    	
    	/**
    	 * The {@link Player} entity requesting to execute the {@link Command}
    	 */
    	private final Player player;
    	
    	/**
    	 * The input of the {@link #player}
    	 */
    	private final String command;
    	
    	/**
    	 * Determines whether or not the {@link #command} came from the client command console
    	 */
    	private final boolean fromConsole;
    	
    	/**
    	 * @Return {@link #getClassListFromClasspath} with input parameter: "com.helios.net.packet.impl.commands.impl" 
    	 */
    	public static final List<Class<?>> getCommandClasses() {
    		return Misc.getClassListFromClasspath( "com.helios.net.packet.impl.commands.impl" );
    	}
    	
    	/**
    	 * Constructs the {@link Command} with the following parameters
    	 * 
    	 * @Param player
    	 * 		the {@link Player} entity
    	 * @Param command
    	 * 		the input of the {@link player}
    	 * 
    	 * @Param fromConsole
    	 * 		whether or not the {@link command} came from the client command console
    	 */
    	public Command(Player player, String command, boolean fromConsole) {
    		this.player = player;
    		this.command = command;
    		this.fromConsole = fromConsole;
    	}
    	
    	/**
    	 * Gets the {@link #player} requesting to execute the {@link Command}
    	 * 
    	 * @Return 
    	 * 		the {@link #player}
    	 */
    	public final Player getPlayer() {
    		return player;
    	}
    	
    	/**
    	 * Gets the {@link #command} input of the {@link #player}
    	 * 
    	 * @Return
    	 * 		the {@link #command}
    	 */
    	public final String getCommand() {
    		return command;
    	}
    	
    	/**
    	 * Gets the {@link #command} input of the {@link #player} split by spaces, as an array
    	 * 
    	 * @Return
    	 * 		the {@link #command} input split by spaces, as an array
    	 */
    	public final String[] getSpaceSplitCommand() {
    		return command.split(" ");
    	}
    	
    	/**
    	 * Gets the {@link #command} input of the {@link #player} split by hyphens, as an array
    	 * 
    	 * @Return
    	 * 		the {@link #command} input split by hyphens, as an array
    	 */
    	public final String[] getHyphenSplitCommand() {
    		return command.split("-");
    	}
    	
    	/**
    	 * @Return @True if the {@link #command} came from the client command console
    	 * 
    	 * @Return @False otherwise
    	 */
    	public final boolean fromConsole() {
    		return fromConsole;
    	}
    	
    	/**
    	 * The default function to send messages to the {@link #player} containing the correct syntax for the {@link #Command}
    	 * 
    	 * @Return @False
    	 */
    	public final boolean invalidCommandSyntax(final String error) {
    		
    		final CommandConfig annotation = getClass().getAnnotation(CommandConfig.class);
    		player.getPacketSender().sendMessage( "<img=12> Command input error! " + error );
    		player.getPacketSender().sendMessage( "<img=12> Valid calls: \"" + String.join( "\", \"", annotation.validCallArray() ) + "\"" );
    		
    		if ( !Arrays.asList(annotation).isEmpty() )
    			player.getPacketSender().sendMessage( "<img=12> Example syntax: \"" + String.join( "\", \"", annotation.exampleSyntaxArray() ) + "\"" );
    		
    		return false;
    	}
    	
    	/**
    	 * The default and finalized function to request the {@link Command} be executed
    	 * 
    	 * @Return
    	 * 		{@link #execute()} if {@link #isEligable()} and {@link #isValid()} returns true
    	 */
    	public final Command request() {
    		return ( isEligable() && isValid() ) ? execute() : this;
    	}
    
    	/**
    	 * The default function to determine whether or not the {@link #command} input is valid
    	 * <br> Should be overridden by extending classes if more specifications/restrictions are required
    	 * 
    	 * @Return @False if the length the {@link #command} input split by spaces as an array, is below the {@link #minimumLength()}
    	 * 
    	 * @Return @True otherwise
    	 */
    	public boolean isValid() {
    		
    		if ( getSpaceSplitCommand().length < minimumLength() )
    			return invalidCommandSyntax( "Invalid length! Arguments missing." );
    		
    		final CommandConfig annotation = getClass().getAnnotation(CommandConfig.class);
    		if ( getSpaceSplitCommand().length > 1 && Arrays.asList(annotation.exampleSyntaxArray()).isEmpty() )
    			getPlayer().getPacketSender().sendMessage( "<img=12> Note: Command executed functions without arguments." );
    		
    		return true;
    	}
    	
    	/**
    	 * The default function to determine whether or not a {@link #player} is eligable to request the {@link Command} be executed
    	 * <br> Should be overridden by extending classes if more privileges are required
    	 * 
    	 * @Return @False if the {@link #eligableRank()} array does not contain the {@link PlayerRights} of the {@link #player}
    	 * 
    	 * @Return @True otherwise
    	 */
    	public boolean isEligable() {
    		
    		if ( Arrays.stream( eligableRank() ).anyMatch( rights -> player.getRights() == rights ) )
    			return true;
    
    		player.getPacketSender().sendMessage( "Sorry, but it appears you are not eligable to use this command.", fromConsole );
    		return false;
    	}
    	
    	/**
    	 * The default array of eligable {@link PlayerRights}
    	 * <br> Should be overridden by extending classes if specific privileges should be required
    	 * 
    	 * @Return
    	 * 		an array of all {@link PlayerRights}
    	 */
    	public PlayerRights[] eligableRank() {
    		return PlayerRights.values();
    	}
    	
    	/**
    	 * The default minimum length that the {@link #command} input as an array should be
    	 * 
    	 * @Return the default minimum length: 1
    	 */
    	public int minimumLength() {
    		return 1;
    	}
    	
    	/**
    	 * The function to execute upon successful request of a {@link Command}
    	 */
    	public abstract Command execute();
    	
    }

    Spoiler for CommandConfig:

    Code:
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
     
    /**
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server Profile</a> 
     */
     @target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CommandConfig{
    	
    	String[] validCallArray();
    	
    	String[] exampleSyntaxArray() default {};
    	
    	boolean toggleCommand() default true;
    	
    }


    Spoiler for CommandPacketListener:

    Code:
    /** 
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * 		<br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    public class CommandPacketListener implements PacketListener {
    	
    	@Override
    	public final void handlePacket(Player player, Packet packet) {
    		final boolean fromConsole = (packet.readByte() == 1); 
    		final String commandInput = packet.readString();
    		final String commandCall = commandInput.split( " " )[0].toLowerCase();
    
    		if( player == null || player.getHitpoints() <= 0 )
    			return;
    		
    		if ( commandInput.length() <= 0 || commandInput.contains( "\r" ) || commandInput.contains( "\n" ) )
    			return;
    		
    		if( commandInput.startsWith("/") && commandInput.length() >= 1 ) {
    			final String message = commandInput.substring(1, commandInput.length());
    			ClanChatManager.sendMessage(player, message);
    			return;
    		}
    		try {
    			final Optional<Entry<Class<?>, Boolean>> commandEntry = GameBuilder.getCommandEntryFor(commandCall);
    			
    			if ( !commandEntry.isPresent() || commandEntry.get().getKey() == null || commandEntry.get().getValue() == null )
    				return;
    			
    			if ( !commandEntry.get().getValue() ) {
    				final String className = commandEntry.get().getKey().getSimpleName().replace("Command", "").replaceAll("(.)([ A-Z])", "$1 $2");
    				player.getPacketSender().sendMessage( "The \"" + className + "\" command is currently disabled.", fromConsole );
    				return;
    			}
    			final Constructor<?> constructor = commandEntry.get().getKey().getConstructor(Player.class, String.class, boolean.class);
    			commandEntry.get().getKey().getMethod("request").invoke( constructor.newInstance(player, commandInput, fromConsole) );
    		} catch( Exception exception ) {
    			exception.printStackTrace();
    			exception.getCause();
    		}
    		return;
    	}	
    }


    Spoiler for Command Classes:

    Will add more.
    Spoiler for TestCommand:

    Code:
    /**
     * A {@link Command} subclass that prints a message to the {@link Player} upon successful execution for the purpose of testing
     * 
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    @CommandConfig(
    		validCallArray = { "test", "testcmd", "testing", "testcommand" }
    )
    public final class TestCommand extends Command {
    
    	/**
    	 * @see {@link #Command(Player, String, boolean)} for constructor details
    	 */
    	public TestCommand(Player player, String command, boolean fromConsole) {
    		super(player, command, fromConsole);
    	}
    
    	@Override
    	public final Command execute() {
    		getPlayer().getPacketSender().sendMessage( "'Test' command successful.", fromConsole() );
    		getPlayer().getPacketSender().sendMessage( "'Test' command input: " + getCommand(), fromConsole()  );
    		return this;
    	}
    	
    }


    Spoiler for ToggleCmdCommand:

    Code:
    /** 
     * A {@link Command} subclass that changes the stored value of {@link CommandConfig#toggleCommand()} 
     *  for another {@link Command} subclass
     * 
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    @CommandConfig(
    		validCallArray = { "togglecmd", "disablecmd", "enablecmd" },
    		exampleSyntaxArray = { "togglecmd validCall", "togglecmd item", "togglecmd spawnitem" }
    )
    public final class ToggleCmdCommand extends Command {
    
    	/**
    	 * @see {@link #Command(Player, String, boolean)} for constructor details
    	 */
    	public ToggleCmdCommand(Player player, String command, boolean fromConsole) {
    		super(player, command, fromConsole);
    	}
    
    	@Override
    	public final Command execute() {
    		final String commandCall = getSpaceSplitCommand()[0];
    		final String desiredCommand = getSpaceSplitCommand()[1];
    		final Optional<Entry<Class<?>, Boolean>> entry =  GameBuilder.getCommandEntryFor(desiredCommand);
    		
    		if ( entry.isPresent() ) {
    			final boolean isToggled = entry.get().getValue();
    			final boolean toggleDisable = commandCall.contains("toggle") && isToggled;
    			final String className = entry.get().getKey().getSimpleName().replaceAll("Command", "").replaceAll("(.)([ A-Z])", "$1 $2");
    			final String toggleType = (toggleDisable || commandCall.contains( "disable" )) ? "Disabling \"" : "Enabling \"";
    			
    			if ( (isToggled && commandCall.contains("enable")) || (!isToggled && commandCall.contains("disable")) ) {
    				final String currentType = (isToggled ? "enabled" : "disabled") + "!";
    				getPlayer().getPacketSender().sendMessage( "The \"" + className + "\" command is already " + currentType );
    				return this;
    			}
    			getPlayer().getPacketSender().sendMessage( toggleType + className + "\" command..." , fromConsole() );
    			entry.get().setValue( commandCall.contains("toggle") ? !isToggled : commandCall.contains("enable") );
    			return this;
    		}
    		return this;
    	}
    	
    	@Override
    	public final int minimumLength() {
    		return 2;
    	}
    	
    }


    Spoiler for AnimationCommand:

    Code:
    /** 
     * A {@link Command} subclass that executes an {@link Animation} for a {@link Player}
     * 
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    @CommandConfig(
    		validCallArray = { "anim", "animation" },
    		exampleSyntaxArray = { "anim 1", "anim 100" }
    )
    public final class AnimationCommand extends Command {
    	
    	/**
    	 * @see {@link #Command(Player, String, boolean)} for constructor details
    	 */
    	public AnimationCommand(Player player, String command, boolean fromConsole) {
    		super(player, command, fromConsole);
    	}
    
    	@Override
    	public final boolean isValid() {
    		
    		if ( !StringUtils.isNumeric(getSpaceSplitCommand()[1]) )
    			return invalidCommandSyntax( "Animation ID(args 1) must be an integer!" );
    		
    		return super.isValid();
    	}
    
    	@Override
    	public final Command execute() {
    		final int animationId = Integer.parseInt(getSpaceSplitCommand()[1]);
    		final Animation animation = new Animation(animationId);
    		getPlayer().performAnimation(animation);
    		return this;
    	}
    	
    	@Override
    	public final PlayerRights[] eligableRank() {
    		return PlayerRights.getStaffArray();
    	}
    	
    	@Override
    	public final int minimumLength() {
    		return 2;
    	}
    	
    }


    Spoiler for ChatInterfaceCommand:

    Code:
    /** 
     * A {@link Command} subclass that opens a chat interface for a {@link Player}
     * 
     * @author Elliot Hayes - "Lumiere" <br> [email protected]
     * <br> <a href="https://www.rune-server.ee/members/lumiere/">Rune-Server</a> 
     */
    @CommandConfig(
    		validCallArray = { "cint", "cinterface", "chatint", "chatinterface"  },
    		exampleSyntaxArray = { "cint 1", "cint 100" }
    )
    public final class ChatInterfaceCommand extends Command {
    
    	/**
    	 * @see {@link #Command(Player, String, boolean)} for constructor details
    	 */
    	public ChatInterfaceCommand(Player player, String command, boolean fromConsole) {
    		super(player, command, fromConsole);
    	}
    
    	public final boolean isValid() {
    		
    		if ( !StringUtils.isNumeric(getSpaceSplitCommand()[1]) )
    			return invalidCommandSyntax( "Interface ID(args 1) must be an integer!" );
    		
    		return super.isValid();
    	}
    
    	@Override
    	public final Command execute() {
    		
    		if ( Integer.parseInt( getSpaceSplitCommand()[1] ) == 30000 )
    			getPlayer().getPacketSender().sendItemOnInterface( 30005, 1129, 1 );
    		
    		getPlayer().getPacketSender().sendChatboxInterface( Integer.parseInt( getSpaceSplitCommand()[1] ) );
    		return this;
    	}
    	
    	@Override
    	public final PlayerRights[] eligableRank() {
    		return PlayerRights.getStaffArray();
    	}
    	
    	@Override
    	public final int minimumLength() {
    		return 2;
    	}
    
    }


    Last edited by Lumiere; 07-17-2020 at 05:28 AM.

    Spoiler for Revy is perfect:
    Reply With Quote  
     

  2. #2  
    Donator


    Join Date
    Jul 2011
    Posts
    921
    Thanks given
    199
    Thanks received
    178
    Rep Power
    189
    How are the commands stored/loaded?

    Few suggestions:

    I would recommend the usage of an annotation, which contains the data for the command such as:
    - The actual access command (could be multiple per command eg. item, spawn, pickup).
    - Description of what the command does.
    - Basic description of parameters/usage (Ex. ::getid [item name] (some optional param))

    The data above can be really useful for ::commands command.

    Also if using annotations you will be able to load them dynamically, making the creation of commands and disabling them a lot easier.
    Reply With Quote  
     

  3. #3  
    What's a sundial in the shade?

    Lumiere's Avatar
    Join Date
    May 2013
    Age
    27
    Posts
    543
    Thanks given
    224
    Thanks received
    100
    Rep Power
    113
    Unfortunately via classpath right now...
    Would like to be able to load classes during runtime, a plugin system more or less, but have failed to do so at every attempt, so I've just accepted this as is


    EDIT: Done did all that jazz, will update soon
    Last edited by Lumiere; 07-14-2020 at 03:54 PM.

    Spoiler for Revy is perfect:
    Reply With Quote  
     

  4. #4  
    Donator


    Join Date
    Jul 2011
    Posts
    921
    Thanks given
    199
    Thanks received
    178
    Rep Power
    189
    Quote Originally Posted by Lumiere View Post
    Unfortunately via classpath right now...
    Would like to be able to load classes during runtime, a plugin system more or less, but have failed to do so at every attempt, so I've just accepted this as is


    EDIT: Done did all that jazz, will update soon
    Use the Reflections api
    Code:
    new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)
    Reply With Quote  
     

  5. #5  
    Registered Member

    Join Date
    Feb 2010
    Posts
    3,253
    Thanks given
    1,145
    Thanks received
    909
    Rep Power
    2081
    wow object orientation on crack
    Reply With Quote  
     

  6. Thankful users:


  7. #6  
    What's a sundial in the shade?

    Lumiere's Avatar
    Join Date
    May 2013
    Age
    27
    Posts
    543
    Thanks given
    224
    Thanks received
    100
    Rep Power
    113
    Quote Originally Posted by Blaketon View Post
    Use the Reflections api
    Code:
    new Reflections("my.package").getTypesAnnotatedWith(MyAnnotation.class)
    Yeh that's what I was working on, among other things hahah, but had to learn a bit about annotations and what not first...
    However, I'm probably wrong but from what I can tell, I can only load the data in a .class file or one from a .jar thats in the library/classpath of the source
    Though, I did try making a runtime class loader to load classes from an external file, the idea being that the commands could be loaded/stored dynamically, basically as plugins
    And so that you could add a jar/class file to the external folder and load/access it via reflections or something during runtime, loop through the directory and get all the files dynamically something like that... I was able to kinda put it together in multiple attempts, but yeh, that's ultimately what I mean I failed at doing, and by "currently loading via classpath" as I mean, most things are
    Am updating now with Annotations


    Quote Originally Posted by Blacklist View Post
    wow object orientation on crack
    Care to elaborate? Is this bad practice? Neat class files that can be told apart by the name of the class, containing only the data for that specific command, and generally only a few couple-liner methods. Only the execute() method and constructor needs to be defined, and the request method is called when a players input matches the valid calls of the command in the packet listener...
    The request method calls the methods to check the length and whatever else necessary, which can be overridden too
    I prefer this over a ton of if statements and clutter in the packet listener... Though, I'm still learning, and I'm open to being educated.

    However, considering this is pretty much exactly the same as the thread by A NULL, in which you said "Nice idea, seems overcomplicated but thanks for release", except this is far cleaner, and happens to already have commands written, seems like it might just be a bias comment.
    If you can explain how this is "On crack" which implies its bad, then I'd love to to the time to understand.
    Last edited by Lumiere; 07-17-2020 at 04:53 PM.

    Spoiler for Revy is perfect:
    Reply With Quote  
     

  8. #7  
    Registered Member
    Join Date
    May 2011
    Posts
    16
    Thanks given
    89
    Thanks received
    0
    Rep Power
    1
    Jesus Christ. I will never understand why people always obsess over OOP so much.
    Reply With Quote  
     

  9. #8  
    Registered Member
    Optimum's Avatar
    Join Date
    Apr 2012
    Posts
    3,564
    Thanks given
    871
    Thanks received
    1,745
    Rep Power
    5000
    Quote Originally Posted by hausdrache5 View Post
    Jesus Christ. I will never understand why people always obsess over OOP so much.
    Pi didn't focus on oop, look where it's at

    Quote Originally Posted by DownGrade View Post
    Don't let these no life creeps get to you, its always the same on here. They'd rather spend hours upon hours in the rune-server spam section then getting laid! ha ha!Its honestly pathetic i haven't seen so many lowlifes in my life its actually insane i wish that this section would just vanish its probably the only way to get these people out of the community...
    PLEASE BE AWARE OF IMPOSTERS MY DISCORD ID: 362240000760348683
    Reply With Quote  
     

  10. #9  
    Registered Member

    Join Date
    Feb 2010
    Posts
    3,253
    Thanks given
    1,145
    Thanks received
    909
    Rep Power
    2081
    Quote Originally Posted by Lumiere View Post
    Yeh that's what I was working on, among other things hahah, but had to learn a bit about annotations and what not first...
    However, I'm probably wrong but from what I can tell, I can only load the data in a .class file or one from a .jar thats in the library/classpath of the source
    Though, I did try making a runtime class loader to load classes from an external file, the idea being that the commands could be loaded/stored dynamically, basically as plugins
    And so that you could add a jar/class file to the external folder and load/access it via reflections or something during runtime, loop through the directory and get all the files dynamically something like that... I was able to kinda put it together in multiple attempts, but yeh, that's ultimately what I mean I failed at doing, and by "currently loading via classpath" as I mean, most things are
    Am updating now with Annotations



    Care to elaborate? Is this bad practice? Neat class files that can be told apart by the name of the class, containing only the data for that specific command, and generally only a few couple-liner methods. Only the execute() method and constructor needs to be defined, and the request method is called when a players input matches the valid calls of the command in the packet listener...
    The request method calls the methods to check the length and whatever else necessary, which can be overridden too
    I prefer this over a ton of if statements and clutter in the packet listener... Though, I'm still learning, and I'm open to being educated.

    However, considering this is pretty much exactly the same as the thread by A NULL, in which you said "Nice idea, seems overcomplicated but thanks for release", except this is far cleaner, and happens to already have commands written, seems like it might just be a bias comment.
    If you can explain how this is "On crack" which implies its bad, then I'd love to to the time to understand.
    You might note I also said Null's system was massively overcomplicated. What is need of all this for something as trivial as commands? Aren't our servers already complicated enough without a gazillion classes everytime someone types :: ?

    Cool you're learning about OOP and collections, part of that learning is learning when they're appropriate to use and not just sticking them everywhere. You'll get there eventually.
    Reply With Quote  
     


Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 42
    Last Post: 09-07-2017, 02:54 AM
  2. [ANY] Better Command System
    By insyane in forum Snippets
    Replies: 10
    Last Post: 04-30-2015, 09:54 AM
  3. [PI/Any] Math Command
    By Uavix in forum Snippets
    Replies: 16
    Last Post: 02-28-2014, 08:50 AM
  4. [ALMOST ANY] Suggestion Command
    By Ash Ketchum in forum Snippets
    Replies: 7
    Last Post: 08-30-2013, 01:55 AM
  5. Adding a reset any stat command.
    By Moronic in forum Tutorials
    Replies: 9
    Last Post: 08-23-2010, 03:05 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •