Entering & Leaving Zones

Status Update

It's been a bit since I've posted, but things are chugging along! I keep the Roadmap/Changelog pretty up to date as things are merged into the next release, so keep an eye on those pages if you're curious where we are and haven't seen a post in a while.

I am happy to say that since my last post we have solved a lot of content/client side challenges and have either a working proof of concept or framework in place to handle the following:

  • Support for non humanoid NPCs
  • Ability to switch between zone servers (I.e. "zoning" into a new location) while keeping your character state synchronized in the world.
  • Shared wardrobe models that can be used by most, if not all humanoids. These can be mapped to NPCs statically or dynamically rendered based on the equipment being worn.
Preview of the wardrobe system. In this case, a matching robe & coif are equipped by my player.

In the near future I am planning on releasing a simplified version of our framework which handles the mapping/rendering of armor as it is equipped/removed, as well as a way to define an entire wardrobe collection for NPCs regardless of what they have equipped.

Given that we're abstracting the Unity Multipurpose Avatar (a free) plugin, the hope is to share some code that others could copy/reuse to jump start their wardrobe/equipment system. This won't actually integrate with a database, but will give a clear picture on how you could manage/scale for an MMORPG.

I should have the above example ready in just a couple weeks here, but in the meantime.. I'm going to post a quick overview of the challenges we faced when getting a character to switch between zones.

Synchronization Challenges

So lets dig into the process of moving a player between zones. If you have been following along with our other posts, you should be aware that the world state is hosted/maintained by a single server. Then, each zone is hosted by its own independent server, maintaining its own state separate from the world. Therefore, certain state changes which occur on the zone need to be propagated up to the world and vice versa.

For the sake of clarity, when referring to the "bind" location of the player, that is the zone, position, and direction they load upon dying (or can teleport to if they are a spell caster).

Before this change we did implement one aspect of zoning: reloading upon death. If your character died, the current zone server would persist your bind location as your current location in the database. Then, the character would (in a hard-coded manner) load the same exact zone, but upon load would use the new position which had been persisted by the zone just moments before.

We had built the death event in this way last year in preparation for changing zones. By handling deaths as an actual zone change, we'd hopefully have most of the code ready for when we get to changing between different zones. However, after we tried fitting in movement between two different zones... the issues with our previous design began to show.

Previous Implementation

  1. Player dies in the zone
  2. Zone server creates a corpse and moves all player items/coins to said corpse. Updates position/direction/zone of the player to his or her bind location. These are persisted in the database.
  3. Zone server alerts connected clients, letting them know of the death
  4. Player who died contacts zone server to "leave" the zone
  5. Zone server unloads the player, saving their state in the database.
    • (Alternatively) if the player disconnected, they will be unloaded after a few minutes. At which point, their new state is persisted.
  6. Player requests a new "Zone Change" token from the world server. The player was specifying the zone they wanted to connect to in this case. (The zone in which they had died in, because we did not support changing zones yet)
  7. Player requests to enter the zone for their bind, providing the "Zone Change" token.
  8. Zone server calls the world server to validate the above token. If valid, the player is loaded from the database into the zone's memory.
  9. Zone informs all of the connected clients that a player has entered the zone
  10. Player continues process for loading the zone (terrain/audio/weather assets, each character individually from the zone server, etc.)

Now there are a boatload of issues with the above. To list a few:

  • The player is specifying the zone they want to go to, rather than simply being told.
  • World server wasn't validating whether a zone change is valid for a player. For example, a player who is far away from a portal shouldn't be able to use it to zone. A player shouldn't be able to "hop" 5-6 zones at once unless they happened to be bound there (or were teleported by a wizard/druid).
  • The player would leave the current zone before seeing if they could get a "Zone Change" token from the world server. This is OK for deaths, but does not work for normal zone changes. What if the target zone server is down for an extended period? Well, you've already disconnected from your current zone -- and with no zone left to connect to you would crash.

New Implementation

So how does the zone change flow actually look like now? Lets look at the death case.

  1. Player dies in a zone
  2. Zone server creates a corpse and moves all player items/coins to said corpse. Updates position/direction/zone of the player to his or her bind location. These are persisted in the database in case the player crashes.
  3. Zone server informs the world server that the player has died.
  4. Zone server alerts connected player clients/authoritative client, letting them know of the death.
  5. Player who died contacts the world server first this time, informing the world server that it is requesting a zone change token for a particular reason of death.
  6. World server checks whether the player is in fact dead (which it learned of in step 3). If true, the world creates a "Simple Zone Change" token and caches it locally, along with the player's target zone/location. The target zone host is returned to the player client, so they know which socket servers to connect to next.
  7. Player calls the current zone they're in with the "Simple Zone Change" token to inform them that they're disconnecting.
  8. Current zone calls the world server, providing the zone change token, to inform them that the player is now leaving.
  9. World server uses the zone change token provided from the zone server to lookup the player's target zone / location. The player's state is updated in the world at this point (required for the non death case).
  10. World server returns back to the zone server an "Upgraded Zone Change" token.
  11. Unless there was a failure validating the token, the zone will finally unload the player -- persisting all changes (except for their zone/position) in the database. Note that this step is required for the non death case. Death persistence happens in step 2.
  12. Zone server returns the "Upgraded Zone Change" token to the player client.
  13. Player client finally connects to the target zone host that it was provided in step 6, this time providing the "Upgraded Zone Change" token.
  14. Target zone server calls the world server with the upgraded token. If valid, the world server will inform the player's party/guild members of the zone update, remove the token, and update their state as no longer dead.
  15. If the target zone server receives back a success from the world, the player is loaded from the database into the zone's memory.
  16. Zone informs all of the connected clients that a player has entered the zone
  17. Player continues process for loading the zone (terrain/audio/weather assets, each character individually from the zone server, etc.)

Although this process became a lot more intricate, I was really motivated after seeing cheating programs for EverQuest which would allow you to instantly zone from any position, as long as the target zone was connected (including bind locations, for example). I wanted to be sure our game servers could authoritatively check that the player is entering a new zone legally, as best I could.

Note that I've primarily discussed the death case, but how do we generically change zones in an authoritative manner?

For this we created a new entity to track throughout servers/clients: "ZoneLinks". A zone link is really just the definition between two zones that are connected, including the position in the world where the links are located as well as other metadata to represent how the player's direction will be calculated after zoning.

Finally, on step 5 of the new flow -- rather than the player requesting a zone change token from the world for a reason of death, the player will request the zone change token for a reason of "ZoneLink". Additionally, the player only provides the zone link ID they are triggering. This allows for the server to decide/enforce which target zone the player will go to, as well as to lookup and see whether the player is truly within range of the zone boundary.

Although not important to the flow, we currently support "Proximity" link triggers on the client and "Clickable" link triggers on the client. A clickable link trigger can be assigned to a GameObject in unity so that whenever clicked it will trigger, whereas a proximity link trigger is simply an invisible game object w/ a collider (triggering when a player walks through it).

Proof of concept / demonstration of proximity based zone links working. (These two zones wont really have a connection)

If you look closely in the above, you should be able to see most of the new features we've added since the last post. For example, the NPCs are wearing armor -- and in this case it is a static wardrobe collection for that type of NPC -- whereas the sword they are wielding is coming from is actually equipped.

And with that.. I'll leave you guys until the next update.

What did you think about this post?

Click on a star to rate it!

Average rating 5 / 5. Vote count: 1

No votes so far! Be the first to rate this post.

Readers Comments (1)

  1. Nice update man. It’s clear you know what you’re doing, keep up the great work.

Leave a comment

Your email address will not be published.