Thread: Instanced Area Design

Page 1 of 4 123 ... LastLast
Results 1 to 10 of 38
  1. #1 Instanced Area Design 
    Donator

    Jason's Avatar
    Join Date
    Aug 2009
    Posts
    6,092
    Thanks given
    2,402
    Thanks received
    2,823
    Rep Power
    4550
    Hello,

    Although the title may be misleading, this tutorial is not about creating map chunks and managing those instances. This tutorial will be about how to create and manage areas, particularly for a single player. Although I can't say i've done a lot of production testing, a server is currently using this to manage a 'single boss instance' related piece of content and it is functioning properly. I am entirely open to any constructive criticism.

    Code:
    public enum InstancedType {
        
        PSEUDO_BOSS_INSTANCE
    
    
    }
    Code:
    public abstract class InstancedArea {
        
        /**
         * The type of instance, defining this area as unique to other different types.
         */
        protected final InstancedType type;
    
    
        /**
         * The boundary or location for this instanced area
         */
        protected final Boundary boundary;
    
    
        /**
         * The height of this area.
         */
        protected final int height;
        
        /**
         * The state that this area is in, why by default is {@link InstancedAreaState#AVAILABLE}.
         */
        private InstancedAreaState state = InstancedAreaState.AVAILABLE;
        
        /**
         * Creates a new area with a boundary for the given type.
         * 
         * <p>If {@link InstancedAreaManager#getNextOpenHeight(InstancedType)} throws an 
         * {@link AreaUnavailableException} then the area should not be used. However, that
         * is up to the implementor to decide. 
         * 
         * @param type        
         *               the type of instanced area, defining this as unique.
         * @param boundary    
         *               the boundary or location.
         */
        public InstancedArea(InstancedType type, Boundary boundary) throws AreaUnavailableException {
            this.type = type;
            this.boundary = boundary;
            this.height = InstancedAreaManager.getSingleton().getNextOpenHeight(type);
        }
        
        /**
         * Changes the current state of this area to a new state. If the parameter
         * 'state' is null, a {@link NullPointerException} will be thrown and should
         * be handled although this is not enforced. If the current state is that of
         * {@link InstancedAreaState#DISPOSABLE} and we attempt to change it by
         * referencing this function, an {@link IllegalStateException} will be thrown.
         * 
         * @param state
         *            the new state of this area.
         * 
         * @throws IllegalStateException
         *             thrown when the current state is
         *             {@link InstancedAreaState#DISPOSABLE}.
         */
        public void setState(InstancedAreaState state) throws IllegalStateException {
            Objects.requireNonNull(state);
    
    
            if (this.state == InstancedAreaState.DISPOSED || this.state == InstancedAreaState.DISPOSABLE
                    && state == InstancedAreaState.AVAILABLE) {
                throw new IllegalStateException(
                        "The current state is '" + state.name() + "', the state cannot be changed.");
            }
    
    
            this.state = state;
        }
        
        /**
         * Determines if this area is disposable. This method can, and most of the
         * time, should be overriden for additional functionality. The
         * {@link SingleInstancedArea} class for example that subclasses this class
         * overrides the method to ensure that if the player is unavailable then the
         * instance should be too. It is worth noting as a reminder that if you do
         * override this method that you should refer to the superclass method.
         * 
         * @return {@code true} if the area can be disposed of by the manager,
         *         otherweise {@code false}.
         */
        public boolean isDisposable() {
            if (state == InstancedAreaState.DISPOSABLE) {
                return true;
            }
            
            return false;
        }
        
        /**
         * Referenced when an area is created.
         */
        public abstract void create();
        
        /**
         * Referenced when an area is to be disposed.
         */
        public abstract void dispose();
        
        /**
         * Referenced every game cycle.
         */
        public abstract void pulse();
        
        /**
         * A method that returns the current state of this area. By
         * default the state is {@link InstancedAreaState#AVAILABLE}.
         * 
         * @return the state of the area.
         */
        public final InstancedAreaState getState() {
            return state;
        }
        
        /**
         * A convenience function that determines if the current state
         * is that of {@link InstancedAreaState#DISPOSED}.
         * 
         * @return {@code true} if disposed, otherwise {@code false}.
         */
        public final boolean isDisposed() {
            return state == InstancedAreaState.DISPOSED;
        }
        
        /**
         * The type of instanced area that this is. Each instance is defined 
         * by a type that separates this instance from others, making it unique.
         * 
         * @return    the type of instance.
         */
        public InstancedType getType() {
            return type;
        }
    
    
        /**
         * Determines the height of this area.
         * 
         * @return    the height
         */
        public int getHeight() {
            return height;
        }
    
    
        /**
         * The boundary or location of this instanced area
         * @return    the boundary
         */
        public Boundary getBoundary() {
            return boundary;
        }
    
    
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof InstancedArea) {
                InstancedArea other = (InstancedArea) obj;
                return height == other.height && boundary.equals(other.boundary)
                        && type == other.type;
            }
    
    
            return false;
        }
    
    
        @Override
        public int hashCode() {
            return 31 * height + boundary.hashCode() + type.hashCode();
        }
    
    
    }
    Code:
    public class InstancedAreaManager { 
    	
    	/**
    	 * A single instance of this class for global usage
    	 */
    	private static final InstancedAreaManager SINGLETON = new InstancedAreaManager();
    	
    	/**
    	 * The minimum height that an instanced area can control.
    	 */
    	private static final int MINIMUM_HEIGHT = 4;
    
    
    	/**
    	 * The maximum height of any one instance which is currently 1024.
    	 */
    	private static final int MAXIMUM_HEIGHT = 1024;
    	
    	/**
    	 * A mapping of all {@InstancedArea} objects that are being operated on
    	 * and are active.
    	 */
    	private Map<InstancedType, Map<Integer, InstancedArea>> active = new HashMap<>();
    	
    	/**
    	 * The task that manages instanced areas.
    	 */
    	private final InstancedAreaTask task = new InstancedAreaTask();
    	
    	/**
    	 * A private empty {@link InstancedAreaManager} constructor exists to ensure that
    	 * no other instance of this class can be created from outside this class.
    	 */
    	private InstancedAreaManager() { 
    		for (InstancedType type : InstancedType.values()) {
    			active.put(type, new HashMap<>());
    		}
    		Server.getTaskScheduler().schedule(task);
    	}
    	
    	/**
    	 * Appends the {@link InstancedArea} object to the mapping for the given height.
    	 * 
    	 * <p>It should be noted that if the given parameter reference is null, a {@link NullPointerException}
    	 * will be thrown.</p>
    	 *  
    	 * @param area
    	 * 			  the area that is being appended to the active list of instanced areas.
    	 */
    	public void append(InstancedArea area) {
    		Preconditions.checkNotNull(area, "Area isn't permitted to be null.");
    		Map<Integer, InstancedArea> areas = active.getOrDefault(area.getType(), new HashMap<>());
    		
    		areas.put(area.getHeight(), area);
    		active.put(area.getType(), areas);
    	}
    	
    	/**
    	 * This method utilizes {@link #append(InstancedArea)} in conjunction with
    	 * {@link InstancedArea#create()} to append and create a new instanced area.
    	 * 
    	 * @param area
    	 *            the area being instanced.
    	 */
    	public void appendAndCreate(InstancedArea area) {
    		append(area);
    		area.create();
    	}
    
    
    	/**
    	 * Retrieves an open height level by sifting through the mapping and
    	 * attempting to retrive the lowest height level.
    	 * 
    	 * @return the next lowest, open height level will be returned otherwise an
    	 * 		   exception is thrown. When an exception is thrown, it signifies that
    	 * 		   there are no heights open from 0 to {@link MAXIMUM_HEIGHT}. 
    	 */
    	int getNextOpenHeight(InstancedType type) throws AreaUnavailableException {
    		Map<Integer, InstancedArea> areas = active.getOrDefault(type, new HashMap<>());
    	
    		for (int height = MINIMUM_HEIGHT; height < MAXIMUM_HEIGHT; height += 4) {
    			if (!areas.containsKey(height)) {
    				return height;
    			}
    		}
    		
    		throw new AreaUnavailableException();
    	}
    	
    	/**
    	 * Retrieves the single instance of this class.
    	 * 
    	 * @return	the single instance.
    	 */
    	public static InstancedAreaManager getSingleton() {
    		return SINGLETON;
    	}
    	
    	/**
    	 * An object that represents a task that will periodically (defined by {@link #EXECUTE_DELAY}) execute.
    	 * The objective of the execute function is to dispose of unnecessary instances and pulse otherwise.
    	 */
    	private final class InstancedAreaTask extends Task {
    		
    		/**
    		 * The amount of game ticks that pass before {@link #execute()} is referenced.
    		 */
    		private static final int EXECUTE_DELAY = 2;
    
    
    		/**
    		 * Creates a new listener for the manager.
    		 */
    		public InstancedAreaTask() {
    			super(EXECUTE_DELAY);
    		}
    
    
    		@Override
    		public void execute() {
    			for (Entry<InstancedType, Map<Integer, InstancedArea>> mapping : active.entrySet()) {
    				Map<Integer, InstancedArea> submap = mapping.getValue();
    				
    				Iterator<InstancedArea> areas = submap.values().iterator();
    				
    				while (areas.hasNext()) {
    					InstancedArea area = areas.next();
    					
    					if (area.isDisposable()) {
    						area.setState(InstancedAreaState.DISPOSED);
    						area.dispose();
    						areas.remove();
    					} else {
    						area.pulse();
    					}
    				}
    			}
    		}
    		
    
    
    	}
    
    
    }
    Code:
    public abstract class SingleInstancedArea extends InstancedArea {
    
    
        /**
         * The player in this single instanced area.
         */
        private Player player;
    
    
        /**
         * Creates a new single instanced area for a player.
         * 
         * @param player
         *            the player in the instanced area.
         * @param type
         *            the type of area.
         * @param boundary
         *            the boundary of the instanced area.
         */
        public SingleInstancedArea(Player player, InstancedType type, Boundary boundary)
                throws AreaUnavailableException {
            super(type, boundary);
            this.player = player;
        }
    
    
        @Override
        public abstract void create();
    
    
        @Override
        public abstract void dispose();
    
    
        @Override
        public abstract void pulse();
    
    
        @Override
        public boolean isDisposable() {
            return super.isDisposable() || player == null || !player.isActive();
        }
    
    
        /**
         * The player for this instanced area.
         * 
         * @return the player.
         */
        public Player getPlayer() {
            return player;
        }
    
    
        @Override
        public boolean equals(Object obj) {
            if (obj instanceof SingleInstancedArea) {
                SingleInstancedArea other = (SingleInstancedArea) obj;
                return super.equals(obj) && other.player.equals(player);
            }
    
    
            return false;
        }
    
    
        @Override
        public int hashCode() {
            return 31 * super.hashCode() + player.hashCode();
        }
    }
    Code:
    public abstract class MultiPlayerInstancedArea extends InstancedArea {
        
        /**
         * A list of all players that are in the room.
         */
        private final List<Player> players;
        
        /**
         * Creates a new instanced area with a given list of players.
         * 
         * @param type
         *               the type of instanced area.
         * @param boundary
         *               the boundary the area resides in.
         * @param players
         *               the players that will occupy this instanced area.
         * @throws AreaUnavailableException
         *                thrown when the area is unavailable, take height for example.
         */
        public MultiPlayerInstancedArea(InstancedType type, Boundary boundary, List<Player> players)
                throws AreaUnavailableException {
            super(type, boundary);
            this.players = players;
        }
    
    
        @Override
        public abstract void create();
    
    
        @Override
        public abstract void dispose();
    
    
        @Override
        public abstract void pulse();
        
        @Override
        public boolean isDisposable() {
            if (super.isDisposable()) {
                return true;
            }
            for (Player player : players) {
                if (player == null || !player.isActive()) {
                    return true;
                }
            }
            return false;
        }
    
    
    }
    Code:
    public class InstancedAreaTest {
        
        public static void main(String... args) {
            /**
             * An example of a player object.
             */
            Player player = new Player("Jason");
            
            /**
             * The type of instance.
             */
            InstancedType type = InstancedType.PSEUDO_BOSS_INSTANCE;
            
            /**
             * The boundary the instanced area should be tied to.
             */
            Boundary boundary = new Boundary(3200, 3200, 3202, 3202);
            
            try {
                /**
                 * Create a new instance, however this instance is not registered yet.
                 */
                PseudoBossInstance instance = new PseudoBossInstance(player, type, boundary);
                
                /**
                 * Sends the instance to the manager to start pulsing and wait for disposal.
                 * This method also references the create method of InstancedArea.
                 */
                InstancedAreaManager.getSingleton().appendAndCreate(instance);
            } catch (AreaUnavailableException e) {
                // send some message to the player stating the instance is unavailable.
            }
        }
        
        private static final class PseudoBossInstance extends SingleInstancedArea {
    
    
            public PseudoBossInstance(Player player, InstancedType type, Boundary boundary)
                    throws AreaUnavailableException {
                super(player, type, boundary);
            }
    
    
            @Override
            public void create() {
                //TODO Create instanced area related content.
                
            }
    
    
            @Override
            public void dispose() {
                // TODO Dispose of instanced area related content.
                
            }
    
    
            @Override
            public void pulse() {
                // TODO Add content that requires constant referencing.
                
            }
        }
        
    }
    Reply With Quote  
     

  2. Thankful users:


  3. #2  
    need java lessons
    Eclipse's Avatar
    Join Date
    Aug 2012
    Posts
    4,436
    Thanks given
    686
    Thanks received
    898
    Rep Power
    490
    so each time one player creates one instance, you need to create a whole class? kinda confused on if im looking at this correctly

    Quote Originally Posted by jerryrocks317 View Post
    i am 14 and have my own laptop im on almost 24/7 currently creating rsps lol so please get off my thread lol
    Reply With Quote  
     

  4. #3  
    Banned

    Join Date
    Jul 2015
    Posts
    607
    Thanks given
    520
    Thanks received
    660
    Rep Power
    0
    Quote Originally Posted by Eclipse View Post
    so each time one player creates one instance, you need to create a whole class? kinda confused on if im looking at this correctly
    hence the name "instanced" lol
    ot: i made something similar to this for ruse (private minigame/boss instances such as fight caves), it's quite useful
    Reply With Quote  
     

  5. Thankful users:


  6. #4  
    Banned

    Join Date
    Jan 2012
    Age
    25
    Posts
    2,703
    Thanks given
    906
    Thanks received
    630
    Rep Power
    0
    I like the idea and concept, nice.
    Reply With Quote  
     

  7. Thankful user:


  8. #5  
    Donator

    Jason's Avatar
    Join Date
    Aug 2009
    Posts
    6,092
    Thanks given
    2,402
    Thanks received
    2,823
    Rep Power
    4550
    Quote Originally Posted by Eclipse View Post
    so each time one player creates one instance, you need to create a whole class? kinda confused on if im looking at this correctly
    Essentially, yes.

    Quote Originally Posted by Swiffy View Post
    hence the name "instanced" lol
    ot: i made something similar to this for ruse (private minigame/boss instances such as fight caves), it's quite useful
    Yeah it's extremely useful. It can definitely be expanded on too.

    Quote Originally Posted by Sam Bartlett View Post
    I like the idea and concept, nice.
    Thanks! .
    Reply With Quote  
     

  9. #6  
    Registered Member Emre's Avatar
    Join Date
    Jan 2014
    Posts
    596
    Thanks given
    91
    Thanks received
    61
    Rep Power
    7
    This looks really good! Thanks
    Reply With Quote  
     

  10. Thankful user:


  11. #7  
    need java lessons
    Eclipse's Avatar
    Join Date
    Aug 2012
    Posts
    4,436
    Thanks given
    686
    Thanks received
    898
    Rep Power
    490
    Quote Originally Posted by Swiffy View Post
    hence the name "instanced" lol
    ot: i made something similar to this for ruse (private minigame/boss instances such as fight caves), it's quite useful
    that wasn't the question, i would think a system like this would be somewhat universal so you can say have a set instance and create it for all players in different heights or something like that

    Quote Originally Posted by jerryrocks317 View Post
    i am 14 and have my own laptop im on almost 24/7 currently creating rsps lol so please get off my thread lol
    Reply With Quote  
     

  12. #8  
    Donator

    Jason's Avatar
    Join Date
    Aug 2009
    Posts
    6,092
    Thanks given
    2,402
    Thanks received
    2,823
    Rep Power
    4550
    Quote Originally Posted by Eclipse View Post
    that wasn't the question, i would think a system like this would be somewhat universal so you can say have a set instance and create it for all players in different heights or something like that
    Then all you would have to do is subclass InstancedArea and add support for a set of players, simple as that. This design right now is for a single player.

    edit;

    Code:
    public class MultiPlayerInstancedArea extends InstancedArea {
    	
    	/**
    	 * A list of all players that are in the room.
    	 */
    	private final List<Player> players;
    	
    	/**
    	 * Creates a new instanced area with a given list of players.
    	 * 
    	 * @param type
    	 * 			  the type of instanced area.
    	 * @param boundary
    	 * 			  the boundary the area resides in.
    	 * @param players
    	 * 			  the players that will occupy this instanced area.
    	 * @throws AreaUnavailableException
    	 * 			   thrown when the area is unavailable, take height for example.
    	 */
    	public MultiPlayerInstancedArea(InstancedType type, Boundary boundary, List<Player> players)
    			throws AreaUnavailableException {
    		super(type, boundary);
    		this.players = players;
    	}
    
    
    	@Override
    	public void create() {
    		// TODO Auto-generated method stub
    		
    	}
    
    
    	@Override
    	public void dispose() {
    		// TODO Auto-generated method stub
    		
    	}
    
    
    	@Override
    	public void pulse() {
    		// TODO Auto-generated method stub
    		
    	}
    	
    	@Override
    	public boolean isDisposable() {
    		if (super.isDisposable()) {
    			return true;
    		}
    		for (Player player : players) {
    			if (player == null || !player.isActive()) {
    				return true;
    			}
    		}
    		return false;
    	}
    
    
    }
    Reply With Quote  
     

  13. Thankful user:


  14. #9  
    Registered Member

    Join Date
    Dec 2012
    Posts
    2,999
    Thanks given
    894
    Thanks received
    921
    Rep Power
    2555
    What's with using height to create a new instance? You should really be using the other map region frame for this
    Attached image
    Reply With Quote  
     

  15. Thankful user:


  16. #10  
    Donator

    Jason's Avatar
    Join Date
    Aug 2009
    Posts
    6,092
    Thanks given
    2,402
    Thanks received
    2,823
    Rep Power
    4550
    Quote Originally Posted by Kaleem View Post
    What's with using height to create a new instance? You should really be using the other map region frame for this
    You're right, I probably should have made use of the existing support for creating a new region. If that was my task then I might have, but it wasn't. Thanks for noting that though.
    Reply With Quote  
     

Page 1 of 4 123 ... LastLast

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. [718] Instanced Areas
    By Soulevoker in forum Help
    Replies: 2
    Last Post: 04-22-2015, 02:16 AM
  2. [718+] Adding music to designated area.
    By Grown in forum Requests
    Replies: 9
    Last Post: 08-26-2013, 09:12 AM
  3. Instanced Area
    By jacob34 in forum Help
    Replies: 2
    Last Post: 06-16-2013, 05:05 PM
  4. 718 Instanced Areas
    By jacob34 in forum Help
    Replies: 3
    Last Post: 06-10-2013, 09:04 AM
  5. [718] Player Instanced Areas
    By Arcanelights in forum Help
    Replies: 1
    Last Post: 05-22-2013, 12:00 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •