Re: [crossfire] Map cache
On Thu, 2005-08-25 at 18:57, Anton Oussik wrote: When the server comes across maps containing large amounts of items it takes a long time to load. While it is loading the whole server freezes for other players, and on very large maps (like the scorn sale shop or some apartments) this can take in excess of 10 seconds on metalforge. The way I would solve this is to have the map loader count how many objects it has loaded. After some fixed number, it would stop until the next time tick. There would be no processing of objects, including players, on a partially-loaded map. This would eliminate the lag for everyone except the player entering the cluttered map. I'm just not sure if this is less work than the other approaches to solving the problem that have been suggested. --PC ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
Preston Crow wrote: On Thu, 2005-08-25 at 18:57, Anton Oussik wrote: When the server comes across maps containing large amounts of items it takes a long time to load. While it is loading the whole server freezes for other players, and on very large maps (like the scorn sale shop or some apartments) this can take in excess of 10 seconds on metalforge. The way I would solve this is to have the map loader count how many objects it has loaded. After some fixed number, it would stop until the next time tick. There would be no processing of objects, including players, on a partially-loaded map. This would eliminate the lag for everyone except the player entering the cluttered map. I'm just not sure if this is less work than the other approaches to solving the problem that have been suggested. the problem is that the loader itself stores state information. So, while unlikely, you could get this case: Player A enters some exit. Map starts to load, gets to point, and returns expecting to continue next tick. Player B enters map, laoder kicks in, this map finishes. Next tick, when it tries to resume loading map A, the lex state is messed up, so the load fails or you get weird results. Now this can obivously be fixed (is any map in proces of being loaded but not finished, etc). Many years ago, I tried this approach, but never to it stable enough. I'd still be more tempted to just go the threading approach - to cover the above case, its probably not too far from getting a threaded system anyways. Its how well you want a threaded server to scale that becomes an issue. One coud just set some very broad thread locks - easier to do, but means it wouldn't scale as well. But at a first pass, and given current loading, that may be good enough. --PC ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
Brendan Lally wrote: On 8/27/05, Mark Wedel [EMAIL PROTECTED] wrote: You are probably correct, it is certainly easier to let Moore's law do the work instead. It is likely that if there ever were a compelling reason to split load across servers it would be to reduce bandwidth usage from each server. But that then requires a fair amount of client logic (move from server A to server B for all communication). Something like that could almost be easier - you could for example have pup land all operate on one server, and so on. When a player moves/leaves pupland, data is tranfered to the other server (player file) as well as telling the client to talk to the other server. In this mode, the amount of work is less - each server pretty much operates independently - what is needed then is code for things like chats/shouts/tells to get passed across servers. but still not convinced if that is necessary. Leaf/Basic - is it possible to set up some form of cpu usage monitoring on metalforge? I know solaris has 'sar'. I'd mostly be interested in seeing on a whole what percentage of the cpu is actually being utilized. Because I think, with current number of players, on the whole, cpu is probably still mostly idle. Exceptions abound - I'd imagine map loads/save generate a small spike, and various 'hundreds of spells' also spikes the cpu, but those are exceptions and not general case. Only if the cpu was actually operating at a high percentage of use would distribution of servers actually make sense (and/or server generating enough traffic to saturate its link, but then a different server only makes sense if that server is on a different link - hosting 2 servers at the same ISP wouldn't help out) Putting objects into misc containers adds a bunch of complication and not sure it really helps out. If another axe is dropped, then presumably you have to check all the objects in the 'axe container' to see if it merges with anything, so loose any efficiency by having fewer objects. Also, having that pseudo container would seem to result in other oddities, like special handling when player tries to pick it up, etc. Well, there was a reason that I did specify that these would have to be no_pick, so that there is no concern with picking it up. (you don't pick up a pile of things, merely the individual items in it.) Ok - but I'd say that makes the user interface a little clunkier - containers now magically show up that the player has to open/close to get the items in/out of. The merging would have to be done anyway, but it may be easier to do it on opening the container, especially if items are often dropped there but rarely retrieved. But presumably any item dropped should merge with the object in the container (that was the point of that container in the first place). In that case, logic is still basically the same - have to examine all objects on the space to see if any of hte right type exist, as well as any containers. If we get any of these special containers, then have to examine objects in that. This also creates a complication that at some point, the code has to decide 'there are 15 different axe like objects here - lets create a container for them'. I think it'd actually make more sense to make this an in game feature. EG, you can buy weapon cabinets at the store, and perhaps have different types (sword cabinet, axe, etc) so item would go in right cabinet. One thing that would probably need to be coded is being able to drop the weapon on a space with all those cabinets and have it go in the correct one (this works for inventory since you can have active containers in your inventory), but not on the ground right now. Maybe using the walk_on flag could be used for this - if set on a container, do appropriate checks and insert object in container if appropriate. It might be possible to 'compile' maps, to take the map files, and run a seperate program on them which would 'load' the maps like the server does currently, and output the raw values of the object structs with the relative place of pointers so that the server on loading the maps can merely dump the items into an appropriatly size array of structs and offset the pointers properly. The problem is that a recompilation of the server (or even new objects) results in those pointer locations being different. So that doesn't work well. My thought for a binary form would be something like: 2 bytes - describes variable/field (eg, hp, sp, etc) - this is basically handled by a table/nice long switch statement. 1 byte - describes type of value (1=8 bit 2, 2=16 bit int, 3=32 bit, 4=float, 5=string, etc) data itself - variable number of bytes determined from above. For strings, length is prepended so loading string is very efficient) Some fields could also be collapsed. For example, the FLAGS could just be saved as the 32 bit values isntead of all the individual flag
Re: [crossfire] Map cache
Le Vendredi 26 Août 2005 01:45, Brendan Lally a écrit : On 8/25/05, Anton Oussik [EMAIL PROTECTED] wrote: A solution I propose is to pre-load large maps and keep them around in memory in case they are needed. quite difficult to implement :) Depends what you mean by pre-load. It might be possible to have the server check each map when it loads, and then load all maps on exits leading of from that map in an independent thread. Of course this requires that the map loading be thread-safe, and I'm fairly sure it isn't. No it isn't as is the whole code of server. An alternitive (which might be easier) would be to have a seperate map loading thread at all times, and when a player enters an exit, not change their map to the new one, but instead place them in 'limbo' if the map isn't loaded (a unique map which is 1x1 and has no objects. Then the player object would need to check every tick to see if their map is ready. This still poses some problems with what to do with the command queue, if a player has hit multiple arrow keys, should they be discarded? This isn't as simple, whole server code is thread unsafe, this mean if you load map in a separate thread you will break about all likned lsits used in server (objet pool, live objet lists, map lists, shared strings, and static variables used in some functions). The other approach is to simply say that all the world maps put together are fairly small, compared to the amount of memory many servers have these days (and this is increasingly more and more true), so it would be possible to just load all maps at start up, and keep them loaded until they reset. There are 30x30 world maps each of them is 50x50 in size this makes 2.250.000 objects just for the ground objets I see 2 problems. First, each of the objects weighting 0.5k minimum (supposing all pointer in object structure points to null or common structures) we reach a total of 1098Megs used for background! (need to hack server to get a real value) Second, if my memory is still working well, this mean a linked list of 2.250.000 live objects in server, list which is checked on a regular basis to see if objects have some turns to play. This will slow down main server loop like hell. Perhaps a bit of work would be first to identify where in the loading code, it take time to load big maps using a profiler tool and then we can fix the real cause. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
On 8/26/05, tchize [EMAIL PROTECTED] wrote: Le Vendredi 26 Août 2005 01:45, Brendan Lally a écrit : An alternitive (which might be easier) would be to have a seperate map loading thread at all times, and when a player enters an exit, not change their map to the new one, but instead place them in 'limbo' if the map isn't loaded (a unique map which is 1x1 and has no objects. Then the player object would need to check every tick to see if their map is ready. This still poses some problems with what to do with the command queue, if a player has hit multiple arrow keys, should they be discarded? This isn't as simple, whole server code is thread unsafe, this mean if you load map in a separate thread you will break about all likned lsits used in server (objet pool, live objet lists, map lists, shared strings, and static variables used in some functions). The way you would get around that would be to have all the required linked lists of items created for the map that is being loaded seperately, then every tick the main thread asks 'are you ready to merge' (checks the value of a variable) and when that is true, it would then call a function in the main thread that took the pointers for the objects in the sub-linked lists and inserted the whole lot into the main list in one go. If done properly, this isn't slower than 1 insertion, but is a lot quicker than 2500 (as is the case for the world maps) Since the player is in limbo, there can't be any need to use the items in the interim, quite what the result of player-ob-map should be is anyone's guess though. (maybe the old map with a flag IN_LIMBO set?) There are 30x30 world maps each of them is 50x50 in size this makes 2.250.000 objects just for the ground objets I see 2 problems. First, each of the objects weighting 0.5k minimum (supposing all pointer in object structure points to null or common structures) we reach a total of 1098Megs used for background! (need to hack server to get a real value) 1098MB still sounds a lot today, but 7 years or so ago 300MB would've sounded just as ridiculous, nowadays an application that occupies 300MB is bloaty, but not stupidly so. As it is, the entire mapset would probably load into about 3-4 GB, but then servers with that amount of Ram isn't so uncommon anymore. Second, if my memory is still working well, this mean a linked list of 2.250.000 live objects in server, list which is checked on a regular basis to see if objects have some turns to play. This will slow down main server loop like hell. yeah, probably there would need to be a new set of linked list pointers that exclude floor, and the main loop to go through them instead. In total there are 3888528 occurances of 'arch' in the bigworld maps. (grep) If we assume on average that every square has one, and only one floor tile, then a little bit of bash and bc gives 2832098 ground squares. This means there is 'only' about a million squares with relevant items on them, which is still much less. Whether a server could run at full speed with that I don't know. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
Le Vendredi 26 Août 2005 13:40, Brendan Lally a écrit : On 8/26/05, tchize [EMAIL PROTECTED] wrote: Le Vendredi 26 Août 2005 01:45, Brendan Lally a écrit : An alternitive (which might be easier) would be to have a seperate map loading thread at all times, and when a player enters an exit, not change their map to the new one, but instead place them in 'limbo' if the map isn't loaded (a unique map which is 1x1 and has no objects. Then the player object would need to check every tick to see if their map is ready. This still poses some problems with what to do with the command queue, if a player has hit multiple arrow keys, should they be discarded? This isn't as simple, whole server code is thread unsafe, this mean if you load map in a separate thread you will break about all likned lsits used in server (objet pool, live objet lists, map lists, shared strings, and static variables used in some functions). The way you would get around that would be to have all the required linked lists of items created for the map that is being loaded seperately, then every tick the main thread asks 'are you ready to merge' (checks the value of a variable) and when that is true, it would then call a function in the main thread that took the pointers for the objects in the sub-linked lists and inserted the whole lot into the main list in one go. If done properly, this isn't slower than 1 insertion, but is a lot quicker than 2500 (as is the case for the world maps) as i said, this is *not* as simple (eg object pool, you have to use it, and it's access is not thread safe, the file parsing code is also not thread safe, so you can only load one map / player file at a time). There are not only linked list related to map, and map loading code does more than just loading the file (create treasures a.s.o), so lots of server functions are called during map load, and nearly 100% of them are not multithreadsafe. Since the player is in limbo, there can't be any need to use the items in the interim, quite what the result of player-ob-map should be is anyone's guess though. (maybe the old map with a flag IN_LIMBO set?) There are 30x30 world maps each of them is 50x50 in size this makes 2.250.000 objects just for the ground objets I see 2 problems. First, each of the objects weighting 0.5k minimum (supposing all pointer in object structure points to null or common structures) we reach a total of 1098Megs used for background! (need to hack server to get a real value) 1098MB still sounds a lot today, but 7 years or so ago 300MB would've sounded just as ridiculous, nowadays an application that occupies 300MB is bloaty, but not stupidly so. As it is, the entire mapset would probably load into about 3-4 GB, but then servers with that amount of Ram isn't so uncommon anymore. point taken, if you except max addressable space on x86 is 2-3G / application :D Second, if my memory is still working well, this mean a linked list of 2.250.000 live objects in server, list which is checked on a regular basis to see if objects have some turns to play. This will slow down main server loop like hell. yeah, probably there would need to be a new set of linked list pointers that exclude floor, and the main loop to go through them instead. In total there are 3888528 occurances of 'arch' in the bigworld maps. (grep) If we assume on average that every square has one, and only one floor tile, then a little bit of bash and bc gives 2832098 ground squares. This means there is 'only' about a million squares with relevant items on them, which is still much less. Whether a server could run at full speed with that I don't know. I still think we are addressing the problem from the wrong part. First as stated in another mail, this is not the big world maps which take time to load but maps with lots of objects. Second, we still haven't check which part of map loading is indee taking this time, responding to those 2 questions will give a better idea of a solution. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
On 8/26/05, tchize [EMAIL PROTECTED] wrote: Le Vendredi 26 Août 2005 13:40, Brendan Lally a écrit : On 8/26/05, tchize [EMAIL PROTECTED] wrote: This isn't as simple, whole server code is thread unsafe, this mean if you load map in a separate thread you will break about all likned lsits used in server (objet pool, live objet lists, map lists, shared strings, and static variables used in some functions). The way you would get around that would be to have all the required linked lists of items created for the map that is being loaded seperately, then every tick the main thread asks 'are you ready to merge' (checks the value of a variable) and when that is true, it would then call a function in the main thread that took the pointers for the objects in the sub-linked lists and inserted the whole lot into the main list in one go. If done properly, this isn't slower than 1 insertion, but is a lot quicker than 2500 (as is the case for the world maps) as i said, this is *not* as simple (eg object pool, you have to use it, and it's access is not thread safe, the file parsing code is also not thread safe, so you can only load one map / player file at a time). There are not only linked list related to map, and map loading code does more than just loading the file (create treasures a.s.o), so lots of server functions are called during map load, and nearly 100% of them are not multithreadsafe. The file accessing code would need rewritten a little, although, if you allow one thread to do all file loading, then it doesn't need to be done in parrallel, since the main thread will just ignore it, then there would be the whole set of structs created for the map (this includes treasure), where there would be a seperate set of linked lists created. So instead of merely having FirstObject linking to all the other objects, you have that, but also add TmpFirstObject which is the start of that component of the linked list that corresponds to the object on the loading map. Effectively two object pools, a temporary one, and a permenent one, and then you merge in the temporary pool once loading is finished. Also you have a seperate list of shared strings, with their own ref count, and a function to merge them with the main set in one operation. anyway, a rough look at the functions involved would suggest: get_linked_map would become redundant, load_map_header would be fine (if we have 1 file loading thread, the other thread won't be loading maps) load_original_map would simple need to use the replacement for get_linked_map, the main loop would need a link_map function to bring the map and all its objects into the main set of linked lists. the object code would need some changes, although mostly just to ensure a seperate set of linked lists, and to stop it assuming that everything is in the FirstObject list all the time, if a get_tmp_object is written to replace get_object, and all other functions use that, then mostly it should work. The overlay code would probably break somewhat, but they are somewhat buggy anyway, and fixing that would be something nice to do. I also don't know how well the random map code would cope with all of this (but only because I haven't looked). Almost certainly there are some other things that I have missed, but it does look like something that is somewhat short of a complete rewrite. In any event, there are going to be limits to what can be done, since /any/ mechanism is going to fail when you are above more than 1 or maybe two map loads a second with the current data structures being used (ways around that might be to seperate the idea of object size and face size, so that for instance, the entire floor of a building might be one object, repeating the same face over and over again - this would cause serious breakage everywhere though, including with existing clients and multi-headed monsters). Certainly though, I agree about the profiler point, if only to decide when certain parts of the map loading should be done, I just don't think that you /can/ make the map loading time less than about 1/10th or so of a second, when you have too many objects there (at least without changing a lot of the data types involved). ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
Le Vendredi 26 Août 2005 15:15, Brendan Lally a écrit : On 8/26/05, tchize [EMAIL PROTECTED] wrote: Le Vendredi 26 Août 2005 13:40, Brendan Lally a écrit : On 8/26/05, tchize [EMAIL PROTECTED] wrote: This isn't as simple, whole server code is thread unsafe, this mean if you load map in a separate thread you will break about all likned lsits used in server (objet pool, live objet lists, map lists, shared strings, and static variables used in some functions). The way you would get around that would be to have all the required linked lists of items created for the map that is being loaded seperately, then every tick the main thread asks 'are you ready to merge' (checks the value of a variable) and when that is true, it would then call a function in the main thread that took the pointers for the objects in the sub-linked lists and inserted the whole lot into the main list in one go. If done properly, this isn't slower than 1 insertion, but is a lot quicker than 2500 (as is the case for the world maps) as i said, this is *not* as simple (eg object pool, you have to use it, and it's access is not thread safe, the file parsing code is also not thread safe, so you can only load one map / player file at a time). There are not only linked list related to map, and map loading code does more than just loading the file (create treasures a.s.o), so lots of server functions are called during map load, and nearly 100% of them are not multithreadsafe. The file accessing code would need rewritten a little, although, if you allow one thread to do all file loading, then it doesn't need to be done in parrallel, since the main thread will just ignore it, then there would be the whole set of structs created for the map (this includes treasure), where there would be a seperate set of linked lists created. So instead of merely having FirstObject linking to all the other objects, you have that, but also add TmpFirstObject which is the start of that component of the linked list that corresponds to the object on the loading map. Effectively two object pools, a temporary one, and a permenent one, and then you merge in the temporary pool once loading is finished. Also you have a seperate list of shared strings, with their own ref count, and a function to merge them with the main set in one operation. anyway, a rough look at the functions involved would suggest: get_linked_map would become redundant, load_map_header would be fine (if we have 1 file loading thread, the other thread won't be loading maps) load_original_map would simple need to use the replacement for get_linked_map, the main loop would need a link_map function to bring the map and all its objects into the main set of linked lists. the object code would need some changes, although mostly just to ensure a seperate set of linked lists, and to stop it assuming that everything is in the FirstObject list all the time, if a get_tmp_object is written to replace get_object, and all other functions use that, then mostly it should work. The overlay code would probably break somewhat, but they are somewhat buggy anyway, and fixing that would be something nice to do. I also don't know how well the random map code would cope with all of this (but only because I haven't looked). Almost certainly there are some other things that I have missed, but it does look like something that is somewhat short of a complete rewrite. In any event, there are going to be limits to what can be done, since /any/ mechanism is going to fail when you are above more than 1 or maybe two map loads a second with the current data structures being used (ways around that might be to seperate the idea of object size and face size, so that for instance, the entire floor of a building might be one object, repeating the same face over and over again - this would cause serious breakage everywhere though, including with existing clients and multi-headed monsters). Certainly though, I agree about the profiler point, if only to decide when certain parts of the map loading should be done, I just don't think that you /can/ make the map loading time less than about 1/10th or so of a second, when you have too many objects there (at least without changing a lot of the data types involved). Don't bet on it when profile is not yet done :) And also, limiting the number of object on a specific square sould be a good thing too. It's such bizzare to be able to put 600 axes on one appartment square :s ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
On Fri, 26 Aug 2005, Brendan Lally wrote: In total there are 3888528 occurances of 'arch' in the bigworld maps. (grep) If we assume on average that every square has one, and only one floor tile, then a little bit of bash and bc gives 2832098 ground squares. This means there is 'only' about a million squares with relevant items on them, which is still much less. Whether a server could run at full speed with that I don't know. Is it the world or dungeon maps that when loaded into memory cause the server to lag or freeze? Or is it player related maps such as guilds, apartments, etc. that is causing the server to lag or freeze? I'm inclined to say player related maps. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
The file accessing code would need rewritten a little, although, if you allow one thread to do all file loading, then it doesn't need to be done in parrallel, Thread creation and destruction is cheap. If it is to be done it might as well be done properly. With one map loading thread the server will scale up to 2 processors. With m maploading threads, where m is the number of maps loaded, it will scale up to n processors, where n = m+1. If a multi-threading of the server is to be done another aspect that may be looked into is the actual object processing. If there were several threads working away at object processing each tick they would be able to do the job much quicker on a parallel system. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
On 8/26/05, tchize [EMAIL PROTECTED] wrote: And also, limiting the number of object on a specific square sould be a good thing too. It's such bizzare to be able to put 600 axes on one appartment square :s Maybe, but how do you impose a restriction? Can I store 50 cauldrons? 10,000 platinum? 30,000 diamonds? Restriction by nrof would not work very well. Another approach is to restrict by weight, but that would not work either, as some things are small and heavy, whilst others are bulky and light. On the other hand if implemented people would be more willing to pay more for more appartments, would explore more, stockpile less, and maybe would even start using banks and imperial notes. I'm open to ideas. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
[crossfire] Map cache
This is an idea I had for a while now, but never had time to implement. If someone has some spare time and wants something to code something for CF they can try this. When the server comes across maps containing large amounts of items it takes a long time to load. While it is loading the whole server freezes for other players, and on very large maps (like the scorn sale shop or some apartments) this can take in excess of 10 seconds on metalforge. A solution I propose is to pre-load large maps and keep them around in memory in case they are needed. Whilst this option would improve the performance of the server like metalforge, it would cause servers with small physical memory size to start swapping the cached maps, and it would then not be beneficial. Therefore the map caching should be optional and off by default if implemented. Casper ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire
Re: [crossfire] Map cache
On 8/25/05, Anton Oussik [EMAIL PROTECTED] wrote: A solution I propose is to pre-load large maps and keep them around in memory in case they are needed. Depends what you mean by pre-load. It might be possible to have the server check each map when it loads, and then load all maps on exits leading of from that map in an independent thread. Of course this requires that the map loading be thread-safe, and I'm fairly sure it isn't. An alternitive (which might be easier) would be to have a seperate map loading thread at all times, and when a player enters an exit, not change their map to the new one, but instead place them in 'limbo' if the map isn't loaded (a unique map which is 1x1 and has no objects. Then the player object would need to check every tick to see if their map is ready. This still poses some problems with what to do with the command queue, if a player has hit multiple arrow keys, should they be discarded? The other approach is to simply say that all the world maps put together are fairly small, compared to the amount of memory many servers have these days (and this is increasingly more and more true), so it would be possible to just load all maps at start up, and keep them loaded until they reset. ___ crossfire mailing list crossfire@metalforge.org http://mailman.metalforge.org/mailman/listinfo/crossfire