Up to Main Index Up to Journal for February, 2018 JOURNAL FOR WEDNESDAY 28TH FEBRUARY, 2018 ______________________________________________________________________________ SUBJECT: Save me! DATE: Wed 28 Feb 19:54:18 GMT 2018 I spent Thursday[1] evening last week modifying the Thing.Marshal method so that it could recurse into inventories. I also put together a very simple SAVE command to write out the currently logged in player and their inventory. At the moment the SAVE command always writes to DATA_DIR/players/save.wrj so that I don’t accidentally blow away any actual player files while testing. How simple was the simple SAVE command? package cmd import ( "os" "path/filepath" "code.wolfmud.org/WolfMUD.git/attr" "code.wolfmud.org/WolfMUD.git/config" "code.wolfmud.org/WolfMUD.git/recordjar" ) // Syntax: SAVE func init() { addHandler(save{}, "SAVE") } type save cmd func (save) process(s *state) { j := s.actor.(*attr.Thing).Marshal(recordjar.Jar{}) n := filepath.Join(config.Server.DataDir, "players", "save.tmp") f, _ := os.Create(n) j.Write(f, "DESCRIPTION") f.Close() s.msg.Actor.SendInfo("You have been saved.") s.ok = true } That’s the whole command for testing — yes, sans error handling. The point of the exercise was to see how complete and accurate the saved data was. Below are the results. The left hand side shows the saved player after picking up the iron bound chest and green ball in the tavern. The right hand side shows the original definitions from the zinara.wrj zone file. Down the middle a ‘|’ indicates lines that are different and ‘>’ indicates an additional line. save.wrj zinara.wrj -------- ---------- Gender: MALE | Inventory: #UID-3B #UID-3D | Name: Diddymus | Alias: DIDDYMUS | Ref: #UID-6J | %% %% Ref: #UID-3B | Ref: O4 Name: an iron bound chest Name: an iron bound chest Reset: AFTER→1M JITTER→1M SPAWN | Reset: AFTER→1m JITTER→1m Alias: CHEST | Aliases: CHEST Cleanup: AFTER→10M JITTER→5M Cleanup: AFTER→10m JITTER→5m | Location: L1 Inventory: #UID-3A | Inventory: O4A This is a very stout wooden chest a This is a very stout wooden chest a bands bind it. bands bind it. %% %% Ref: #UID-3A | Ref: O4A Name: a small leather pouch Name: a small leather pouch Reset: AFTER→1M JITTER→1M SPAWN | Reset: AFTER→1m JITTER→1m Alias: POUCH | Aliases: POUCH Cleanup: AFTER→10M JITTER→10M Cleanup: AFTER→10m JITTER→10m Inventory: #UID-39 | Inventory: This is a small pouch made of soft This is a small pouch made of soft %% %% Ref: #UID-39 | Ref: O3 Name: a small red ball Name: a small red ball Reset: AFTER→0S JITTER→2M SPAWN→T | Reset: AFTER→0s JITTER→2m SPAWN Alias: BALL | Aliases: BALL Cleanup: AFTER→1S JITTER→0S | Cleanup: AFTER→1s > Location: O4A This is a small, red, rubber ball. This is a small, red, rubber ball. %% %% Ref: #UID-3D | Ref: O2 Name: a small green ball Name: a small green ball Reset: SPAWN→FALSE AFTER→0S JIT | Reset: AFTER→0s JITTER→0s Alias: BALL | Aliases: BALL Cleanup: AFTER→0S JITTER→0S Cleanup: AFTER→0s JITTER→0s Onreset: There is a bright flash OnReset: There is a bright flash > Location: L1 Oncleanup: There is a bright flash OnCleanup: There is a bright flash This is a small, green, rubber ball This is a small, green, rubber ball %% %% In order to compare the two I’ve ordered the field names within each record consistently. The reference identifiers are the most obvious difference. This is expected as the ids have to be unique within the file, so we just use the already available system unique id. The saved version will favour the use of Inventory over Location. Inventory/Location are interchangeable, depending on whether you want to specify ‘that goes here’ (Inventory) or ‘this goes there’ (Location). The saved version also looks at the number of aliases and uses Alias or Aliases accordingly. When players log out their character and its current inventory are saved and removed from the world. When they log back in again, the character and its inventory are loaded and added back into the world. However, not all items can be kept by a player and removed from the world. This is because some items are unique and needed by other players. An example would be a key for a door. If the key was removed from the world, then no other players would be able to unlock the door and open it. The only items a player can keep are those that are created or duplicated in the world. Currently this means any item that is spawnable. Later on it will also mean items that are bought or crafted. Bought items will be cloned from a shop’s inventory. Crafted items will be created from a template item. Hypothetically, in the case of the key above, it could be possible for a player to craft a duplicate key, which they could then keep. Thinking through the above, players can keep any item that can be disposed of permanently. There is already a check for this in the JUNK command. If an item has no origin set and no reset attribute then it is permanently disposed of when cleaned up. When we save a player and their inventory, we simply ignore items that are not permanently disposable. This leads to a clean way of extracting the player from the world after they have been saved. We JUNK the player. Using the JUNK command items will be cleaned up and either disposed of or reset as appropriate. Plus the code is already written :) While experimenting with using the JUNK command on a player I noticed a weird bug in the message buffer handling: >quit You junk an iron bound chest. You leave this world behind. You junk a small green ball. You leave this world behind. Main Menu --------- 1. Enter game 0. Quit Select an option: >0 Bye bye... Connection closed by foreign host. Notice that “You leave this world behind.” appears twice when it should only appear once. This only seems to happen if a player quits when there is nothing else at the same location. Not sure why, will investigate. Once I have all of the above sorted out, I just need to work on reversing the process and loading the player and their inventory back into the world. For that I need to delve into the zone loader and start refactoring it into a generic .wrj file loader. On a side note, I’ve switched to using Go 1.10 for development with no issues. -- Diddymus [1] Yes, this post has been delayed quite a bit again… Up to Main Index Up to Journal for February, 2018