Skip to content

Commit

Permalink
advance minecraft parser and builder
Browse files Browse the repository at this point in the history
  • Loading branch information
YoanWithY committed Sep 30, 2024
1 parent c1232c4 commit 57a3608
Show file tree
Hide file tree
Showing 39 changed files with 2,606 additions and 954 deletions.
246 changes: 168 additions & 78 deletions frontend/package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@
"typescript": "^5.5.3",
"vite": "^5.4.1",
"vite-svg-loader": "^5.1.0"
},
"dependencies": {
"unzipit": "^1.4.3"
}
}
}
Binary file removed frontend/public/dirt.png
Binary file not shown.
Binary file added frontend/public/glowstone.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed frontend/public/untitled (1).png
Binary file not shown.
Binary file removed frontend/public/untitled color.png
Binary file not shown.
1 change: 0 additions & 1 deletion frontend/public/vite.svg

This file was deleted.

12 changes: 12 additions & 0 deletions frontend/src/c/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
BasedOnStyle: LLVM
IndentWidth: 4
TabWidth: 4
UseTab: Always
PointerAlignment: Left
AlignTrailingComments: true
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
BreakBeforeBraces: Attach
ColumnLimit: 0
SpacesBeforeTrailingComments: 1
238 changes: 201 additions & 37 deletions frontend/src/c/hello.cpp
Original file line number Diff line number Diff line change
@@ -1,68 +1,232 @@
#include <emscripten/bind.h>
#include "parse/minecraftWorld.hpp"
#include "parse/nbt.hpp"
#include "parse/parse.hpp"
#include "zlibUtil/zlibUtil.hpp"
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstring>
#include <emscripten/bind.h>
#include <iostream>
#include <map>
#include <tuple>
#include <vector>
#include "zlibUtil/zlibUtil.hpp"
#include "parse/parse.hpp"
#include "nbt/nbt.hpp"

void helloWorld()
{
void helloWorld() {
std::cout << "Hello from WASM!" << std::endl;
}

struct ChunkDescription
{
std::map<std::string, MinecraftWorld*> minecraftWorlds;

struct ChunkDescription {
uint32_t offset;
uint8_t sectorCount;
};

ChunkDescription getChunkDescription(const Bytef *data, uint8_t x, uint8_t y)
{
ChunkDescription getChunkDescription(const Bytef* data, uint8_t x, uint8_t y) {
uint32_t off = (y * 32 + x) * 4;
uint32_t offset = readU32AsBigEndian(data + off, 3);
uint8_t sectorCount = CHAR_TO_U8(data[off + 3]);
return {offset, sectorCount};
}

void readChunk(const Bytef *data, ChunkDescription chunkDescription)
{
void buildChunkSections(std::map<std::tuple<int, int8_t, int>, const uint16_t*>& sectionsMap, const NBT::CompoundTag* chunk) {
NBT::ListTag* sections = chunk->getTag<NBT::ListTag>("sections");
int xPos = chunk->getTag<NBT::IntTag>("xPos")->value;
int zPos = chunk->getTag<NBT::IntTag>("zPos")->value;

for (NBT::NBTBase* sectionBase : sections->value) {
NBT::CompoundTag* section = sectionBase->as<NBT::CompoundTag>();
int8_t yPos = section->getTag<NBT::ByteTag>("Y")->value;
NBT::CompoundTag* block_states = section->getTag<NBT::CompoundTag>("block_states");
NBT::ListTag* palette = block_states->getTag<NBT::ListTag>("palette");
int paletteSize = palette->value.size();
if (paletteSize <= 1)
continue;

uint8_t bitsPerIndex = std::max(static_cast<uint8_t>(std::ceil(std::log2(paletteSize))), uint8_t(4));
uint64_t mask = (1ULL << bitsPerIndex) - 1;

uint8_t indicesPerLong = 64 / bitsPerIndex;
NBT::LongArrayTag* data = block_states->getTag<NBT::LongArrayTag>("data");

uint16_t* sectionIndices = new uint16_t[4096];
int longIndex = 0;
uint8_t indexInLong = 0;
int64_t currentLong = data->value[longIndex];
for (int i = 0; i < 4096; i++) {
if (indexInLong == indicesPerLong) {
indexInLong = 0;
longIndex++;
currentLong = data->value[longIndex];
}
sectionIndices[i] = extractIndex(currentLong, indexInLong * bitsPerIndex, mask);
indexInLong++;
}

sectionsMap[std::make_tuple(xPos, yPos, zPos)] = sectionIndices;
}
}

NBT::CompoundTag* readChunk(const Bytef* data, ChunkDescription chunkDescription) {
uint32_t byteOffset = chunkDescription.offset * 4096;
size_t byteSize = chunkDescription.sectorCount * 4096;
const Bytef *chunk = data + byteOffset;
const Bytef* chunk = data + byteOffset;
uint32_t chunkDataLength = readU32AsBigEndian(chunk, 4);
uint8_t compressionType = CHAR_TO_U8(chunk[4]);
std::cout << "length: " << chunkDataLength << " compression type: " << int32_t(compressionType) << std::endl;
size_t size = 0;
const Bytef *res = zlib_decompress(chunk + 5, chunkDataLength - 1, &size);
const Bytef* res = zlib_decompress(chunk + 5, chunkDataLength - 1, &size);

size_t progress = 0;
NBTBase *tag = parseNBT(res, progress);
if (tag->id == 10)
{
CompoundTag *c = tag->as<CompoundTag>();
c->display();
}
NBT::NBTBase* tag = NBT::parseNBT(res, progress);
auto t = tag->as<NBT::CompoundTag>();

return t;
}

int buildChunk(std::string world, int dimension, int chunkX, int chunkZ) {
auto minecraftWorld = minecraftWorlds[world];
if (!minecraftWorld)
return -1;

int regionX = chunkX >> 5;
int regionZ = chunkZ >> 5;
auto region = minecraftWorld->getRegionsByDimension(dimension)[std::make_tuple(regionX, regionZ)];
if (!region)
return -2;

int innerChunkX = modulus(chunkX, 32);
int innerChunkZ = modulus(chunkZ, 32);
auto chunk = readChunk(region, getChunkDescription(region, innerChunkX, innerChunkZ));
if (!chunk)
return -3;

auto& chunks = minecraftWorld->getChunksByDimension(dimension);
chunks[std::make_tuple(chunkX, chunkZ)] = chunk;

auto& sections = minecraftWorld->getSectionsByDimension(dimension);
buildChunkSections(sections, chunk);
return 0;
}

const NBT::CompoundTag* activeChunk = nullptr;
int setActiveChunk(std::string world, int dimension, int chunkX, int chunkZ) {
auto minecraftWorld = minecraftWorlds[world];
if (!minecraftWorld)
return -1;

auto chunk = minecraftWorld->getChunksByDimension(dimension)[std::make_tuple(chunkX, chunkZ)];
if (!chunk)
return -2;

activeChunk = chunk;
return 0;
}

int readFileAsString(std::string file)
{
const Bytef *data = reinterpret_cast<const Bytef *>(file.data());
for (uint8_t y = 0; y < 32; y++)
{
for (uint8_t x = 0; x < 32; x++)
{
ChunkDescription d = getChunkDescription(data, x, y);
std::cout << "x: " << uint32_t(x) << " y: " << uint32_t(y) << " offset: " << d.offset << " sectors: " << uint32_t(d.sectorCount) << std::endl;
std::string getPalette(int8_t y) {
auto sections = activeChunk->getTag<NBT::ListTag>("sections");
for (auto sectionBase : sections->value) {
auto section = sectionBase->as<NBT::CompoundTag>();
auto Y = section->getTag<NBT::ByteTag>("Y")->value;
if (y == Y) {
std::ostringstream oss;
section->getTag<NBT::CompoundTag>("block_states")->getTag<NBT::ListTag>("palette")->displayContent(oss);
return oss.str();
}
}
readChunk(data, getChunkDescription(data, 0, 0));
return 0;
return "";
}

emscripten::val getSectionView(std::string world, int dimension, int sectionX, int8_t sectionY, int sectionZ) {
auto minecraftWorld = minecraftWorlds[world];
if (!minecraftWorld) {
std::ostringstream oss;
oss << "The given world: \"" << world << "\" is not known." << std::endl;
throw std::runtime_error(oss.str());
}

auto section = minecraftWorld->getSectionsByDimension(dimension)[std::make_tuple(sectionX, sectionY, sectionZ)];
if (!section) {
std::ostringstream oss;
oss << "The given section at dimension: " << dimension << ", X: " << sectionX << ", Y: " << sectionY << ", Z: " << sectionZ << " is not known." << std::endl;
throw std::runtime_error(oss.str());
}
return emscripten::val(emscripten::typed_memory_view(4096 * 2, section));
}

int8_t getByte(std::string name) {
return NBT::getGeneric<int8_t, NBT::ByteTag>(activeChunk, name);
}

int16_t getShort(std::string name) {
return NBT::getGeneric<int16_t, NBT::ShortTag>(activeChunk, name);
}

int32_t getInt(std::string name) {
return NBT::getGeneric<int32_t, NBT::IntTag>(activeChunk, name);
}

int64_t getLong(std::string name) {
return NBT::getGeneric<int64_t, NBT::LongTag>(activeChunk, name);
}

float getFloat(std::string name) {
return NBT::getGeneric<float, NBT::FloatTag>(activeChunk, name);
}

double getDouble(std::string name) {
return NBT::getGeneric<double, NBT::DoubleTag>(activeChunk, name);
}

emscripten::val getByteArray(std::string name) {
NBT::ByteArrayTag* bat = NBT::getTag<NBT::ByteArrayTag>(activeChunk, name);
int8_t* data = bat->value.data();
size_t bufferLength = bat->value.size();
return emscripten::val(emscripten::typed_memory_view(bufferLength, data));
}

std::string getString(std::string name) {
return NBT::getGeneric<std::string, NBT::StringTag>(activeChunk, name);
}

std::string getList(std::string name) {
std::ostringstream oss;
NBT::getTag<NBT::ListTag>(activeChunk, name)->displayContent(oss);
return oss.str();
}

std::string getCompound(std::string name) {
std::ostringstream oss;
NBT::getTag<NBT::CompoundTag>(activeChunk, name)->displayContent(oss);
return oss.str();
}

void openRegion(std::string world, int dimension, int x, int y, std::string file) {
uint8_t* data = new Bytef[file.size()];
std::memcpy(data, file.data(), file.size());
auto minecraftWorld = minecraftWorlds[world];
if (!minecraftWorld) {
minecraftWorld = new MinecraftWorld();
minecraftWorlds[world] = minecraftWorld;
}
auto& regionMap = minecraftWorld->getRegionsByDimension(dimension);
regionMap[std::tuple<int, int>(x, y)] = data;
}

using namespace emscripten;
EMSCRIPTEN_BINDINGS(my_module)
{
EMSCRIPTEN_BINDINGS(my_module) {
function("helloWorld", &helloWorld);
function("readFileAsString", &readFileAsString);
function("openRegion", &openRegion);
function("setActiveChunk", &setActiveChunk);
function("getByte", &getByte);
function("getShort", &getShort);
function("getInt", &getInt);
function("getLong", &getLong);
function("getFloat", &getFloat);
function("getDouble", &getDouble);
function("getByteArray", &getByteArray);
function("getString", &getString);
function("getList", &getList);
function("getCompound", &getCompound);
function("buildChunk", &buildChunk);
function("getSectionView", &getSectionView);
function("getPalette", &getPalette);
}
16 changes: 15 additions & 1 deletion frontend/src/c/hello.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,21 @@ interface WasmModule {
type EmbindString = ArrayBuffer|Uint8Array|Uint8ClampedArray|Int8Array|string;
interface EmbindModule {
helloWorld(): void;
readFileAsString(_0: EmbindString): number;
openRegion(_0: EmbindString, _1: number, _2: number, _3: number, _4: EmbindString): void;
setActiveChunk(_0: EmbindString, _1: number, _2: number, _3: number): number;
getByte(_0: EmbindString): number;
getShort(_0: EmbindString): number;
getInt(_0: EmbindString): number;
getLong(_0: EmbindString): bigint;
getFloat(_0: EmbindString): number;
getDouble(_0: EmbindString): number;
getString(_0: EmbindString): string;
getList(_0: EmbindString): string;
getCompound(_0: EmbindString): string;
buildChunk(_0: EmbindString, _1: number, _2: number, _3: number): number;
getPalette(_0: number): string;
getByteArray(_0: EmbindString): any;
getSectionView(_0: EmbindString, _1: number, _2: number, _3: number, _4: number): any;
}

export type MainModule = WasmModule & typeof RuntimeExports & EmbindModule;
Expand Down
Loading

0 comments on commit 57a3608

Please sign in to comment.