Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XML Exploration #46

Open
Offroaders123 opened this issue Jul 24, 2024 · 5 comments
Open

XML Exploration #46

Offroaders123 opened this issue Jul 24, 2024 · 5 comments
Assignees
Labels
enhancement New feature or request

Comments

@Offroaders123
Copy link
Owner

(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 Offroaders123 added the enhancement New feature or request label Jul 24, 2024
@Offroaders123 Offroaders123 self-assigned this Jul 24, 2024
@Offroaders123
Copy link
Owner Author

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
Copy link
Owner Author

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
Copy link
Owner Author

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 added a commit that referenced this issue Oct 9, 2024
Looking into being able to store NBT as XML! I think I'm just curious how this might be able to be interesting in terms of possibly reactive components, and or JSX, and just composability in general. You wouldn't want to write it out by hand like this, but maybe instead it could be a way to render NBT in a vector format, say like with SVG :O

#46

Got some help from GPT and the web in general (along with some of my own validations) to get what would be the XML entities for the odd characters I have experimented with serializing, like newlines, tabs, things like those.

This file was written by hand, as a transform from the original `bigtest.snbt` file. I am going to see if I can make a custom parser and writer for XML, which goes between NBTify JS primitives, and XML strings. Seems like it could be another great way to make NBT interop even greater!
Offroaders123 added a commit that referenced this issue Oct 9, 2024
Tiny Tears, Terria

https://www.npmjs.com/package/xmldom

Getting some help from GPT to implement my own XML to NBTify object parser, since it is essentially tag-specific in what it parses to. Thankfully it actually does make a bit of sense to use an existing XML tree parser, and to generate the NBT object tree from that. I wonder if that's not very performant though, maybe handling it all on my own would make more sense in terms of being scoped to NBTify. I don't like needing to parse XML outright with my own code, but I also don't want to need to rely on generating actual DOM trees themselves, unless maybe that makes sense to do so. Seems like it could be a bunch of extra memory overhead for no reason, if just converting to and from NBT. If you want to render it as a tree of elements in the browser though, it might actually make a lot of sense to do so like that though. Would it still make sense to go from NBT, to a JS object tree, then to an XML element tree? I kind of like the premise of that a bit more. We'll see though!

Kind of surprised this works so well actually, GPT got things working not too bad, and considering this is in Node as well! So DOMParser is from the polyfill itself.

And actually, now I want to make my own DOMParser code but in TypeScript, maybe I can fork xmldom and add types, and make things more concise overall.

#46
@Offroaders123
Copy link
Owner Author

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
Copy link
Owner Author

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>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant