PageSourceSearch

https://saving.enzenebner.com/docs/assets/js/38228b33.3fa53fb3.js

js enzenebner.com collected 2026-10-04 10:03:45 UTC 4,540 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunkmy_website=self.webpackChunkmy_website||[]).push([[926],{1768:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>s,default:()=>c,frontMatter:()=>r,metadata:()=>o,toc:()=>u});var i=n(4848),a=n(8453);const r={title:"Custom debug viewer"},s=void 0,o={id:"advanced/custom-viewer",title:"Custom debug viewer",description:"For data that is unsupported in the viewer, like IDynamicHashMap or generic implementations of FixedLists,",source:"@site/docs/advanced/custom-viewer.md",sourceDirName:"advanced",slug:"/advanced/custom-viewer",permalink:"/advanced/custom-viewer",draft:!1,unlisted:!1,tags:[],version:"current",frontMatter:{title:"Custom debug viewer"},sidebar:"documentationSidebar",previous:{title:"Custom serializer",permalink:"/advanced/custom-serializer"},next:{title:"Save file format",permalink:"/advanced/save-file-format"}},l={},u=[];function d(e){const t={code:"code",li:"li",p:"p",pre:"pre",ul:"ul",...(0,a.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(t.p,{children:"For data that is unsupported in the viewer, like IDynamicHashMap or generic implementations of FixedLists,\nthere is a way to write a custom debug viewer for the SaveGame viewer."}),"\n",(0,i.jsx)(t.p,{children:"The boilerplate is simple:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-csharp",children:"[CustomSerializeDebugView(typeof(MySavedComponent))]\npublic unsafe class MySavedComponentDebugViewer : ICustomSerializerDebugView\n{\n    public bool RequiresFullPayload() => false;\n    \n    public void DebugView(VisualElement root, ref ByteDeserializer deserializer, ref SavedEntityMapping entityMap)\n    {\n        ...\n    }\n}\n"})}),"\n",(0,i.jsxs)(t.ul,{children:["\n",(0,i.jsxs)(t.li,{children:["implement the ",(0,i.jsx)(t.code,{children:"CustomSerializeDebugView"})," attribute with the component you want to make the viewer for."]}),"\n",(0,i.jsxs)(t.li,{children:["implement the interface ",(0,i.jsx)(t.code,{children:"ICustomSerializerDebugView"}),":"]}),"\n"]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"RequiresFullPayload"}),": when set to true, the full payload will be available in the deserializer.\nThis is useful when you want to get the full data of a DynamicBuffer for example and not just each element."]}),"\n",(0,i.jsxs)(t.p,{children:[(0,i.jsx)(t.code,{children:"DebugView"}),": gives access to the root VisualElement, the deserializer and the entity mapping in case that's needed."]}),"\n",(0,i.jsx)(t.p,{children:"A full example for an IDynamicHashmap looks like this:"}),"\n",(0,i.jsx)(t.pre,{children:(0,i.jsx)(t.code,{className:"language-csharp",children:"[CustomSerializeDebugView(typeof(MyHashMapBuffer))]\npublic unsafe class MyHashMapBufferDebugViewer : ICustomSerializerDebugView\n{\n    public bool RequiresFullPayload() => true;\n\n    public void DebugView(VisualElement root, ref ByteDeserializer deserializer, ref SavedEntityMapping entityMap)\n    {\n        // to cast to a hashmap we have to get a valid DynamicBuffer (with safety)\n        var buffer = DynamicBufferExtensions.ConvertFromNativeArray((MyHashMapBuffer*) deserializer.CurrentPtr, deserializer.DataLength);\n        \n        var bufferHashMap = buffer.AsHashMap<MyHashMapBuffer, Entity, MyValue>();\n\n        var enumerator = bufferHashMap.GetEnumerator();\n        while (enumerator.MoveNext())\n        {\n            var key = enumerator.CurrentKey;\n            var value = enumerator.CurrentValue;\n            \n            root.Add(new TextField(key.ToString()) { value = value.Value.ToString() });\n        }\n\n        // also release the safety after we are done\n        buffer.ReleaseSafety();\n    }\n}\n"})}),"\n",(0,i.jsxs)(t.p,{children:["For a FixedList you'd read it from the deserializer like this: ",(0,i.jsx)(t.code,{children:"var fixedList = deserializer.Read<FixedList32Bytes<int>>();"}),".\nThen iterate over it and add the the appropriate PropertyField (IntegerField, etc...) to the root."]})]})}function c(e={}){const{wrapper:t}={...(0,a.R)(),...e.components};return t?(0,i.jsx)(t,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>o});var i=n(6540);const a={},r=i.createContext(a);function s(e){const t=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:s(e.components),i.createElement(r.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.