Thread: Instanced Area Design

Page 1 of 4 123 ... LastLast
Results 1 to 10 of 39
  1. #1 Instanced Area Design 
    I need some more coffee

    Jason's Avatar
    Join Date
    Aug 2009
    Age
    23
    Posts
    5,810
    Thanks given
    2,106
    Thanks received
    2,479
    Rep Power
    3086
    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 [email protected] InstancedAreaState#AVAILABLE}.
         */
        private InstancedAreaState state = InstancedAreaState.AVAILABLE;
        
        /**
         * Creates a new area with a boundary for the given type.
         * 
         * <p>If [email protected] InstancedAreaManager#getNextOpenHeight(InstancedType)} throws an 
         * [email protected] 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 [email protected] NullPointerException} will be thrown and should
         * be handled although this is not enforced. If the current state is that of
         * [email protected] InstancedAreaState#DISPOSABLE} and we attempt to change it by
         * referencing this function, an [email protected] IllegalStateException} will be thrown.
         * 
         * @param state
         *            the new state of this area.
         * 
         * @throws IllegalStateException
         *             thrown when the current state is
         *             [email protected] 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
         * [email protected] 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 [email protected] true} if the area can be disposed of by the manager,
         *         otherweise [email protected] 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 [email protected] 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 [email protected] InstancedAreaState#DISPOSED}.
         * 
         * @return [email protected] true} if disposed, otherwise [email protected] 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 [email protected]} 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 [email protected] 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 [email protected] InstancedArea} object to the mapping for the given height.
    	 * 
    	 * <p>It should be noted that if the given parameter reference is null, a [email protected] 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 [email protected] #append(InstancedArea)} in conjunction with
    	 * [email protected] 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 [email protected] 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 [email protected] #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 [email protected] #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,473
    Thanks given
    685
    Thanks received
    892
    Rep Power
    472
    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
    617
    Thanks given
    521
    Thanks received
    658
    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
    18
    Posts
    2,707
    Thanks given
    907
    Thanks received
    630
    Rep Power
    0
    I like the idea and concept, nice.
    Reply With Quote  
     

  7. Thankful user:


  8. #5  
    I need some more coffee

    Jason's Avatar
    Join Date
    Aug 2009
    Age
    23
    Posts
    5,810
    Thanks given
    2,106
    Thanks received
    2,479
    Rep Power
    3086
    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 Yoshisaur's Avatar
    Join Date
    Jan 2014
    Posts
    547
    Thanks given
    63
    Thanks received
    40
    Rep Power
    11
    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,473
    Thanks given
    685
    Thanks received
    892
    Rep Power
    472
    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  
    I need some more coffee

    Jason's Avatar
    Join Date
    Aug 2009
    Age
    23
    Posts
    5,810
    Thanks given
    2,106
    Thanks received
    2,479
    Rep Power
    3086
    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,903
    Thanks given
    850
    Thanks received
    813
    Rep Power
    2147
    What's with using height to create a new instance? You should really be using the other map region frame for this
    Reply With Quote  
     

  15. Thankful user:


  16. #10  
    I need some more coffee

    Jason's Avatar
    Join Date
    Aug 2009
    Age
    23
    Posts
    5,810
    Thanks given
    2,106
    Thanks received
    2,479
    Rep Power
    3086
    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)

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
  •