Current ToDo

The latest version of the UOAI library and all directly related information can be found in this forum.

Re: Current ToDo

Postby Artaxerxes on Mon Nov 24, 2008 6:48 pm

Carpates wrote:Very nice job with the event handling. Any chance we can get an onMobileExit, i.e. when a Mobile leaves the field of view?

I'm currently working on implementing more events. Notice that a lot of events I did not list above, are already included in the above version, but don't get triggered yet. One of those events, which does not get triggered yet, is UOMobile.onDeletion(). Once implemented, that will be sort of the same as your onMobileExit, since the mobile object will be deleted when it walks off the screen (or through a gate or logs out or...).

By the way, a small annoyance: 'targetting' vs. 'targeting.'

Thank you! An IDE should come with a built-in spelling-corrector!! :D
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

Re: Current ToDo

Postby arul on Tue Nov 25, 2008 12:56 am

Works like a charm now :) Also thanks for the comprehensive explanation.
arul
Pro
 
Posts: 107
Joined: Thu Nov 20, 2008 2:03 am
Location: Prague

Re: Current ToDo

Postby Artaxerxes on Tue Nov 25, 2008 10:47 pm

FYI: Didnt get the rest of the event-code and packet-filtering to work completely yet, and I have two quite busy days ahead of me, so you shouldn't expect a next version of UOAI before friday.
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

Re: Current ToDo

Postby Artaxerxes on Wed Nov 26, 2008 7:55 am

FYI: some info on the current issues. Any constructive critisism is welcome.

How packet-events work in UOAI:

I have packet-hooks set client-side, basically a hook of the client's packet-handler function which receives decrypted and decoded packets, and a hook of the function the client uses to write packets to its network-buffer.

Basically those hooks will duplicate each packet, and then inform UOAI about the packet + pass the memory-address where I can find the duplicate of the packet. The client then continues, while UOAI handles the packet and takes care of cleaning up the duplicate client-side. This "informing" of UOAI is done by posting a windows message to UOA (Posting not Sending! so the client doesnt wait for UOAI to handle the packet!). Actually each client gets a seperate Inter Process Communication Thread (IPCThread) in UOAI, which has a windows message loop that receives those windows messages informing them about received and sent packets and where to find the duplicates.
Now such an IPC thread, will do this: it will locally duplicate the packet, clean up the remote duplicate, check the packet-type (cmd-byte) and then call a packet-handler in UOAI for that type of packet. The packet-handler then parses the packet and invokes the correct events in your program if any callbacks where set.
Now the IPC through PostMessage, which means UOAI events are not synchronized with the client, works smoothely, cause the client does not have to wait for UOAI to handle the packet and therefore there are no noticeable slow-downs. But it also has the major problem that the packet needs to be duplicated client-side.
That is a problem, because the packethandling/event-invocation in UOAI is slow compared to the client's handling function (since I need to do a lot of extra work to get the information to your app). This means that the client is duplicating packets faster than I am cleaning them up or, and I cannot handle the IPC-messages as fast as they arriving in the IPCThread. So basically there is a bottleneck in the IPCThread.
I can solve this by calling the packethandlers asynchronously from the IPCThread. Then the IPCThread for a client just simply reads the remote packet to UOAI, cleans up the packet client-side and then calls the packethandler async-based, so it doesnt wait for it to return. Probably I'll use a pool of working threads there, cause setting up a thread per packethandler would spawn a load of thread, where UOAI already has a lot of threads running (main thread, thread listing clients, COM-threading pool used to make free-threaded calls to objects in UOAI, an IPC thread per client). I will use a sync'd queue to communicate with the worker threads. The number of worker threads to handle packets will vary by killing inactive workers and adding new ones if a bottleneck is detected. This is a lot to code (also since I was kind of sloppy in some of the updates i recently made to the event-triggering, so I think I'll recode all of that from scratch), and i don't have too much time the next two days, so I can give no ETA. Most likely a first version will be available by friday night.
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

Re: Current ToDo

Postby arul on Wed Nov 26, 2008 9:00 am

So basically we need some kind of a packet queue, where packets will be stored before they'll be asynchronously dispatched to the client?
Assuming that the client enqueues the packets faster than we manage to dequeue them, in a long run, the packet queue will grow to an enormous size. So the queue will probably need to be a sort of circular buffer, by which we loose some packets.

Events are fine, but for this particular purpose (maybe for the packet filters too), wouldn't it be better to use raw function pointers instead of them? I realize that events are function pointers on steroids in particular, and even though the overhead of events over function pointers may be very little, we're in a section of code that gets executed so much that even little overhead eliminated might mean significant performance gain.
arul
Pro
 
Posts: 107
Joined: Thu Nov 20, 2008 2:03 am
Location: Prague

Re: Current ToDo

Postby Artaxerxes on Wed Nov 26, 2008 12:22 pm

I think doing things asynchronously will already improve things a lot, cause I can tune the number of worker-threads to prevent a bottleneck.
Also I can drop packets much earlier in the process than I do now, whenever your application has no eventhandler installed for an event triggered by that packet.
Also, I think I'm going to remove events on UOItem, UOGump and UOMobile and instead put all events on the UOClient object, passing the id, not the item, gump or mobile, that moves the lookup of the item from the IPCthread to your code.
Its eventually not really a bottleneck, just a 'potential bottleneck', in that in some situations specific packets will be handled a lot slower, creating a global bottleneck, since all events were handled synchronoulsy in the IPC thread. Distributing the work on different worker threads through an eventqueue (so that a 'bottleneck' for one type of packet does not get the whole event-handling stuck), and moving things like looking up items to your code by just passing IDs, rather than objects, should already solve the problem, though i'm not sure yet.
So I might still get it to work. If I can get the average handling time below or at the average time it takes the client to handle a packet, then there will not really be a problem.

I think a better idea than function pointers, would be a UOEventQueue object that you can create, which allows you to specify which packets to filter. Then the performance would just depend on how many different packets you look for at the a time.
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

Re: Current ToDo

Postby arul on Thu Nov 27, 2008 1:49 am

Artaxerxes wrote:Also, I think I'm going to remove events on UOItem, UOGump and UOMobile and instead put all events on the UOClient object, passing the id, not the item, gump or mobile, that moves the lookup of the item from the IPCthread to your code.


Yes, that might help - UOClient is aware of those objects at the time those events are fired, so instantiating them over and over again is not necessary. Which also involves implementing some kind of entity cache at the UOClient (or it's wrappers) level. I think that might actually help a lot, since the CreateSomething functions are probably the most expensive elements in the call chain.

Artaxerxes wrote:Its eventually not really a bottleneck, just a 'potential bottleneck', in that in some situations specific packets will be handled a lot slower, creating a global bottleneck, since all events were handled synchronoulsy in the IPC thread. Distributing the work on different worker threads through an eventqueue (so that a 'bottleneck' for one type of packet does not get the whole event-handling stuck), and moving things like looking up items to your code by just passing IDs, rather than objects, should already solve the problem, though i'm not sure yet.
So I might still get it to work. If I can get the average handling time below or at the average time it takes the client to handle a packet, then there will not really be a problem.


Yes, I agree that asynchronous events will help a lot, especially considering that synchronous event are almost of no practical use at all.

Artaxerxes wrote:I think a better idea than function pointers, would be a UOEventQueue object that you can create, which allows you to specify which packets to filter. Then the performance would just depend on how many different packets you look for at the a time.


I'm still thinking of a packet logging app, sou I'll be actually collecting all packets :)
I mentioned function pointers because that's probably the fastest way how to deliver packets from UOAI to the user app - the queue or such similar structure can be implemented at the user app level.
arul
Pro
 
Posts: 107
Joined: Thu Nov 20, 2008 2:03 am
Location: Prague

Re: Current ToDo

Postby Artaxerxes on Thu Nov 27, 2008 8:07 am

arul wrote:Yes, that might help - UOClient is aware of those objects at the time those events are fired, so instantiating them over and over again is not necessary. Which also involves implementing some kind of entity cache at the UOClient (or it's wrappers) level. I think that might actually help a lot, since the CreateSomething functions are probably the most expensive elements in the call chain.

You're right there. I did not start with an entity cache at first, cause that required synchronization code (since multiple programs and threads would be using the same objects), that synchronization was added afterwards when I made the step from a single threaded to a free threaded com-model. Since I needed such an entity cache anyway to trigger events on a specific object (other than UOClient and UOAI, which already had such a cache), so the first thing I did when implementing more events was adding the cache for UOItems (a collection in the form of a synchronized AVL balanced binary tree, key'd by the ID of the item). I'll also add such a cache for UOMobiles and UOGumps, for most other things it should not matter. I think I will pass objects directly and trigger specific events on object different from a UOClient too anyway, cause the lookup in the entity cache does not seem to make the difference.

arul wrote:Yes, I agree that asynchronous events will help a lot, especially considering that synchronous event are almost of no practical use at all.


I ran a few tests already and it seems to improof a lot. Packets are now queued in a sync'd queue, there's always 1 async handler thread running and new ones are spawned (at max every 1 second) if too many packets are in the queue when adding a new one (currently it's something like 10, but thats not an optimal value probably). Also an async handler thread will shut down itself if it has to wait over half a second before getting anything from the queue. (1 thread is always running).
This solves multiple problems : when the server sends a load of packets (f.e. if I log into an over-crowded britain bank area) the work gets distributed; and also it prevents lockdown when eventhandlers in a user's app take too long to handle the event or don't return at all.

arul wrote:I'm still thinking of a packet logging app, sou I'll be actually collecting all packets :)
I mentioned function pointers because that's probably the fastest way how to deliver packets from UOAI to the user app - the queue or such similar structure can be implemented at the user app level.


With the improvements i'm currently working on, I think the onReceive and onSend events as already present will just stay as they are and work quite well.

It will still take some time though, i found a serious bug in the current version, where packet-handlers didnt get unadvised correctly (removal of packet-handlers) cause i was passing information in an incorrect form to the automation libraries (they actually crashed on an access violation, but somehow no error was generated in the compiled version)... it took me quite some time to figure this one out, so still a lot of work left on the eventhandling.

Greetz,
Artaxerxes
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

Re: Current ToDo

Postby arul on Thu Nov 27, 2008 5:57 pm

Artaxerxes wrote:a collection in the form of a synchronized AVL balanced binary tree, key'd by the ID of the item


This may be opening a can of worms, but wouldn't hashtable perform better in this case? Computing the hash would be easy - simply return the entity's Serial/ID.
arul
Pro
 
Posts: 107
Joined: Thu Nov 20, 2008 2:03 am
Location: Prague

Re: Current ToDo

Postby Artaxerxes on Thu Nov 27, 2008 8:02 pm

arul wrote:
Artaxerxes wrote:a collection in the form of a synchronized AVL balanced binary tree, key'd by the ID of the item


This may be opening a can of worms, but wouldn't hashtable perform better in this case? Computing the hash would be easy - simply return the entity's Serial/ID.


Sure an optimized hashtable would perform the best here measured in running-time, but it will have a larger memory footprint, so I'd need to optimize the size. I would have a hashtable per client so for multi-client apps and scripts that would make a lot of difference in UOAI's memory-usage. Also, optimizing the size will be hard, if not impossible... a hashtable performs bad if its size is too small, if its too large you're wasting memory... but in the case of UO the number of objects, gumps and mobiles can largely vary depending on the context: on a typical shard at brit bank there are loads of mobiles and items, while a lonely miner's cave will be much less crowded... a crafter script would use loads of gumps, whereas other scripts probably don't... so I think there is actually no optimum size, meaning that I'd have to choose one that is too large to get linear performance, resulting in a large memory-footprint.

I mostly use an avl balanced binary tree as a general collection object, since both it's memory footprint and average lookup and insertion time are always acceptable.

By using such a balanced binary tree I don't have to spend time on optimizing the datastructures. If the lookup-time would need to be really really fast, than a hashtable will be the next choice, but i don't think that will happen. Optimizing these datastructures will be done later on, when UOAI reaches a more stable version.
Artaxerxes
Site Admin
 
Posts: 530
Joined: Tue Nov 18, 2008 9:51 pm

PreviousNext

Return to UOAI Developers Forum

Who is online

Users browsing this forum: No registered users and 1 guest

cron