Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Area Instancing #97

Open
shawncplus opened this issue Aug 16, 2019 · 8 comments
Open

Area Instancing #97

shawncplus opened this issue Aug 16, 2019 · 8 comments
Labels
enhancement New feature or request
Milestone

Comments

@shawncplus
Copy link
Member

There are currently a few architectural road blocks preventing multiple instances of a single area.

  • Engine needs to allow for instanced and non-instanced versions of an area
  • Items from instanced/non-instanced versions of an area should save/load correctly
  • Hooks for instances when a server gets shut down?
  • What happens to players who log out in an instance and log back in after the instance has been destroyed?
  • How are area instances indexed?

There are other questions but I think the answers to these are in the responsibility of bundle developers:

  • How does grouping work with instances?
  • How do instances expire?
  • Can they be reset?
  • Others?
@shawncplus shawncplus added the enhancement New feature or request label Aug 16, 2019
@seanohue
Copy link
Contributor

This is kind of opinionated but I think that logging back into a destroyed instance should emit an event so the developer can decide what to do. An example implementation might involve recreating the instance, then dropping the player back in to a starting room.

@nelsonsbrian
Copy link
Contributor

What I wanted to make for it was the ability to pass in a config object that you can utilize for npcs and objects. I would have to make my own custom helpers, but if you did area.clone(config) and had something like config = { health: 1.4, ...} and have that that health percent be applied to all the npcs being loaded.

Essentially be able to scale the attributes on items or npcs being loaded in the zone.

@nelsonsbrian
Copy link
Contributor

  • Can instanced zones be saved for next reboot?

@ratacat
Copy link
Contributor

ratacat commented Aug 17, 2019

How are area instances indexed?

I'm not understanding what you mean...like added to RoomManager / AreaManager type of thing?

What happens to players who log out in an instance and log back in after the instance has been destroyed?

I agree with Sean, just emit an event.

Items from instanced/non-instanced versions of an area should save/load correctly

I suspect that we should also approach mobs in a similarly careful way. It wouldn't be out of the ordinary for games to incorporate features that might reference a mob. Such as taming, or npc memory. We'd want any references to mobs from previously existing instances to be handled well.

For general modes of operation, I feel like there are two distinct ways games tend to use instances.

  1. As a load balancer for mobs, quests, resources, overcrowding etc...
  2. As an isolation mechanic for individual or group quests, raids...etc.

I wonder how this would interact with quests? They have an area in their id.

Here's an odd thought, when cloning an area as a new instance, perhaps you leave the area part of the entityReference the same, and add an instance id onto each of the id's for mobs, rooms, items...I don't know, maybe that would be terrible implications, maybe not, haven't looked into it versus changing the area part of the entityReference. Or maybe there's an elegant way to go underneath all of that anyway, I'm not sure.

Essentially be able to scale the attributes on items or npcs being loaded in the zone.

I LOVE THIS IDEA BRIAN!!! SO GOOD!

Can instanced zones be saved for next reboot?

Well considering Ranvier engine at the moment doesn't serialize rooms, probably not.

@seanohue
Copy link
Contributor

I don't see why there couldn't be event hooks in place that, with a custom DataSource, would enable saving instanced zones. But you are correct that it does not serialize rooms by default.

@shawncplus
Copy link
Member Author

shawncplus commented Aug 20, 2019

Basic instancing functionality is now testable on the instancing branch of core. You'll also need to update your example bundles to the most recent code. For bundle-example-player-events you'll need to be on the instancing branch as well.

Overview

  • An Area's manifest may now include a new instanced property:
title: Test Area
instanced: player

Non-falsey values of instanced are arbitrary and used purely for bundle developers to change how a given area should be instanced.

Areas which have a non-falsey value for instanced will not be created/hydrated at server startup. Instead, creation of these areas is left entirely to bundle developers.

Creating an instance

An example implementation can be seen here https://github.com/RanvierMUD/bundle-example-player-events/tree/instancing

The gist being:

  • when the player tries to move it will check to see if the room they are moving to is in an instanced area
  • if it is it will check to see if a valid instance exists based on the instanceId (in the case of the example bundle it's static to the player's name but again, the instanceId is left to the bundle developers to determine.)
  • If an instance doesn't exist it will create one with AreaFactory.createInstance(gameState, areaName, instanceId) which handles the work of creating an area, assigning the given instance id, then hydrating the area. This happens synchronously
  • Finally it gets the room from the instance and returns it as the valid target for the movement command

Removing an instance

Like creating an instance, how an instance gets removed and cleaned up is entirely up to the bundle developer. The removeArea(area, instanceId) will dereference the area and allow for creation with the same key but it's the responsibility of the caller of removeArea() to do things like halt combat, make sure players don't get stuck in a dereferenced area, cancel any effects, etc.

@shawncplus
Copy link
Member Author

As to @nelsonsbrian 's idea I've added events for an area when an NPC is added/removed. This currently happens when an NPC is spawned by room.spawnNpc.

To accomplish something like that you would create 2 effects. The first being an Area effect which listens for npcAdded and based on its configuration would create an NPC effect and attach it to the new NPC.

It would look something like:

Somewhere a configuration exists that says { health: 1.4, ..., an instanced area is generated, you initialize the new area-bonuses effect passing in that config, then attach it to the area.

Whenever an NPC gets spawned in that area your effect will be listening for it and when it does will be able to create a new effect like area-npc-bonus, and attach it to said NPC.

@shawncplus shawncplus added this to the 3.1 milestone Aug 22, 2019
@ratacat
Copy link
Contributor

ratacat commented Feb 21, 2020

I've found some an unexpected thing come up with using the instancing branch. Largely it works awesome! Basically, if you try and use state.RoomManager#getRoom to fetch a room from an instance that isn't loaded, it can't find it. Maybe this makes since as the rooms don't technically exist yet. I've run into it using the teleport command, as well as the starting room.

I've been using this function, which comes from someplace else. Just figured I'd share in case anyone else runs into this.

RoomUtil.getRoom = function(state, player, nextRoomId) {
	const nextAreaName = state.AreaFactory.getNameByEntityReference(nextRoomId)
	let nextRoom = null
	if (state.AreaFactory.isInstanced(nextAreaName)) {
		const instanceId = player.name
		if (player.area != nextAreaName ){
			B.sayAt(player, `W]Entering instanced area: w]${nextAreaName}`)

			if (!state.AreaManager.getArea(nextAreaName, instanceId)) {
				B.sayAt(player, "W]No instance exists, creating...")
				state.AreaFactory.createInstance(state, nextAreaName, instanceId)
			}
			B.prompt(player)
		}

		nextRoom = state.RoomManager.getRoom(nextRoomId, instanceId)
	} else {
		nextRoom = state.RoomManager.getRoom(nextRoomId)
	}

	return nextRoom
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants