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!

In Game Map Editor

Status
Not open for further replies.

Jeff

Lord
This is some really interresting work you've been able to develop
Being able to make map changes in realtime and even stream that to players is incredible, might even consider the management to implement it as a sub-feature in razor so no custom installation is required for players on a server.
"The management" has been out of the office for 3+ months, I'm pretty sure "the management" doesn't care.
 

Hiring Man

Sorceror
lol awesome features don't get added to minimally maintained projects

Your absolutely right. But they can be suggested to "the management" who can take it into consideration? And than if good enough and decides okay, sure why not. Can and hopefully take some time out of "the managements" day(s) to add this feature. Does not mean "the management" will but we could all wish and hope and try contacting like we do with ConnectUO and Runuo And Ryan etc.

None different, where still waiting for these problems but waiting is life i guess :/
 

KHzspeed

Sorceror
Your absolutely right. But they can be suggested to "the management" who can take it into consideration?
I'm sure they will too..
 

Warstone

Sorceror
Hi Praxiiz.

1st of all - great job.

I'm wonderring... How you get this? Right now I know how to syncronize static from server to client (original client)... It's done by WinAPI hook and "make all static dynamic" for client. But how you ship map changes to client?

BTW... Right now I'm working on UOExt (Hook dll that can change protocol and/or reaction on it from client)... And this feature (online map data change) is what i want ))) You can see source here: http://code.google.com/p/uo-ext/
 

Praxiiz

Sorceror
I'm using the God Client packets 0x3F and 0x40. They are meant to ship blocks back and forth and are simply ignored by regular clients. I'm intercepting them at the client end and then updating the clients memory to include changes.
 

Praxiiz

Sorceror
Status Update:
I refactored the way I was implementing commands on the server end. Its now possible to do a series of changes and only send out updates at the end of the series.
I've been doing quite a bit of house keeping on the server end code, getting it ready for release.
I refactored the hashing system I was using - now I'm only using a single byte per block hash instead of two. This cut down the amount of data from hash queries that the server sends by close to half.
 

Warstone

Sorceror
I'm using the God Client packets 0x3F and 0x40. They are meant to ship blocks back and forth and are simply ignored by regular clients. I'm intercepting them at the client end and then updating the clients memory to include changes.
But you need to "patch" every client you play on...

For statics: hook MapViewOfFile and return 0 pointers. This will force client not to show static on map. Then, when player get to map (search player movment packets to determine where it right now), just send to client (from local proxy) static from server. And check if it changed on server.

This approuch dosn't bind to client version and one code works for every client. But I can't find smth like that for map info.
 

Praxiiz

Sorceror
I'm hooking CreateFile and MapViewOfFile. I pass valid pointers back to the client from both functions. This allows the client to show all statics on the map. I use a predictable shared name for the file mapping. Then I use Razor to intercept the two block packets. When razor intercepts a block packet (0x3F and 0x40), I alter the memory directly and force the client to refresh its static and map cache. This approach doesn't bind to a single client version, and no patching is required. It allows me to alter the mul files directly (not just map files, but any of the mul files) while the client is running.

I hope this helps, I think you're project will be extremely useful. Is your project able to translate packets from older clients to work with newer servers?
 

Warstone

Sorceror
Is your project able to translate packets from older clients to work with newer servers?
Yes it can, but you need to write your own translation plugin.(I try to separate plugins so they do not need to be under GPL license, because they do not use core code :p )
I'm hooking CreateFile and MapViewOfFile. I pass valid pointers back to the client from both functions.
It won't work if you get bigger statics for tile square. You need to grow statics file size, but MapViewOfFile set mapping size so, last bytes of staticsX.mul will be unreadable. Correct me if I'm wrong.
Another one thing that might mentioned... Base MapViewOfFile do not load file into memory on start (If msdn is right), but when you create Named map - it will do. So... You grow memory amount, needed for client. Right now i can enlarge map 4 times (it need to change map sizes in client). Right now map0.mul has 300Mb+ size... And with your approuch we may not fit into memory. (Again - correct me if I not right).
I alter the memory directly and force the client to refresh its static and map cache.
"force the client to refresh"... How you do this? When I plan to change map, I found only one method to refresh map - tele out from client cache and tele back. How do you do this?
 

Pure Insanity

Sorceror
He's injecting the memory into the client it's self. So it's forced to see the update. At least that's what I've been able to understand from this. If it works like promised...I can't wait for a release. Really think it would go over well on the shard I'm working on...As I want to give them the ability to colonize/change the maps.
 

Warstone

Sorceror
He's injecting the memory into the client it's self. So it's forced to see the update. At least that's what I've been able to understand from this. If it works like promised...I can't wait for a release. Really think it would go over well on the shard I'm working on...As I want to give them the ability to colonize/change the maps.
If you write smth into memory(map memory) client won't show changes before game situation need to redraw map. That's what i saw from client debug. So, you need to force client to redraw. Or... You need to inject into local cache, but i'm hardly understand - how you can find it.
 

Praxiiz

Sorceror
I'm hooking CreateFile and MapViewOfFile.

Sorry, I was mistaken when I wrote that. I'm not actually hooking MapViewOfFile, but I'm hooking CreateFileMapping.

Here's a general overview of what I'm doing on the client end:

I hook CreateFileA and in my function that gets called instead of CreateFileA, I parse the filename and see if it contains .mul.
If it contains .mul, I change the desired access from read only to read/write. I change the sharemode to 0x3 (fileshare read/write)

I then hook CreateFileMappingA (I wrote the wrong one in my post above).
I get the filename to parse, (you can see how to do this here: http://msdn.microsoft.com/en-us/library/aa366789(v=vs.85).aspx)
Be sure to unmap the view you make when getting the filename, and clean things up.

After I have obtained the filename, I parse it again. If it contains static*.mul, I call CreateFileMappingA and pass it INVALID_HANDLE_VALUE. This causes the mapping to be put on the system paging file. I give it a size that is 1.5 bigger than the original statics file. Then I copy the contents of the original static file to the memory map I created and I pass it back to the client. Now the client has a view that is larger than the original file, but its index doesn't point to any bytes that are beyond the end of the original file. When I intercept one of the two update block packets, I check to see if the new statics size is larger than the existing one. If it is, I write the new block to the end of the filemap, and I update the index. I also write the change out to the original statics file (which isn't mapped at this point). This causes fragmentation in the original statics file (spaces of data that are not being used). To resolve this, I plan to defragment them periodically. To do this I will just read the index file block by block, building a new file with all the blocks in order and the fragments gone.

If the file isn't a statics file, I simply call CreateFileMappingA with flProtect set to 4, and I set the lpName to the filename.mul and I append the Process ID to the end of it. (This allows me to find the mapped file in Razor, and it can be found for multiple clients because they have different process IDs)

After I receive a block update, I update the corresponding portion of the file maps. Then I need to force the client to refresh. Now this took me a long time to figure out. In fact the entire project depended on being able to force the client to update at an arbitrary time. To figure it out, I did a lot of searching with OlyDbg and Ida. I received some help from www.JoinUO.com, they helped me find the packet tables and some other very useful things. Up until just recently I was creating a remote thread to call a specific function inside the client, but I found a better way.

To force the client to refresh, all you have to do is send it a reject movement packet (0x21) passing zero as its sequence number and pass it a valid location for the player. Then immediately after, pass the client a move acknowledge packet (0x22) with a sequence number of 0, and a status of 0. I do this from razor immediately after I receive a block update. (I actually send both packets in a clump) In this way I don't have to send any extra packets from the server. I'm assuming you would send these packets from your local proxy.

The result is that the client updates its local cache and you see your changes immediately on screen. Now you may see your screen flash black for part of a second when it does this, but that's a small price to pay for the functionality.
 

Warstone

Sorceror
Ok. I'm understood... Thanks for packet-way cache update, I'll use it, but my static algo is bette (I think). 1st of all... I'm hook CreateFileA, when client try to read Login.cfg (right now i think that no Razor and/or other thing around) I'm starting new thread in client's address sapce. In that thread i run proxy... Listening localhost port and connecting to server. When it init, I'm "filling" client with smth like this: 127.0.0.1,<RndProt, getted from Proxy>. Now client talk to me, and I talk to server.

This way allow me to make complex calculation when packet arrives, because it dosn't freeze client (separate thread) and player can do 4-5 steps... It's about 1 second of real time... It's a big time for calculations...

Now... When client loading statics, I'm forcing him to load staticsX.mul with 0 bytes size, and I fill with zeros mapped (MAP_COPY) staidxX.mul file. Now client think that there is no static on map. Next step it hook client movment with proxy, determine it's position and send to client all static as "dynamic"... It's like "all static came from server". Now i can sync map blocks (CRC from 5 blocks is sending to server, when player get to new block, and it it's wrong - static sync begins), while client see original static, I can modify this static, play complex animation with static... etc, etc, etc...

This way may be too clumsy, but it can't be broken by long play or "too many new static". But it can be used only for static. Because there is no map packet, "that came from server".

And again... Thanks for "force client refresh" method. This is only one thing that stops me.

BTW, you can join to my ExtUO project... It's always need fresh minds.

PS: Sorry for my english if it's not well... I'm from Russia. (Yes, vodka, balalaika and "In soviet Russia..." :p )
 
Status
Not open for further replies.
Top