RunUO Community

This is a sample guest message. Register a free account today to become a member! Once signed in, you'll be able to participate on this site by adding your own topics and posts, as well as connect with other members through your own private inbox!

Instances Experiment

Serp

Sorceror
Instances Experiment

Before you do anything, read this whole post (especially the last part). This is not for beginners.

First off, what is an instance?
An instance alone is not different from any place anywhere. However there can be several instances of the same area which adds a lot of possibilities! Multiple people can be in seemingly the same area but without bumping into eachother. This is not only cool but also an upcoming feature of some UO:ML dungeons and I just had to give it a shot.

How it works
Everytime an instance is created is makes a new map. This map is derived from another map and it gets all the properties that the "master map" had as of looks and basic rules. When the instance is removed the entire map with all items and mobiles is removed.
This one of the easiest way to do instancing, but not necessarily the best.

Alrighty, how do I get to instanciating?
  • Download the files here and either overwrite or merge with the existing ones. (Item.cs, Map.cs, Mobile.cs and World.cs go into the core, the rest into your custom scripts folder)
  • Compile your new core and start your server
  • Decorate the "instance blueprint". All instances will look like this area. Keep in mind that if you want mobiles you need to put spawners instead of actual mobiles. And yes, XmlSpawner is supported (if you uncomment some code in Instances.cs.) And don't forget the exit teleporters! They can be any kind of teleporters or gates or whatever. The important thing is that they have a "real" map as target and not an instance.
  • Add an InstanceTeleporter
  • Setup your teleporter (see below for options)
  • Play around with it.
  • Report all bugs here!

InstanceTeleporter options
  • InstanceID: when you have multiple teleporters that have the same target you want to specify the same InstanceID in all of them
  • InstanceType: do you want one instance for every player, every party or every guild?
  • Bounds (X1Y1 and X2Y2): Area from where to copy items to the new instance. When you have multiple teleporters that have the same target you want them all to have the same bounds to avoid weirdness

More about the instances
  • Instances decay 10 minutes after the last player leaves it. If any player comes back it will reset the timer.

Known issues
  • Pets will be deleted if they are in the instance when it decays
  • If players are saved in an instance they will pop up in the "blue print" on next server load
  • Almost no speed or resource optimizations
  • If you enter a party instance you can't re-enter if the party would dissolve for some reason. The same goes for guild instances.
  • A region needs to be added to the newly created instance to prevent people from marking runes, walking outside the bounds etc.

Fix list
13 aug. First release.

Some more info...
I mark everything I modify in the core with // Jakob's instance implementation
The code is not very well commented, but I try to keep it simple enough for most people to follow what's going on.


Special thanks to..
Asayre8 for sharing some thoughts on instancing
Xavier for helping me test the scripts


This system is in no way ready for use on a production shard. It may (read probably will) cause some irregularities on your shard. DO NOT use it for anything other than playing around with as of now. I created this as an experiment and you are more than welcome to help fixing it into something stable, but again, please do not use this on a regular shard right now.
 

Attachments

  • Instances.zip
    74.5 KB · Views: 180

Atomic

Wanderer
Very interesting script :) Pity I don't like the idead of instances at all, but it will be nice to give it a shot
 

Xavier_WER

Sorceror
Instances have all sorts of great uses, from "adventure" type Dungeons, where you fight
your way down, to Duel Arenas. (Those "challange system") Where you can have your own
arena with minimal lagg caused by bistanders and no waiting in lines.
 

tejster24

Sorceror
I've got a brilliant use for this.
I've been trying to make a good single player RPG from UO for a while, and I needed a way to do multiple copies of Trammel on the fly, but never really attempted coding it. This gives me all the code I need, I just need to rewrite the way it saves ;)
 

Alis

Wanderer
hmmz me shall test every inch of the posted subject project :) thanks lets look what you have done :)
 

Ravatar

Knight
I did something similar before, although with a different approach. My method was an Array of "InstanceMap", that were basically Item/Mobile lists. When sending new Mobile/Effect/Item data, I checked wether the emitting object was in the same InstanceMap as the destination object.
 

Serp

Sorceror
Ravatar said:
I did something similar before, although with a different approach. My method was an Array of "InstanceMap", that were basically Item/Mobile lists. When sending new Mobile/Effect/Item data, I checked wether the emitting object was in the same InstanceMap as the destination object.
That approach break just about every script that "scans" the map for mobiles or items, for example housing, movement, ai and area effect spells as well as scripts that make items or mobiles to appear (for instance at creation).
It's just too much modification (imo) for a script that isn't even a part of the official release.
 

Ravatar

Knight
But only a handful of methods do the bulk of that work.

My only concern is the performance hit related to creating new maps so frequently.
 

Killamus

Knight
So, what happens? I'm gonna run a few senarieo's by ya, I want to know what would happen.

Player a runs in, player b runs in afterwards.
player a leaves, player b leaves
player b comes back in, player a comes back in

In order, would player b then see player a's instance? Or is it seralized per player?

Next:
Player a runs into instance a, player c runs into instance c
player b leaves, player a leaves, player a runs back in, does he still see player b's instance?

last:
Player a is in party with player c. Player a runs into instance a, player c runs into instance b. player c runs into (a) instance a or (b) instance c. Which is it?


This reminds me of guildwars...:D
 

Serp

Sorceror
Ravatar said:
But only a handful of methods do the bulk of that work.

My only concern is the performance hit related to creating new maps so frequently.
Creating a map is really not a big deal. I could see a performance hit with copying all the items though.
This can be solved by giving the instance a Reset() method that deletes all corpses and reset all spawners (providing all items are decoration only).
 

Serp

Sorceror
Killamus said:
So, what happens? I'm gonna run a few senarieo's by ya, I want to know what would happen.

You can set it to identify per player, party or guild.

If you set it per player:
Killamus said:
Player a runs in, player b runs in afterwards.
player a leaves, player b leaves
player b comes back in, player a comes back in
Player b will go back to his instance and player a will go back to his instance.

Killamus said:
Player a runs into instance a, player c runs into instance c
player b leaves, player a leaves, player a runs back in, does he still see player b's instance
Player a will go back to his instance.


killamus said:
Player a is in party with player c. Player a runs into instance a, player c runs into instance b. player c runs into (a) instance a or (b) instance c. Which is it?
If it's set to identify instances per player they will go to different instances, otherwise they will share. You can set this in the InstanceTeleporter.
 

Tobbe371

Wanderer
Ok, i dont know if you still support this script, but il ask a question anyways.


The core, would you like to me more exact? i do not got a Item.cs (do you mean BaseItem.cs?) and neither one of those other Core scripts. would be cool if some one told me how to replace these files and replace with what..

realy nice idea btw
 

Kamron

Knight
if you downloaded the RunUO source coded, then you do have those files.

This script is not supported, and was only put together in a few hours as an example of the idea.
 

Serp

Sorceror
The core is the Server.exe executable you click when starting RunUO. You need to get the source code for that to be able to do this thing, and it doesn't seem like yuo can download that atm.

And like XxSP1DERxX said, it was just a quick example of the idea that works, but it's not very pretty at all, and it's not really supported.
 

Tobbe371

Wanderer
ok, just wondering. cuz this could be realy helpfull for allot of shards i think. maby hard with uo, thats not as organized as wow, maby could work on a serious shard. with large guilds and such..
 
Jakob said:
Creating a map is really not a big deal. I could see a performance hit with copying all the items though.
This can be solved by giving the instance a Reset() method that deletes all corpses and reset all spawners (providing all items are decoration only).

Dunno if you've ran over this already... Butttttttttt...

Why don't you make it so you can choose to only copy a specified area?

(i'm loving my scenarios today...)

Say you got your map (splats map down on table)...
It has towns, cities, and stuff...
Copying all that makes massive lag...
Copying a SPECIFIED area creates less lag...

Copy I dunno... (0, 0) to (500, 500)... Not all of (0, 0) to (6112, 4096)...

We see what i'm getting at? (I hope... if you don't I dunno how to explain it more... :p)

-Aiden
 
I know this is an old thread so sorry if a Mod feels that I am digging it up.
However I've just started to play around with this, it looks exactly what I need for the project that I want to undertake.
So first a big thanks to Jakob from me for his hard work in creating this.

Instead of copying the core files and replacing my old Core files I wanted to merge Jakob's changes with my current Core files. His instructions and commenting was great to help me find all his core changes, however there was only one bit that was missed out so this post is to correct this and help any others if they encounter the same problem.
I encountered this compile error:
'Server.Map' does not contain a definition for 'Index'
To fix this:
In Map.cs you will need to find these two methods:
Code:
[COLOR=black][SIZE=2]public [/SIZE][SIZE=2]static[/SIZE][/COLOR][SIZE=2][COLOR=black] Map[] GetMapValues()[/COLOR]
[COLOR=black]{[/COLOR]
[COLOR=black]    CheckNamesAndValues();[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_MapValues;[/COLOR]
[COLOR=black]}[/COLOR]
[/SIZE]

and

Code:
[COLOR=black][SIZE=2]public [/SIZE][SIZE=2]static[/SIZE][SIZE=2] Map Parse( [/SIZE][SIZE=2]string[/SIZE][/COLOR][SIZE=2][COLOR=black] value )[/COLOR]
[COLOR=black]{[/COLOR]
[COLOR=black]    CheckNamesAndValues();[/COLOR]
[/SIZE][COLOR=black][SIZE=2]    for[/SIZE][SIZE=2] ( [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] i = 0; i < m_MapNames.Length; ++i )[/COLOR]
[COLOR=black]    {[/COLOR]
[/SIZE][SIZE=2][COLOR=black]         if[/COLOR][/SIZE][SIZE=2][COLOR=black] ( Insensitive.Equals( m_MapNames[i], value ) )[/COLOR]
[/SIZE][SIZE=2][COLOR=black]              return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_MapValues[i];[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    try[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         int[/SIZE][SIZE=2] index = [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black].Parse( value );[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         if[/SIZE][SIZE=2] ( index >= 0 && index < m_Maps.Length && m_Maps[index] != [/SIZE][SIZE=2]null[/SIZE][/COLOR][SIZE=2][COLOR=black] )[/COLOR]
[/SIZE][SIZE=2][COLOR=black]              return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_Maps[index];[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    catch[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][COLOR=black][SIZE=2]    throw [/SIZE][SIZE=2]new [/SIZE][SIZE=2]Exception[/SIZE][SIZE=2]( [/SIZE][SIZE=2]"Invalid map name"[/SIZE][/COLOR][SIZE=2][COLOR=black] );[/COLOR]
[COLOR=black]}[/COLOR]
[/SIZE]

In between these new methods place this code:
Code:
[SIZE=2][COLOR=#008000][COLOR=black]// Jakob's instances[/COLOR]
[/COLOR][/SIZE][COLOR=black][SIZE=2]public [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] Index[/COLOR]
[COLOR=black]{[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    get[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         for[/SIZE][SIZE=2] ([/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] i = 0; i < m_Maps.Length; ++i)[/COLOR]
[/SIZE][COLOR=black][SIZE=2]              if[/SIZE][SIZE=2] (m_Maps[i] == [/SIZE][SIZE=2]this[/SIZE][/COLOR][SIZE=2][COLOR=black])[/COLOR]
[/SIZE][SIZE=2][COLOR=black]                   return[/COLOR][/SIZE][SIZE=2][COLOR=black] i;[/COLOR]
[/SIZE][SIZE=2][COLOR=black]         return[/COLOR][/SIZE][SIZE=2][COLOR=black] -1;[/COLOR]
[COLOR=black]    }[/COLOR]
[COLOR=black]}[/COLOR]
// end instances
[/SIZE]

Your code should now look like this:

Code:
[COLOR=black][SIZE=2]public [/SIZE][SIZE=2]static[/SIZE][/COLOR][SIZE=2][COLOR=black] Map[] GetMapValues()[/COLOR]
[COLOR=black]{[/COLOR]
[COLOR=black]    CheckNamesAndValues();[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_MapValues;[/COLOR]
[COLOR=black]}[/COLOR]
 
[SIZE=2][COLOR=#008000][COLOR=black]// Jakob's instances[/COLOR]
[/COLOR][/SIZE][COLOR=black][SIZE=2]public [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] Index[/COLOR]
[COLOR=black]{[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    get[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         for[/SIZE][SIZE=2] ([/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] i = 0; i < m_Maps.Length; ++i)[/COLOR]
[/SIZE][COLOR=black][SIZE=2]              if[/SIZE][SIZE=2] (m_Maps[i] == [/SIZE][SIZE=2]this[/SIZE][/COLOR][SIZE=2][COLOR=black])[/COLOR]
[/SIZE][SIZE=2][COLOR=black]                   return[/COLOR][/SIZE][SIZE=2][COLOR=black] i;[/COLOR]
[/SIZE][SIZE=2][COLOR=black]         return[/COLOR][/SIZE][SIZE=2][COLOR=black] -1;[/COLOR]
[COLOR=black]    }[/COLOR]
[COLOR=black]}[/COLOR]
// end instances
 
[COLOR=black][SIZE=2]public [/SIZE][SIZE=2]static[/SIZE][SIZE=2] Map Parse( [/SIZE][SIZE=2]string[/SIZE][/COLOR][SIZE=2][COLOR=black] value )[/COLOR]
[COLOR=black]{[/COLOR]
[COLOR=black]    CheckNamesAndValues();[/COLOR]
[/SIZE][COLOR=black][SIZE=2]    for[/SIZE][SIZE=2] ( [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black] i = 0; i < m_MapNames.Length; ++i )[/COLOR]
[COLOR=black]    {[/COLOR]
[/SIZE][SIZE=2][COLOR=black]         if[/COLOR][/SIZE][SIZE=2][COLOR=black] ( Insensitive.Equals( m_MapNames[i], value ) )[/COLOR]
[/SIZE][SIZE=2][COLOR=black]              return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_MapValues[i];[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    try[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         int[/SIZE][SIZE=2] index = [/SIZE][SIZE=2]int[/SIZE][/COLOR][SIZE=2][COLOR=black].Parse( value );[/COLOR]
[/SIZE][COLOR=black][SIZE=2]         if[/SIZE][SIZE=2] ( index >= 0 && index < m_Maps.Length && m_Maps[index] != [/SIZE][SIZE=2]null[/SIZE][/COLOR][SIZE=2][COLOR=black] )[/COLOR]
[/SIZE][SIZE=2][COLOR=black]              return[/COLOR][/SIZE][SIZE=2][COLOR=black] m_Maps[index];[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    catch[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    {[/COLOR]
[COLOR=black]    }[/COLOR]
[/SIZE][COLOR=black][SIZE=2]    throw [/SIZE][SIZE=2]new [/SIZE][SIZE=2]Exception[/SIZE][SIZE=2]( [/SIZE][SIZE=2]"Invalid map name"[/SIZE][/COLOR][SIZE=2][COLOR=black] );[/COLOR]
[COLOR=black]}[/COLOR]
[/SIZE][/SIZE][/SIZE]

You are now ready to compile your new core.
 
After some more playing with this I noticed that when the world saved it crashed during Serialization of Mobiles.
So I did some error checking using Console.WriteLine and narrowed it down to m_LogoutMap being null during Serialization. This caused crashes because it checks m_LogoutMap.IsInstance, during Serialization and because m_LogoutMap was null it couldnt check the IsInstance and caused a null object reference crash.

The fix for this is to find the following code in the Serialize method of Mobile.cs:

Code:
[SIZE=2][COLOR=black]//writer.Write( m_LogoutMap );[/COLOR]
[/SIZE][COLOR=black][SIZE=2]if[/SIZE][SIZE=2] ([/SIZE][SIZE=2]m_LogoutMap.IsInstance && m_Player) [/SIZE][SIZE=2]// Jakob's instance implementation
[/SIZE][/COLOR][SIZE=2][COLOR=black]    writer.Write(m_LogoutMap.Parent); [/COLOR][/SIZE][SIZE=2][COLOR=black]// TODO: Better "kick" method that prevent break-ins and such (move to last location?)[/COLOR]
[/SIZE][SIZE=2][COLOR=black]else[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    writer.Write(m_LogoutMap);
[/COLOR][/SIZE]

Change this code to:

Code:
[SIZE=2][COLOR=black]//writer.Write( m_LogoutMap );[/COLOR]
[/SIZE][COLOR=black][SIZE=2]if[/SIZE][SIZE=2] (m_LogoutMap != null && [/SIZE][SIZE=2]m_LogoutMap.IsInstance && m_Player) [/SIZE][SIZE=2]// Jakob's instance implementation
[/SIZE][/COLOR][SIZE=2][COLOR=black]    writer.Write(m_LogoutMap.Parent); [/COLOR][/SIZE][SIZE=2][COLOR=black]// TODO: Better "kick" method that prevent break-ins and such (move to last location?)[/COLOR]
[/SIZE][SIZE=2][COLOR=black]else[/COLOR]
[/SIZE][SIZE=2][COLOR=black]    writer.Write(m_LogoutMap);
[/COLOR][/SIZE]
 
Top