Offroaders123 / NBTify

A library to read and write NBT files on the web!
http://npm.im/nbtify
MIT License
42 stars 5 forks source link

XML Exploration #46

Open Offroaders123 opened 4 months ago

Offroaders123 commented 4 months ago

(Initially copied this from a Discord thread)

I think I figured something out

NBTML demo

I think NBT is indeed was originally meant to be typed as 'primitives, with attributes', compared to a plain 'key-value record'/'hashmap' setup It explains the need for the name at the top level of the file XML translates very well over between NBT/SNBT, in terms of conceptually explaining it I want to make a demo converter to show it now

Dovetail render demo

Dovetail SNBT demo

NBTify CLI demo

So using something like SNBT or JSON to describe NBT is (possibly) counter to how the original concept was described Maybe it was more like a markup language afterall, to where the tags themselves could have been literal markup tags

<CompoundTag name="hello world">
  <StringTag name="name">Bananrama</StringTag>
</CompoundTag>

I'm not sure how that would work for the more complex derived-types though, like ByteArrayTag, IntArrayTag, or LongArrayTag I guess technically it is just like this

<LongArrayTag>
  <LongTag>25000</LongTag>
  <LongTag>25000</LongTag>
  <LongTag>25000</LongTag>
  <LongTag>25000</LongTag>
</LongArrayTag>

So maybe it's not complex afterall

<ListTag>
  <CompoundTag>
    <ByteTag name="nice-going-genghis">12345</ByteTag>
  </CompoundTag>
  <StringTag name="gg">hello there once again</StringTag>
</ListTag>

[Generic PnP Monitor] well a LongArrayTag doesn't store LongTags it just stores plain longs

Yeah in terms of the binary format I'm curious how else it could be represented as plain text then? Does XML (or HTML, just for the sake of looking into it) have any list-based elements/tags that don't require a special child type for it to work?

[Generic PnP Monitor] hmm dunno maybe just having a LongValue element idk

<ol>
  <li>hello</li>
</ol>

Ok I think I'm going to try adding it into NBTify

Offroaders123 commented 4 months ago

So part of the concept here is around my new realization as to how NBT tags would be represented with markup languages. If it were with XML, I think it actually lines up very well with how the tags currently work, and were originally implemented years back.

Offroaders123 commented 4 months ago

JSX? More Discord thread discussions!

React NBTML JSX? JSNBTML? oh god 😂

export default function Hello() {
  return (
    <CompoundTag name="hello world">
      <ByteTag name="result-of-something">25</ByteTag>
    </CompoundTag>
  );
}

I actually love the concept of this in a way! What if you programatically create NBT with functional components? Lol I'm not sure the need for this, but it does line up very well in terms of the actual editing work

I already like the TS type checking around using plain objects though

export default function Hello() {
  return new CompoundTag("hello world", {
    "result-of-something": new ByteTag(25)
  });
}

So if the constructors for the tag classes were just rearranged to work with how JSX calls the components, it would actually work nicely

Offroaders123 commented 4 months ago

This does bring up another great point though Using something like SolidJS on top of NBT primitive JSX elements You could have reactive data structures built on top of plain NBT values 🤯

Offroaders123 commented 1 month ago

Info from Discord about the background on why this is fairly cool:

It's more for the compatibility of maybe being useful for other tools and building blocks, ones we might not have quite put together yet One idea I thought of is being able to render NBT with SVG maybe, or just to view it in the browser itself, with a nice tree view possibly Yeah this is pretty cool, here's how it renders in Chrome

XML tree render in the browser

[EternalModz]: Would it be similar to SNBT?

Yeah to some extent

It's not as easy for readability, but more tools might be able to edit it since XML is commonplace So say if you wanted to edit NBT with a non-NBT program, you could just convert it to XML, then back again, like we do currently for SNBT Not sure if it's really usable to any extent, but even using JSX to template NBT sounds kind of interesting to me

export default function Bigtest() {
  return (
    // <?xml version="1.0" encoding="UTF-8"?>
    <CompoundTag name="Level">
      <LongTag name="longTest">9223372036854775807</LongTag>
      <ShortTag name="shortTest">32767</ShortTag>
      <StringTag name="stringTest">HELLO WORLD THIS IS A TEST STRING ÅÄÖ!</StringTag>
      <FloatTag name="floatTest">0.4982314705848694</FloatTag>
      <IntTag name="intTest">2147483647</IntTag>
      <CompoundTag name="nested compound test">
        <CompoundTag name="ham">
          <StringTag name="name">Hampus</StringTag>
          <FloatTag name="value">0.75</FloatTag>
        </CompoundTag>
        <CompoundTag name="egg">
          <StringTag name="name">Eggbert</StringTag>
          <FloatTag name="value">0.5</FloatTag>
        </CompoundTag>
      </CompoundTag>
      <ListTag name="listTest (long)">
        <LongTag>11</LongTag>
        <LongTag>12</LongTag>
        <LongTag>13</LongTag>
        <LongTag>14</LongTag>
        <LongTag>15</LongTag>
      </ListTag>
      <ListTag name="listTest (compound)">
        <CompoundTag>
          <StringTag name="name">Compound tag #0</StringTag>
          <LongTag name="created-on">1264099775885</LongTag>
        </CompoundTag>
        <CompoundTag>
          <StringTag name="name">Compound tag #1</StringTag>
          <LongTag name="created-on">1264099775885</LongTag>
        </CompoundTag>
      </ListTag>
      <ByteTag name="byteTest">127</ByteTag>
      <DoubleTag name="doubleTest">0.4931287132182315</DoubleTag>
      <ByteArrayTag name="byteArrayTest">
        <ByteTag>0</ByteTag>
        <ByteTag>62</ByteTag>
        <ByteTag>34</ByteTag>
        <ByteTag>16</ByteTag>
        <ByteTag>8</ByteTag>
      </ByteArrayTag>
      <IntArrayTag name="intArrayTest">
        <IntTag>543</IntTag>
        <IntTag>123</IntTag>
        <IntTag>7567</IntTag>
        <IntTag>244</IntTag>
      </IntArrayTag>
      <LongArrayTag name="longArrayTest">
        <LongTag>7676</LongTag>
        <LongTag>53534</LongTag>
        <LongTag>34534</LongTag>
        <LongTag>345345345</LongTag>
      </LongArrayTag>
      <StringTag name="escapedString">"noice, I gotchya"</StringTag>
      {/* <StringTag name="escapeSequences">&#x8;&#xC;&#xA;&#13;&#x9;"'\</StringTag> */}
      <StringTag name="otherEscape">"</StringTag>
    </CompoundTag>
  );
}

I like the JS object setup more personally though, since it's more typesafe with TypeScript

import { Int8, Int16, Int32, Float32 } from "nbtify"

type Bigtest = ReturnType<typeof Bigtest>;

export default function Bigtest() {
  return {
    longTest: 9223372036854775807n,
    shortTest: new Int16<number>(32767),
    stringTest: "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!",
    floatTest: new Float32<number>(0.4982314705848694),
    intTest: new Int32<number>(2147483647),
    "nested compound test": {
      ham: {
        name: "Hampus",
        value: new Float32<number>(0.75)
      },
      egg: {
        name: "Eggbert",
        value: new Float32<number>(0.5)
      }
    },
    "listTest (long)": [11n, 12n, 13n, 14n, 15n],
    "listTest (compound)": [
      {
        name: "Compound tag #0",
        "created-on": 1264099775885n
      },
      {
        name: "Compound tag #1",
        "created-on": 1264099775885n
      }
    ],
    byteTest: new Int8<number>(127),
    doubleTest: 0.4931287132182315,
    byteArrayTest: new Int8Array([0,62,34,16,8]),
    intArrayTest: new Int32Array([543,123,7567,244]),
    longArrayTest: new BigInt64Array([7676n,53534n,34534n,345345345n]),
    escapedString: '"noice, I gotchya"',
    escapeSequences: '\b\f\n\r\t"\'\\',
    otherEscape: "\""
  };
}

[In reference to the JSX file code snippet above] So if the JSX pragma for this was actually implemented, it would just resolve to the same object that you see in the file above, bigtest.ts

TS NBT typings

Offroaders123 commented 1 month ago

Come to think of it, I wonder if it would be cool to render these with Web Components themselves! Then you could write your NBT with HTML too 🫨 Or rather, you could use HTML to create NBT declaratively

It's a bit hacky, but yeah!

<!DOCTYPE html>
<html lang="en-US">

<head>

<title>Bigtest</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <meta name="color-scheme" content="dark"> -->

<style>
:where(
  byte-tag,
  short-tag,
  int-tag,
  long-tag,
  float-tag,
  double-tag,
  byte-array-tag,
  string-tag,
  list-tag,
  compound-tag,
  int-array-tag,
  long-array-tag
) {
  font-family: monospace;
  display: list-item;
  text-align: -webkit-match-parent;
  unicode-bidi: isolate;

  :not(
    byte-array-tag,
    list-tag,
    int-array-tag,
    long-array-tag
  ) > &::before {
    content: attr(name) ": ";
  }
}

:where(
  byte-array-tag,
  list-tag,
  compound-tag,
  int-array-tag,
  long-array-tag
) {
  display: block;
  list-style-type: disc;
  list-style-position: inside;
  margin-block-start: 1em;
  margin-block-end: 1em;
  margin-inline-start: 0;
  margin-inline-end: 0;
  padding-inline-start: 40px;
  border-left: 2px dashed black;
  unicode-bidi: isolate;
}
</style>

</head>

<body>

<compound-tag name="Level">
  <long-tag name="longTest">9223372036854775807</long-tag>
  <short-tag name="shortTest">32767</short-tag>
  <string-tag name="stringTest">HELLO WORLD THIS IS A TEST STRING ÅÄÖ!</string-tag>
  <float-tag name="floatTest">0.4982314705848694</float-tag>
  <int-tag name="intTest">2147483647</int-tag>
  <compound-tag name="nested compound test">
    <compound-tag name="ham">
      <string-tag name="name">Hampus</string-tag>
      <float-tag name="value">0.75</float-tag>
    </compound-tag>
    <compound-tag name="egg">
      <string-tag name="name">Eggbert</string-tag>
      <float-tag name="value">0.5</float-tag>
    </compound-tag>
  </compound-tag>
  <list-tag name="listTest (long)">
    <long-tag>11</long-tag>
    <long-tag>12</long-tag>
    <long-tag>13</long-tag>
    <long-tag>14</long-tag>
    <long-tag>15</long-tag>
  </list-tag>
  <list-tag name="listTest (compound)">
    <compound-tag>
      <string-tag name="name">Compound tag #0</string-tag>
      <long-tag name="created-on">1264099775885</long-tag>
    </compound-tag>
    <compound-tag>
      <string-tag name="name">Compound tag #1</string-tag>
      <long-tag name="created-on">1264099775885</long-tag>
    </compound-tag>
  </list-tag>
  <byte-tag name="byteTest">127</byte-tag>
  <double-tag name="doubleTest">0.4931287132182315</double-tag>
  <byte-array-tag name="byteArrayTest">
    <byte-tag>0</byte-tag>
    <byte-tag>62</byte-tag>
    <byte-tag>34</byte-tag>
    <byte-tag>16</byte-tag>
    <byte-tag>8</byte-tag>
  </byte-array-tag>
  <int-array-tag name="intArrayTest">
    <int-tag>543</int-tag>
    <int-tag>123</int-tag>
    <int-tag>7567</int-tag>
    <int-tag>244</int-tag>
  </int-array-tag>
  <long-array-tag name="longArrayTest">
    <long-tag>7676</long-tag>
    <long-tag>53534</long-tag>
    <long-tag>34534</long-tag>
    <long-tag>345345345</long-tag>
  </long-array-tag>
  <string-tag name="escapedString">"noice, I gotchya"</string-tag>
  <string-tag name="escapeSequences">&#x8;&#xC;&#xA;&#13;&#x9;"'\</string-tag>
  <string-tag name="otherEscape">"</string-tag>
</compound-tag>

</body>

</html>