Thread: Instanced Area Design

Page 2 of 4 FirstFirst 1234 LastLast
Results 11 to 20 of 38
  1. #11  
    -Founder Off Returnofpk-


    Join Date
    Oct 2012
    Age
    28
    Posts
    655
    Thanks given
    97
    Thanks received
    211
    Rep Power
    303
    Quote Originally Posted by Jason View Post
    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 = 3;
    
    
            /**
             * 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();
                    
                    Collection<InstancedArea> areas = submap.values();
                    
                    for (InstancedArea area : areas) {
                        if (area.isDisposable()) {
                            area.setState(InstancedAreaState.DISPOSED);
                            area.dispose();
                            areas.remove(area);
                        } 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.
                
            }
        }
        
    }
    nice ass always jason
    Attached image
    Reply With Quote  
     

  2. #12  
    Banned

    Join Date
    Apr 2014
    Posts
    419
    Thanks given
    117
    Thanks received
    56
    Rep Power
    0
    gonna use this
    Reply With Quote  
     

  3. #13  
    Registered Member
    Join Date
    Jan 2016
    Posts
    162
    Thanks given
    26
    Thanks received
    4
    Rep Power
    29
    i got 98Error, using eclipse....
    Reply With Quote  
     

  4. #14  
    Registered Member
    Stanaveli's Avatar
    Join Date
    Aug 2014
    Posts
    1,490
    Thanks given
    184
    Thanks received
    653
    Rep Power
    1338
    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
    I never understood how that worked, I always thought that that packet only created the map on different positions? So are you implying we should create the same map over x amount of coordinates?

    This shouldn't be done with the player's height, Graham made a suggestion of a couple ways this could of been done years ago, also this wouldn't work on a few bases such as on Apollo since there is no height above 3, if you try to spawn a npc / player above that height it'll crash the server due to the region system.

    Graham's post if anybody is interested. I'm not sure if this is an actual post by him but it was like this where I read it from.

    Quote Originally Posted by Graham
    There are various ways to do this, I'll go over 3 main methods.

    Using the heightLevel method (heightLevel * instanceId * 4):

    This relies on the fact that the server sees the players on different height levels, however, when the server writes the height level it only uses 2 bits. Hence it overflows, and you get the effect of heightLevel % 4. So the client displays the correct height level and only has certain players in it.

    This way is a bit messy and I wouldn't reccomend it.

    Using the secondary map region packet

    The secondary map region packet dynamically creates areas. So you could copy lumbridge, and put it in the coords 40 000, 40 000. If you had multiple target areas where you copied other areas, you could also use instancing. Jagex are believed to do this as when you play minigames the coordinates are seemingly random.

    Again, this is messy, I wouldn't reccomend it (however it is good for player owned houses and such!).

    Adding another variable (fourth dimension!) - instance

    This would involve checking for X, Y, Height and Instance. I'd prefer this method over all of them as it works cleanly without much extra effort. For a server like rs2d or Hyperion it'd be easy to make this due to the use of a Location class. On winterlove it's probably better to use one of the others as it isn't using OOP properly.

    Hope that answers your question . If you need any more detail/have questions pm me back.
    Keep your head up.



    Reply With Quote  
     

  5. #15  
    🍥🍥🍥


    Join Date
    Dec 2008
    Posts
    1,702
    Thanks given
    664
    Thanks received
    293
    Rep Power
    621
    Quote Originally Posted by Stanaveli View Post
    I never understood how that worked, I always thought that that packet only created the map on different positions? So are you implying we should create the same map over x amount of coordinates?

    This shouldn't be done with the player's height, Graham made a suggestion of a couple ways this could of been done years ago, also this wouldn't work on a few bases such as on Apollo since there is no height above 3, if you try to spawn a npc / player above that height it'll crash the server due to the region system.

    Graham's post if anybody is interested. I'm not sure if this is an actual post by him but it was like this where I read it from.
    http://www.rune-server.org/runescape...on-packet.html
    Attached image
    Reply With Quote  
     

  6. Thankful user:


  7. #16  
    q.q


    Join Date
    Dec 2010
    Posts
    6,519
    Thanks given
    1,072
    Thanks received
    3,535
    Rep Power
    4752
    Quote Originally Posted by Stanaveli View Post
    I never understood how that worked, I always thought that that packet only created the map on different positions? So are you implying we should create the same map over x amount of coordinates?

    This shouldn't be done with the player's height, Graham made a suggestion of a couple ways this could of been done years ago, also this wouldn't work on a few bases such as on Apollo since there is no height above 3, if you try to spawn a npc / player above that height it'll crash the server due to the region system.

    Graham's post if anybody is interested. I'm not sure if this is an actual post by him but it was like this where I read it from.
    Why even bother? If your not creating a unique scene then abusing overflow for instanced regions works perfectly. Not to mention there's nothing released that accounts for server side clipping implementation at custom regions (though isn't hard).

    Grahams suggests that

    This way is a bit messy and I wouldn't reccomend it.
    Which isn't a valid reason not to use it if it achieves the desired effect in less steps.
    Reply With Quote  
     

  8. Thankful users:


  9. #17  
    Registered Member
    Stanaveli's Avatar
    Join Date
    Aug 2014
    Posts
    1,490
    Thanks given
    184
    Thanks received
    653
    Rep Power
    1338
    I have read that, I guess it's just me who doesn't understand how to use it.

    Quote Originally Posted by Harlan View Post
    Why even bother? If your not creating a unique scene then abusing overflow for instanced regions works perfectly. Not to mention there's nothing released that accounts for server side clipping implementation at custom regions (though isn't hard).

    Grahams suggests that

    Which isn't a valid reason not to use it if it achieves the desired effect in less steps.
    Well not necessarily more code/steps then what was supplied by the OP, e.g to keep it bare bones you can just declare an array in your world class and cache the slot the node is in and work off that in the updating procedure.

    I'm not sure if @OP sees this as any hate since he doesn't respond himself and maybe thinks it's just smarter to ignore but I don't mean any of this in a negative way.

    This is better then what gets released now-a-days, i'm just trying to let him know he should do this in a better manner because I know he can.
    Last edited by Stanaveli; 03-04-2016 at 12:12 AM. Reason: Forgot to mention something.
    Keep your head up.



    Reply With Quote  
     

  10. #18  
    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 Stanaveli View Post
    I'm not sure if @OP sees this as any hate since he doesn't respond himself and maybe thinks it's just smarter to ignore but I don't mean any of this in a negative way.
    I have not responded to you directly because other members have responded to you and in turn have covered everything I would have brought up. Do not assume that because I haven't responded it's because i'm ignoring you, thats a very paranoid way to live.
    Reply With Quote  
     

  11. #19  
    The Meme God

    4DFo's Avatar
    Join Date
    Dec 2013
    Age
    26
    Posts
    616
    Thanks given
    54
    Thanks received
    81
    Rep Power
    185
    Quote Originally Posted by Jason View Post
    I have not responded to you directly because other members have responded to you and in turn have covered everything I would have brought up. Do not assume that because I haven't responded it's because i'm ignoring you, thats a very paranoid way to live.
    #ShotsFired BANG BANG
    But i like the way you did this tbh. Makes perfect sense imo but thats just me, and creating a new class for a new instanced area is a good idea for something like if you wanted an instanced callisto (which is wild) a player could pay x amount of coins to get his own instance and use a timer to also limit how long they can do it because even if they paid, having your own instance for a wildy boss is OP asf. Many great uses/ideas could spark out of this if used properly. Great work, Jason. May use on my personal project.
    Reply With Quote  
     

  12. #20  
    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 44.. View Post
    #ShotsFired BANG BANG
    But i like the way you did this tbh. Makes perfect sense imo but thats just me, and creating a new class for a new instanced area is a good idea for something like if you wanted an instanced callisto (which is wild) a player could pay x amount of coins to get his own instance and use a timer to also limit how long they can do it because even if they paid, having your own instance for a wildy boss is OP asf. Many great uses/ideas could spark out of this if used properly. Great work, Jason. May use on my personal project.
    Thanks, I hope you do.
    Reply With Quote  
     

Page 2 of 4 FirstFirst 1234 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
  •