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!

Ultima Live - In Game Map Editing Framework

Praxiiz

Sorceror
Ultima Live v0.50 Alpha

This is an ALPHA release! Do not use it on a live production server!!

Change Log
--------------------------
v.0.50 Alpha

Support for auxiliary maps.

(changes to this page are shown in green)

Statics are now sorted not only by their location in a block, but then by Z location, and item id.
Log4Net has been added to handle error logging.
[m is now supported for commands ([area coming soon!)
objectlist target implemented server side (Thanks -hash- for the packet info)
The entire structure has been reworked.
Some packets have been modified.
And many many more internal changes on the client side.

v0.44 Alpha
Changed the save system to save only pending changes to disk on each save. When a server loads, it will load all the change files and apply them. Use [ExportClientFiles to write the full client files out to disk with all changes. They can then be copied to the server's client file folder. Then the change files can be deleted from the /UltimaLive/ClientFiles.

To revert to an old save, the change files after that save date will have to be moved or deleted.

v0.38 Alpha
Removed the version hashing system. The hash files and folders are no longer necessary.
Added a CRC system to take the place of the hashing system.
Fixed a bug where blocks with 0 statics wouldn't update properly.
--------------------------

Summary
This project allows the modification of client map and static files while the client is running.

Requirements
This project requires Razor.
This project supports clients version 7 and above. It does not support diff files.

Client 6 may be supported at a later time, use at your own risk.

Known Issues

Maps need to be a minimum size of 1024x1024

You will experience problems if your coordinates are outside of the map size. I recommend getting to somewhere well inside the size limit of the map on an OSI map before you switch to an auxiliary map.

Razor's Auto-Queue does not play well when a player is on an auxiliary map. I will be working on this issue in upcoming releases. For now just turn off auto-queue on the More Options tab on Razor when you're on an auxiliary map.

Defrag Button is broke.

[Livefreeze works, but you will not see the changes immediately, this is really simple to fix. Look for it in the next release.

The wrapping parameter in the MapRegistry.cs is currently ignored. Its mostly there for systems to refer to.

There is no provision for wrapping in the boat system. Please explore this and post results.

There is a known issue with about a 100ms delay (it looks like your screen blacks out for a fraction of a second). This is caused by the client re-synching with the server after it refreshes. I will be working on correcting this in future releases. The only non-visual side effect that it causes is a little bit of wasted bandwidth. As of 0.50 Alpha, this has been modified, please provide feedback if you think its better or worse.

Notes

If you're not comfortable with the performance of the streaming part of this system, you can disable it by removing the playermobile modifications. You will then need to distribute your auxiliary maps as you normally would.

You would do this if you were interested in having auxiliary maps, but not necessarily the editing aspects of the system. Or perhaps you are done editing and just want your players to be able to be able to use the auxiliary maps.


You cannot run a server and a client off the same set of data files! Make a copy of your UO folder if you're running the client and server on the same machine.

The commands that are available with this release should be viewed as example commands for building player interact-able systems.. Its not very efficient to type commands to edit a map, so I will be adding a map editing component to the client end that will make editing more efficient.

For now, the example commands are as follows:

[inclandalt increment land altitude (z) by a positive or negative number at a single tile
[setlandalt set land altitude (z) to a specific number
[setlandid change the tile number of a land tile
[incstaticalt increment static altitude (z) by a positive or negative number
[setstaticalt set static altitude (z) to a specific number
[setstaticid change statid art id number
[delstatic delete static
[addstatic add a static
[movestatic move a static
[livefreeze freeze dynamic statics in a bounding box into the map
[getblocknumber tells you what block number your standing in

[circularindent radius height change raises/lowers the map (not statics) in a circular area

Enjoy, and please report bugs / issues here.

Setup Instructions
Server Side:

Drop the included server files into your customs folder, then make the
following modifications to PlayerMobile.cs

Add this section near the top of the file just under
namespace Server.Mobiles
{

/* Begin UltimaLive Mod */
public interface UltimaLiveQuery
{
int QueryMobile(Mobile m, int previousBlock);
}
/* End UltimaLive Mod */

Look in playermobile and find the SetLocation method
public override void SetLocation( Point3D loc, bool isTeleport )

Add this right above the SetLocation method:
/* Begin UltimaLive Mod */
public static UltimaLiveQuery BlockQuery;
private int m_PreviousMapBlock = -1;
/* End UltimaLive Mod */

Add this to the bottom of the SetLocation method and to the top of the OnMapChange method

/* Begin UltimaLive Mod */
if (BlockQuery != null)
{
m_PreviousMapBlock = BlockQuery.QueryMobile(this, m_PreviousMapBlock);
}
/* End UltimaLive Mod */

You will need to go into the MapRegistry and define the additional physical maps you want to use. You can see existing examples that have been commented out in the file.

You will also need to define maps in your regular server/misc/mapdefinitions.cs, for example

RegisterMap(32, 32, 32, 6144, 4096, 1, "Map 32", MapRules.TrammelRules);
RegisterMap(33, 33, 33, 7168, 4096, 1, "Map 33", MapRules.TrammelRules);
RegisterMap(34, 34, 34, 2304, 1600, 1, "Map 34", MapRules.TrammelRules);
RegisterMap(35, 35, 35, 2560, 2048, 1, "Map 35", MapRules.TrammelRules);
RegisterMap(36, 36, 36, 2560, 2048, 1, "Map 36", MapRules.TrammelRules);
RegisterMap(37, 37, 37, 1448, 1448, 1, "Map 37", MapRules.TrammelRules);

You will need to export your current maps to avoid excessive block updates. In the next release I will be re-enabling the defrag button on the UltimaLive tab so that players will not have to re-download existing maps to avoid the excessive updates.

Client Side:
Shutdown Razor and unrar the archive into the razor installation
folder. Run the included setup.bat file and restart Razor. The
setup file will need to be run after each time Razor updates.
 

Attachments

  • UltimaLive_44a.rar
    58.7 KB · Views: 128
  • UltimaLive_50a.rar
    171.9 KB · Views: 225

Pure Insanity

Sorceror
Awesome upload, I'm testing it now and it's completely working. This is very awesome and will help develop things faster. I suggest using a gump to be able to use the commands faster, also making it so you can use [m delstatic...something I had to do for myself rather fast as I believe it's needed. Thanks for sharing this awesome release. Can't wait to see what everyone comes up with now, as I'm sure this will allow making maps much easier. Few commands I'd like to see created is something to create caves/dungeon entrances. But since all of this is client side, we'll get to toy with it and create what we need. Thanks again. =D

Also about the save times. Things have increased a lot so far and all I've done is start removing some static buildings in a town. I plan to only use it when developing though, so it shouldn't be a problem.
 

Praxiiz

Sorceror
A wipe command wouldn't be very hard to implement. What I'm planning on adding eventually will be a button on the UltimaLive razor tab that brings up a window with another view of the map and some fast editing buttons. This way the client could send changes directly to the server using packets (which would be checked for access level etc on the server end). It would also allow saving structures, cave entrances, etc., and you could just drop them back onto the map as needed. This would be a plugin to the system that shard admins could install on their end to speed up map building.

I'm still investigating the God Client slowly, but I think patching any grid functionality will prove really difficult. I may try hooking the bitblt call and see if it slows the framerate down too much. If it doesn't slow things down too much, then I might be able to add in an overlay of sorts.

As I mentioned before, this project will head in two directions - the map editing aspect and the malleable world aspect (the possibilities for this are endless: volcanoes, meteor landing sites, mining, lumber harvesting, farming, random dungeon generation, etc..).

The prospect of allowing the client to handle additional maps seems to be more promising as I investigate it. Since I know the memory addresses of the map files that the client uses (i'm already hooking that functionality), I can search for them the instant the client loads. I can load additional maps into memory, and when the client changes maps, I just check to see if its a non-standard map and I change up the memory addresses and the client doesn't know the difference.
 

Thagoras

Sorceror
I'm yet to try this, by the way. I just wanted to nail a point quickly. The only real function of the grid lines is to assist in getting things lined up (on the same x and y axis). How hard would it be to instead have either a gump report the x and y of the cursor or perhaps have the cursor display the x and y like how it does item properties? I mean, grid lines would be ideal, but if you're having a hard time getting them to work...

Thank you for putting this together! Karma++
 

Vorspire

Knight
I will be happy to write a UI for this as my next project after the SuperGumps system is completed, that's if you don't have time, Praxiis :)
 

Praxiiz

Sorceror
As far as having the cursor display the coords, I'll look into it. I've been looking over the house customization system lately, trying to see if there's any functionality we could use there.
 

Warstone

Sorceror
As far as having the cursor display the coords, I'll look into it. I've been looking over the house customization system lately, trying to see if there's any functionality we could use there.
And one more thing... If you modding with your friends - you need to ship changes to every client. Futhermore, If new client login - you need to show changes to them too. I solve this problem like this: track via packets X and Y of current player and, if he get to uncached static - sends to client CRC of 5 blocks of tiles (all line of blocks, that will be cached), before player get to it... Then client analyze this CRC and if it's wrong - it sends back 5 CRC of all 5 blicks. Server analyze this and send back contents of changed blocks.
This way gives me online static patch system, that can be used in your scripts (like terraforming for additionl houses or some geo-magic... Imagine... 10 casters cast one spell that... Bring a volcano to map)
 

Praxiiz

Sorceror
Right now I'm using a single byte version number for each block. When a player changes blocks, the server sends out a request to the client to send back version numbers. If the server gets the response from the client, it checks the returned version numbers against its own stored version and sends the appropriate blocks. When the client receives an updated block, it saves the version number, map and static data. Any changes that are made to a block result in the version number of that block being incremented on the server end and block updates are send to all nearby clients.

Both methods are similar. There's a trade off between CPU time, bandwidth usage, and storage space. Depending on which CRC you are using, you could be using from 2 to 4 bytes per block. (See http://en.wikipedia.org/wiki/List_of_checksum_algorithms) Using a version number and storing it on both the client side and the server side uses more storage, but less CPU and less network bandwith. (I'm sending one byte per block and I don't have to calculate any CRCs). On the other hand, if I did calculate a CRC every time, I wouldn't have to worry about storing the version/hash files, which could reduce save time slightly. The tradeoff is that I would then have to read the entire block (map and statics) and perform a CRC calculation (both client and server side). Now I could cache each CRC and only update it on a block change, that could save on CPU time, at the cost of some additional space.

Right now, a request from the server to the client asking for version numbers takes 15 bytes, and a response takes 43 bytes. It isn't optimized because I'm asking for the surrounding 25 blocks of the player (see the previous discussion thread). If I optimized it by only requesting blocks in the direction the player moves (5 blocks in a line if the player changes blocks horizontally/vertically, or 9 blocks if the player changes blocks diagonally) it would reduce that 43 down to 20 bytes or 24 bytes respectively. This optimization also complicates the algorithm because it would have to start checking for direction of the block change and figure out which blocks need to be checked. I could probably also reduce the initial query from 15 bytes to 9 bytes if I used a different packet (0x61 for example), but I haven't checked to see if most clients drop this packet yet.

If I started using a two byte crc and I optimized the algorithm to only check blocks in the respective direction, then the client response would be 25 bytes for vertical/horizontal (15 bytes for base packet + 2 bytes * 5 blocks) or 33 bytes if the client moves diagonally (15 bytes for base packet + 2 bytes * 9 blocks).

Calculating a CRC would most likely be a better way to go than block versions, but right now the block version functionality is already in place, and I think my time would be better spent building up some of the other functionality of the system. I can come back and optimize this later.
 

Praxiiz

Sorceror
On the other hand, if you have a good algorithm for calculating the CRC of land and static block, I'd be more than willing to take a look at it :)

Edit: I was just looking over this page ZLIB Technical Details, and it seems like it only supports 32 bit CRCs.
 

Praxiiz

Sorceror
I have been working on figuring out a grid system. I have tried hooking bitblt in hopes of figuring out a good way to append grid lines to the display, but I am having a hard time figuring out how I would calculate where the tiles are on screen among all the calls to bitblt. The client seems to use screen size rectangles and takes sections from each of them. I was able to outline them, but differentiating them from even the mouse graphics has proven really difficult.

On the other hand, I have been able to make a virtual type grid by sending statics to the client from razor. They don't reside on the server, so they're for display and targeting purposes only. I can add in the functionality to raise and lower them, and they could be used to help target and line things up.

The other option would be to build another window that could be used to target more effectively, but it means switching back and forth between the client and the window.

Bitblt experiments:


Grid using client side statics:
 

-hash-

Sorceror
Calculating a CRC would most likely be a better way to go than block versions, but right now the block version functionality is already in place, and I think my time would be better spent building up some of the other functionality of the system. I can come back and optimize this later.

The file formats/network protocols accommodate versioning. With map.mul, what most file format documents refer to as the "header" is actually a version number. With staidx.mul, the "extra" field is the version number. Implementation varies between clients. Derivation of the version number for these files is likely related to a tick count of some sort. Related packets include 0x3E (Versions), 0x45 (Version OK), and 0x4B (Check Version).

If you like, I can explain a bit more in depth how the built-in version system works; though, it's mostly arbitrary.

These days, the versioning functionality is simply an artifact of the God Client and certain older (very old) end-user clients. So, any implementation specifics would be up to you.

p.s. Through all these years, I've even looked into the file mapping of the client and never once did I think, "Gee, I can just hook here!" Good for you :)
 

Attachments

  • example-1.jpg
    example-1.jpg
    38 KB · Views: 184

Praxiiz

Sorceror
I would love an explanation of how the built-in version system works. I actually didn't even realize the client had its own version system. I really appreciate it.
 

Praxiiz

Sorceror
Also - I am experimenting with a transparent form that always sits on top of the client (much like the OpenUO mining script screenshots shown in the other thread). The main problem I have with this approach is that if you move your game screen around, the grid is thrown off. If anyone has a reliable method of detecting where on the screen the game window actually is, I might be able to make this work well.
 

Pure Insanity

Sorceror
What about people that use different resolutions for their game screen? Too much could mess it up. You'd have to hook into the client to grab where the game screen is, and the size.

The overlay thing you was doing could end up being a good idea. Maybe try using the Blood tile, or another similar transparent tile from the client. Use it as the overlay, and hue it something that looks good and you can see through. Think it'd show the grid very well. Also, how will you display the grid? A command? Is it for anyone in that area, or just the person that wants to see the grid?
 
Top