Up to Main Index Up to Journal for March, 2018 JOURNAL FOR SATURDAY 24TH MARCH, 2018 ______________________________________________________________________________ SUBJECT: Out of the rabbit hole DATE: Sat 24 Mar 19:27:41 GMT 2018 I really don’t like the phrase ‘permanently disposable items’, it’s a bit of a mouthful and a pain to keep typing. I was thinking of calling ‘permanently disposable items’ collectable, as these are items players can collect and keep. Non-collectable items cannot be kept by players. Let’s try it for this journal entry and see how it goes… At long last I have found my way out of a deep rabbit hole. For the last few weeks I’ve been working on saving players. I think most players would agree this is quite an important feature to have. While working on said feature I’ve tried many different approaches to modifying the existing code. For example, changing the Thing.Marshal method to save recursively, changing Inventory to only Marshal references for collectable items, changing the Marshal interface so that the methods can take behaviour modifying parameters. However, all of these changes resulted in Thing.Marshal and Inventory.Marshal becoming specialised and incapable of marshaling any Thing or any Inventory. This in turn would have meant no saving and persisting of the world using existing code. Actually I think ‘broken’ would have been better description for the changes. After a while I stopped banging my head against the wall, and deleted all of my ill conceived changes. Sometimes it’s better to throw out changes and spending some time just sitting and thinking about a problem. What was I actually trying to achieve here? I wanted to marshal a Thing, if the Thing had an Inventory I wanted to marshal those Thing as well — but only if they were collectable. I could actually do that in the save command quite easily without touching any other code: func (sa save) inventory(jar *recordjar.Jar, t has.Thing) { *jar = append(*jar, t.(*attr.Thing).Marshal()) if i := attr.FindInventory(t); i.Found() { for _, t := range i.Contents() { if t.Collectable() { sa.inventory(jar, t) } } } } Oops! Little white lie there, I touched Thing to add a Collectable method. This wraps a call to FindLocate(t).Origin(), but means that the definition of what is considered collectable can be easily changed. The inventory method takes the Jar we want to marshal into and the Thing we want to marshal into it. In doing so we end up with incorrect Inventory fields in the Jar because Inventory marshals all of the references it knows about. For example, we might end up with this snippet: Ref: #UID-6O Name: a small sack Alias: SACK Cleanup: AFTER→10M JITTER→5M Inventory: #UID-6P #UID-6Q This is a small sack, handy for carrying things in. %% Ref: #UID-6P Name: a small red ball Alias: BALL Cleanup: AFTER→1S JITTER→0S This is a small, red, rubber ball. %% Here we have a sack that contains two items #UID-6P and #UID-6Q. Reference #UID-6P is the small red ball. We don’t know what #UID-6Q was because it wasn’t collectable, and hence not saved. This was why I originally tried modifying the Inventory.Marshal method — to only write out references for collectable items. However, what we have now is a Jar with records that can be easily processed. Why not fix the Jar by rewriting the inventory fields so that they only contain references found in the Jar? func (sa save) fixInventory(jar *recordjar.Jar) { // Extract all "ref" fields from the jar refs := make(map[string]struct{}) for _, rec := range *jar { refs[string(rec["ref"])] = struct{}{} } // Find all of the "inventory" fields in the jar and rewrite them to only // contain references found in the jar for _, rec := range *jar { if i, ok := rec["inventory"]; ok { newRefs := []string{} for _, ref := range decode.KeywordList(i) { if _, ok := refs[ref]; ok { newRefs = append(newRefs, ref) } } rec["inventory"] = encode.KeywordList(newRefs) } } } That took a lot longer to debug than it should have. Mainly due to the field name map keys being a mess of all upper, all lower and mixed case. As a result the recordjar.Write method now does a better job of normalising the field names. The code for the SAVE command and fixes to the recordjar package are now on the public dev branch. The SAVE command will only write to the file ‘save.wrj’ and not to real player files yet. I’m still working on being able to load player files saved with the SAVE command. The save files are still missing the account information record and the loader doesn’t load any saved items yet. Non-collectable containers are still an issue :( A non-collectable container will not be saved, as expected, but ALL of its content, whether collectable or not, will not be saved either. This results in ALL items in non-collectable containers effectively being ‘lost’ when a player is saved. For now a simple solution is to not create non-collectable containers while I try to come up with a solution. One possible solution would be to take the collectable items out of the non-collectable container and put them into the parent container. If the parent container was full the item would need to be pushed further up the container hierarchy. This could eventually push the item into the player’s main inventory. If the player’s main inventory was full the item would push all the way up to the current location the player is in — resulting in the item being dropped. In the end the logic works out neatly, although the player’s inventory may be left in a mess. To help with testing I added two new items to the tavern. A bag and a sack, both are collectable. They can be used to experiment with the SAVE command and its interaction with containers in containers and items in containers — which maybe in other containers: Ref: O9 Name: a small sack Aliases: SACK Reset: AFTER→1m JITTER→1m SPAWN Cleanup: AFTER→10m JITTER→5m Location: L1 Inventory: This is a small sack, handy for carrying things in. %% Ref: O10 Name: a small bag Aliases: BAG Reset: AFTER→1m JITTER→1m SPAWN Cleanup: AFTER→10m JITTER→5m Location: L1 Inventory: This is a small bag, handy for carrying things in. %% The bag and sack have been included in the dev branch update. For collectable items the small red ball, found in the pouch that is in the chest, already exists. There are also mushrooms in the undergrowth in the forest to the south. This was only supposed to be a quick entry and I was going to spend the afternoon coding… -- Diddymus Up to Main Index Up to Journal for March, 2018