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

Minecraft Data Structure Type Definitions #6

Closed
Offroaders123 opened this issue Sep 27, 2022 · 2 comments
Closed

Minecraft Data Structure Type Definitions #6

Offroaders123 opened this issue Sep 27, 2022 · 2 comments
Assignees
Labels
enhancement New feature or request

Comments

@Offroaders123
Copy link
Owner

Offroaders123 commented Sep 27, 2022

Not sure if it will be in this project or not yet (or it could be a separate repo), but I want to write type definitions for the Minecraft file data structures. One example could be the Bedrock level.dat file. It may look something like this:

import { NBTData, ByteTag, IntTag, StringTag } from "nbtify";

export interface BedrockLevel extends NBTData {
  name: "";
  endian: "little";
  compression: "none";
  data: {
    BiomeOverride: StringTag;
    CenterMapsToOrigin: ByteTag;
    ConfirmPlatformLockedContent: ByteTag;
    Difficulty: IntTag;
    FlatWorldLayers: StringTag;
    ForceGameType: ByteTag;
    GameType: IntTag;
    Generator: IntTag;
    // More entries...
  };
}
@Offroaders123
Copy link
Owner Author

This is still a goal, but it will not be for this project specifically, so I'm closing the idea for it here.

Initially I planned on making a central repo for managing the logic for reading from all different Minecraft world formats, but maybe I will instead make 3 separate packages for each version; one for Bedrock, one for Java, and one for Legacy Console Edition. That sounds like possibly a nicer way to organize things, rather than trying to shoehorn all of the different versions' handling into a single codebase. That doesn't sound very nice.

The first idea consisted of working with world data using JavaScript objects to over-arch the platform differences, so you wouldn't have to worry about the underlying implementation itself, only the data that it holds. So, for Bedrock and Java, you could do Region.read(x: number, y: number, { platform: "bedrock" | "java" | "legacy-console"; }) or something like that, and it would get the Chunk entries inside of that specific Region offset, even though each platform may not specifically use that kind of data storage (Bedrock Edition). This kind of abstraction could be used to work with other parts of the data, which allows you to use simple function calls like that to do larger amounts of work with simple commands, like Chunk.from(BedrockChunk,{ platform: "java"; }) or things like that, which simply maps the JavaScript object-representation of that NBT/game data into the shape that the other platform can use, without having to manually do anything with the NBT format yourself.

Say if I take the multi-repo route, then each versions' NBT TypeScript definitions will have the type definitions unique to that version. So, the Bedrock level.dat file types would be defined in the Bedrock library, for example.

I think I will look into defining the logic for each program, separately in different packages/repos, then a central API can leverage those libraries together, providing a single, consistent API to work with the raw world data from a top-down view, or something like that.

Maybe instead of doing separate repos, I can simply use separate module folders, and it can still be under a singular package, I really like the sound of that too.

One more example of the top-down view would be the type definitions for each building block. Say, to differ a Bedrock chunk from a Java chunk, while still using the same object building block to manage each of them:

// This is a bit of an ugly API, but it allows the typing setup that I may try using for working with the world data.

export type Platform = "bedrock" | "java" | "legacy-console";

export interface BedrockChunkData {
  x: number;
  y: number;
  version: string;
}

export interface JavaChunkData {
  xPos: number;
  yPos: number;
  Version: number;
}

export interface LegacyConsoleChunkData {
  X: number;
  Y: number;
  Version: number;
}

export interface ChunkDataPlatformMap {
  "bedrock": BedrockChunkData;
  "java": JavaChunkData;
  "legacy-console": LegacyConsoleChunkData;
}

// Inspired by the typings for `ParentNode.querySelector()`
export declare class Chunk<K extends keyof ChunkDataPlatformMap> {
  platform: K;
  data: ChunkDataPlatformMap[K];

  // I think using further `interface` declarations here could help make the typings a bit nicer to work with.
  // Seeing `Chunk<"bedrock">` as a type isn't very neat, I'd rather have an interface of `BedrockChunk` or something like that.
  static read<K extends keyof ChunkDataPlatformMap>(data: Uint8Array, platform: K): Promise<Chunk<K>>;
}

const bedrockChunk = await Chunk.read(new Uint8Array(),"bedrock");
bedrockChunk.data.version;

const javaChunk = await Chunk.read(new Uint8Array(),"java");
javaChunk.data.xPos;

const legacyConsoleChunk = await Chunk.read(new Uint8Array(),"legacy-console");
legacyConsoleChunk.data.X;

Offroaders123 added a commit to Offroaders123/Region-Types that referenced this issue Apr 25, 2023
These are the initial start to my demos for the TypeScript types of NBT structures within world saves. This is a continuation of these issues here:
Offroaders123/NBTify#6
sandstone-mc/sandstone#202

For context of the files present here, this is the huge write-up I just did over in the second issue listed above. Rather than summarizing it, or just linking to it, I think having it here to start with is the best context :D

Listening to a ton of my favorite albums today/tonight, it's been great. On Voyage 34: Phase IV right now :)

---------------------------------------------

It may be out of the scope of this project, but I thought I'd mention it just to get a little conversation going.

I've been working on a TypeScript NBT library to work with editing world saves, and manipulating any data that it holds anywhere in the save. I'm still working on getting the world format logic set up, but my NBT library is fully functional at this point. It supports big and little endian, and compressed or uncompressed NBT files.

With how I've shaped the data structures that are output by my NBT library, I have the eventual goal of making TypeScript definitions for world save NBT data structures, which could allow for a lot quicker development pace in terms of iteration time, and working between the different world formats.

With this goal in mind, I'd like to use those type definitions to possibly make a world format converter between the different versions, which would be strongly driven by those type definitions. I think if there are definitions for all of the different structures, it would actually make it a fair bit easier to describe the shapes of the actual data inside of our world saves, and it may possibly make it much more easy to debug the shapes of each world format. Say, if the converter were to go between Java, Bedrock, or Legacy Console Edition.

Here's a link to my NBT library, [NBTify](https://github.com/Offroaders123/NBTify). It's built using TypeScript and ESM, and it works directly in the browser or Node, out of the box.

I only just found your project tonight, and it already looks very awesome, using functional APIs for Minecraft development has been a dream of mine for a while, and the first sight I saw here looked both very promising and inspiring. These are the kinds of structures I'd like to provide for working with world saves too.

I started working on a demo set of type definitions while finishing up writing this, but it started to get really big. I'm going to demo it out some more, and publish it as a new repo to get some more ideas flowing. Using `.d.ts` files for demoing API designs is a very helpful way to figure out how you want things to work, before you know how they work internally, would recommend! I will add an update with a reference to the repo once I have it up and running.

For a few more references, I've started working on an NBT editing PWA called [Dovetail](https://github.com/Offroaders123/Dovetail), which supports all of the formats mentioned above.

Still in the demo/experimental stages, I have some other projects which I am using to explore opening the world formats themselves:

- [Gamedata-Parser](https://github.com/Offroaders123/Gamedata-Parser) (Legacy Console `GAMEDATA` files)
- [MCRegionJS](https://github.com/Offroaders123/MCRegionJS) (Legacy Console Region files)
- [Bedrock-LevelDB](https://github.com/Offroaders123/Bedrock-LevelDB) (Bedrock Region files)

I haven't looked into Java quite yet (I just have a small local demo), funnily enough because it was the *most* documented out of them all. So, to make sure that this could be somewhat fairly doable, I wanted to start out with the less-documented, more challenging versions which are a bit more obscured in their format, before thinking the rest would be as straightforward as Java.
@Offroaders123
Copy link
Owner Author

This has been superseded by Region-Types!

@Offroaders123 Offroaders123 closed this as not planned Won't fix, can't repro, duplicate, stale Sep 17, 2023
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