• Advertisement
Sign in to follow this  

marking world objects as "currently being used" by players

This topic is 713 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

marking world objects as "currently being used" by players

 

in caveman, there are a number of crafting actions that require more tool and parts than the player can carry at once according to encumbrance rules.    so far my solution to this has been to allow the tools or parts to simply be dropped nearby on the ground somewhere.    but unless i mark the items as "in use", another band member can come along and use the same item at the same time (such as an anvil stone for example).  

 

the problem with marking them is un-marking them if the crafting action stops for whatever reason.    for actions that use anvil stones, i was able to get away with modding just the the code for actions that use anvil stones, and the setaction(current_bandmember,DONOTHING) code that stops all actions.

 

currently, building huts and rafts also allows objects to simply be nearby, but does not mark them in use as i recall.

 

the thing i don't like about this solution is its like new()ing something in one place, then having to dispose() it in half a dozen different places. its easy to miss one and introduce a bug. the item never gets un-marked, and you can never use it again, it always says "this item is already in use". but i can't really think of a better way to do it.

 

any suggestions?

 

i suspect the "mark and un-mark" algo is about the best that can be done, and you just have to make sure you "cross all your i's and dot all your t's" so to speak.

 

IE its a variation on the old dynamic memory allocation problem of making sure you free everything you malloc, where the best defense is to do it as little as possible - ideally in just one place - or even better - not at all  (don't have to free what you never malloc).

 

has anybody come up with a "not at all" solution for this case yet?  <g>.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
Advertisement

Can't an object remember its last owner, and only become available again if he either gives the object explicitly to someone else, or if he leaves it (distance becomes larger than x)?

Share this post


Link to post
Share on other sites

I would add a 32 bit unsigned integer to your items. Then I'd use the first bit to flag whether it is in use. A boolean works too.

 

Then on the owner side add an array/list of items they own, that way you can implement Actors that can choose to ignore an item's owned status and an Actor will be able to remember it owns a particular item so it is okay to use it.

 

Any problems unmarking an item is all you. Add a logger and figure out where the bug is being introduced, then design a solution.

Edited by coope

Share this post


Link to post
Share on other sites

If your code is flexible enough to support multiple notions of ownership for a game object (visual, inventory, tribe, etc...), then this should just be a matter of a player having a "temp crafting inventory". Those items are placed in the "temp crafting inventory" as needed - then when the player is done crafting, the "temp crafting inventory" is emptied. All along the items remain visible in the world, but any code that checks if someone can pick it up needs to check that it isn't owned by a "temp crafting inventory".

Share this post


Link to post
Share on other sites

the problem with marking them is un-marking them if the crafting action stops for whatever reason.    for actions that use anvil stones, i was able to get away with modding just the the code for actions that use anvil stones, and the setaction(current_bandmember,DONOTHING) code that stops all actions.

 

currently, building huts and rafts also allows objects to simply be nearby, but does not mark them in use as i recall.

 

the thing i don't like about this solution is its like new()ing something in one place, then having to dispose() it in half a dozen different places. its easy to miss one and introduce a bug. the item never gets un-marked, and you can never use it again, it always says "this item is already in use". but i can't really think of a better way to do it.

Why so?

When your Actor enters the 'crafting' action objects are registered to the actor - perhaps this is phil's the temp crafting inventory, even though I wouldn't call it that way. When the action goes out of scope, it unregisters all contained objects.

That is, add to actor 'used list' implies a call to object->usedBy(actor), whereas destroying the action automatically ensures object->usedBy(nullptr), becoming available for registration by other actors.

Share this post


Link to post
Share on other sites

Can't an object remember its last owner, and only become available again if he either gives the object explicitly to someone else, or if he leaves it (distance becomes larger than x)?


This.

The problem you're running into Norman is that you are trying to duplicate state. e.g., the state is "Bob is using the Hammer." The typical naive approach I see (and I see recommended in this thread...) is to duplicate that state so that Bob knows he's using the hammer and the hammer knows that Bob is using it. This turns into bugs because you can end up with state desync.

A very strong instinct you should develop is to spot and avoid that kind of duplicated state. Alberth's recommendation does that. The Hammer only remembers "Bob _was_ using me last". Bob remembers "I _am_ using the Hammer." The _currently using_ state is the important piece of data that we don't want to duplicate, and this approach does not. If you want to know if the Hammer _is_ being used by anyone, find out who _was_ using it and then query that person to see if they _are_ using it. Using a handle system instead of pointers of course to elegantly deal with destroyed objects.

More interestingly, this tiny tweak even opens up whole new AI opportunities. For instance, maybe folks want to wait until the previous user walks away after using an item in order to avoid the dreaded "I popped into my inventory for a sec and when I popped back out someone took over my stuff" problem. Take the principle further and you'll find more opportunities like that.

Basically, where possible, only record a specific piece of information once (sometimes for performance reasons you want to duplicate/cache it, of course, so this isn't a hard rule). Then, if ever you need a piece of information that you don't have, see if you can _derive_ that information. e.g., instead of storing "whom am I being used by" you can instead calculate that value by checking "does whomever was using me last still think they're using me".

You could feasibly even remove the "who was using me" state by just querying all actors in proximity. e.g, "does anybody nearby think they're using me?" If you have a fast spatial query data-structure (you should) that might be fast enough for your needs. You lose the extra information but you never have to worry that you might forget to set the "was last used by" state.

Now, you can also fix this in the _exact same way_ that new/delete problems were fixed: RAII. That is, make a single object helper in charge of tracking this state. When it is cleared, both Bob and the Hammer have their state updated. When the RAII object is initialized, likewise, both Bob and the Hammer have their state updated. Don't allow state to be modified by anything but that helper. No more bugs.

Share this post


Link to post
Share on other sites

>> Can't an object remember its last owner, and only become available again if he either gives the object explicitly to someone else, or if he leaves it (distance becomes larger than x)?

 

yeah, that's the other way to do it (object points to owner as opposed to owner points to object).  right now i actually have them doubly linked: band member points to anvil stone, and anvil stone points to band member. less iterating thru lists.

 

the "other way" i'm referring to is more of a "garbage collection" type approach. when a player wants to use an object, you check all players to see if they are still using it. IE don't rely on the player to release it, you grab it when they're done with it. but you have to check all the players.

Edited by Norman Barrows

Share this post


Link to post
Share on other sites

>> I would add a 32 bit unsigned integer to your items. Then I'd use the first bit to flag whether it is in use. A boolean works too. Then on the owner side add an array/list of items they own, that way you can implement Actors that can choose to ignore an item's owned status and an Actor will be able to remember it owns a particular item so it is okay to use it.

 

that's essentially what i'm doing now, although at the moment they can only own one of these "special items" at a time (so far). adding logs for wood palisades to the game is what prompted this question, as its yet another big object you can't just carry around with you. more actions requiring large objects are probably in the future, so i'll be needing  to come up with a good general solution and approach for oversize objects.

 

>> Any problems unmarking an item is all you

 

i'm looking for a more robust design solution that would minimize that. 

Share this post


Link to post
Share on other sites

>> If your code is flexible enough to support multiple notions of ownership for a game object (visual, inventory, tribe, etc...), then this should just be a matter of a player having a "temp crafting inventory". Those items are placed in the "temp crafting inventory" as needed - then when the player is done crafting, the "temp crafting inventory" is emptied. All along the items remain visible in the world, but any code that checks if someone can pick it up needs to check that it isn't owned by a "temp crafting inventory".

 

i use the world object's generic data variables to indicate ownership. these are checked when an attempt is made to use the object. but "emptying the crafting inventory", IE the call to stop_using_anvil_stone() - which "un-marks" it - must be called at each bail point in an action handler, as well as the funneling chokepoint at setaction(DONOTHING). i may be able to just put it into setaction(DONOTHING), but i still have to hanlde it for all oversized objects, or large piles of parts and tools tool big for the player to carry at once.

 

some games exhibit systems like this, such as the sims with fridges, and skyrim with smithing tools like grindstones and forges. "someone else is using this".

 

i suppose they too use "in use" flags and owner and/or ownee pointers / IDs, which must be both set and cleared.

Share this post


Link to post
Share on other sites

>> The problem you're running into Norman is that you are trying to duplicate state. e.g., the state is "Bob is using the Hammer." The typical naive approach I see (and I see recommended in this thread...) is to duplicate that state so that Bob knows he's using the hammer and the hammer knows that Bob is using it. This turns into bugs because you can end up with state desync.

 

i only doubly linked them to avoid iteration, but yes, if i forget to unmark - its a de-sync. players doesn't think they are using an object - and objects think they are.

 

>> make a single object helper in charge of tracking this state. When it is cleared, both Bob and the Hammer have their state updated. When the RAII object is initialized, likewise, both Bob and the Hammer have their state updated.

 

i built a little API that sets and clears both player and object at once, but i have yet to automate it by wrapping it in a constructor and destructor.

 

>> If you want to know if the Hammer _is_ being used by anyone, find out who _was_ using it and then query that person to see if they _are_ using it.

 

ok, but if you just track last user...

if last user was none, no problem. 

if last user is not doing an action that requires it - its obviously not in use - again no problem.

if they are - are they using this one, or the one 5 feet to the north?   problem?    multiple users, multiple objects - all in close proximity.   that's the scenario.

 

i think i tried the "are there enough to go around" approach and it didn't work for some reason. it took a while to trap out all the cases correctly as i recall. this code was probably written 1 or 2 months ago.

 

>> You could feasibly even remove the "who was using me" state by just querying all actors in proximity.

 

i've been trying to avoid iterating over lists of entities. its probably an act of pre-mature optimization on my part. in this particular case, we're only talking about objects that can be shared between members of the player's band, which is limited to 50 max - and like lots of followers in skyrim with follower mods installed, at first its cool, but after a while you dismiss most of them. you can really only care about so many PCs at a time. and it looks like that magic number might be 4.

 

so if i decide to iterate over lists, how could that simplify things? i'm looking to engineer a generic solution for all oversize objects going forward and also to be retrofitted to anvil stones, rafts and huts, which are special coded at the moment.

 

i have two lists: band members, and dropped objects. i can store anything i want i either one. band members is 50 max, dropped objects is 500 max. they are arrays of structs, and use an active field to produce an array implementation of an un-ordered singly linked list. the lists are linked to each other and various other lists in the game in a relational database manner. so once insterted, items don't move, to avoid pointer fixups  (it actually uses IDs - IE array indexes - not pointers) 

 

if i'm willing to iterate thru these lists - and i don't think the performance hit of doing so would be an issue - whats the simplest way i could do this?

 

>>   RAII. That is, make a single object helper in charge of tracking this state. When it is cleared, both Bob and the Hammer have their state updated. When the RAII object is initialized, likewise, both Bob and the Hammer have their state updated.

 

hmm ...  i may have finally come across a case that truly calls for OO syntax - after all these years! <g>.

 

ok, so i'd put mark in the constructor, and unmark in the destructor, and create the instance when i start the action, but i'd still have to release it in all the same places that i would have to call unmark. am i missing something?

 

i think i should inspect the code and see if setaction(DONOTHING) is a true choke point at which i can catch everything. if so, then my calls to unmark at the various bail points in the action handlers are redundant. its quite possible the bail points call setaction(DONOTHING).  in that case i'd just have to do some sort of check in setaction(), that if they were doing an action that uses an oversize object, mark it as unused. new()ing and delete()ing at one point only [action start and section(DONOTHING)] is acceptable <g>

 

but how could iteration simplify things?  whats the least amount of data i can track?

 

is it even necessary to track which player is using which item? or is "in use" sufficient?

 

assume multiple players and objects in close  proximity, with actions starting, stopping, changing, being interrupted, and being resumed frequently. all possible cases must be handled correctly.

 

makes for a nice little design challenge eh?

 

any ideas or suggestions?

Edited by Norman Barrows

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement