2023-06-22 12:43:28 +01:00
|
|
|
import { createWriter, Endian } from "bufferstuff";
|
2024-07-08 09:56:03 +01:00
|
|
|
import FunkyArray from "funky-array";
|
2024-10-26 14:24:38 +01:00
|
|
|
import ItemStack from "./inventories/ItemStack";
|
|
|
|
import MetadataFieldType from "./enums/MetadataFieldType";
|
2024-02-16 09:34:02 +00:00
|
|
|
import Vec3 from "./Vec3";
|
2023-04-10 21:52:30 +01:00
|
|
|
|
|
|
|
export class MetadataEntry {
|
|
|
|
public type:MetadataFieldType;
|
2024-02-16 09:34:02 +00:00
|
|
|
public value:number|string|Vec3|ItemStack;
|
2023-04-10 21:52:30 +01:00
|
|
|
|
2024-02-16 09:34:02 +00:00
|
|
|
public constructor(type:MetadataFieldType, value:number|string|Vec3|ItemStack) {
|
2023-04-10 21:52:30 +01:00
|
|
|
this.type = type;
|
|
|
|
this.value = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export class MetadataWriter {
|
|
|
|
// https://wiki.vg/index.php?title=Protocol&oldid=488#Entity_Metadata_.280x28.29
|
|
|
|
private entries:FunkyArray<number, MetadataEntry>; // TODO: Extend with Item and Vector types
|
|
|
|
|
|
|
|
public constructor() {
|
|
|
|
this.entries = new FunkyArray<number, MetadataEntry>();
|
|
|
|
}
|
|
|
|
|
|
|
|
public addMetadataEntry(identifier:number, entry:MetadataEntry) {
|
|
|
|
this.entries.set(identifier, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
private calculateBufferSize() {
|
|
|
|
let size = this.entries.length + 1; // Type/Identifiers + Stream end magic
|
|
|
|
this.entries.forEach(entry => {
|
|
|
|
switch (entry.type) {
|
|
|
|
case MetadataFieldType.Byte: size += 1; break;
|
|
|
|
case MetadataFieldType.Short: size += 2; break;
|
|
|
|
case MetadataFieldType.Int: size += 4; break;
|
|
|
|
case MetadataFieldType.Float: size += 4; break;
|
|
|
|
case MetadataFieldType.String:
|
|
|
|
if (typeof(entry.value) === "string") {
|
|
|
|
size += 2 + entry.value.length * 2; break;
|
2023-10-29 05:08:26 +00:00
|
|
|
} else {
|
|
|
|
throw "Non-string value assigned to a String MetadataEntry";
|
2023-04-10 21:52:30 +01:00
|
|
|
}
|
2024-02-16 09:34:02 +00:00
|
|
|
case MetadataFieldType.Item:
|
|
|
|
if (entry.value instanceof ItemStack) {
|
|
|
|
size += 5;
|
|
|
|
} else {
|
|
|
|
throw "Non-ItemStack value assigned to an ItemStack MetadataEntry";
|
|
|
|
}
|
|
|
|
case MetadataFieldType.Vector:
|
|
|
|
if (entry.value instanceof Vec3) {
|
|
|
|
size += 12;
|
|
|
|
} else {
|
|
|
|
throw "Non-Vec3 value assigned to an Vec3 MetadataEntry";
|
|
|
|
}
|
2023-04-10 21:52:30 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
public writeBuffer() {
|
2023-05-02 10:24:48 +01:00
|
|
|
const writer = createWriter(Endian.BE, this.calculateBufferSize());
|
2023-06-26 10:00:06 +01:00
|
|
|
for (const key of this.entries.keys) {
|
2023-04-10 21:52:30 +01:00
|
|
|
const entry = this.entries.get(key);
|
|
|
|
if (entry instanceof MetadataEntry) {
|
|
|
|
writer.writeByte((entry.type << 5 | key & 0x1f) & 0xff);
|
|
|
|
if (typeof(entry.value) === "number") {
|
|
|
|
switch (entry.type) {
|
|
|
|
case MetadataFieldType.Byte: writer.writeByte(entry.value); break;
|
|
|
|
case MetadataFieldType.Short: writer.writeShort(entry.value); break;
|
|
|
|
case MetadataFieldType.Int: writer.writeInt(entry.value); break;
|
|
|
|
case MetadataFieldType.Float: writer.writeFloat(entry.value); break;
|
|
|
|
}
|
|
|
|
} else if (typeof(entry.value) === "string") {
|
2023-05-02 10:24:48 +01:00
|
|
|
writer.writeString16(entry.value);
|
2024-02-16 09:34:02 +00:00
|
|
|
} else if (entry.value instanceof ItemStack) {
|
|
|
|
writer.writeShort(entry.value.itemID).writeByte(entry.value.size).writeShort(entry.value.damage);
|
|
|
|
} else if (entry.value instanceof Vec3) {
|
|
|
|
const absVec = entry.value.toAbs();
|
|
|
|
writer.writeInt(absVec.x).writeInt(absVec.y).writeInt(absVec.z);
|
2023-04-10 21:52:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Metadata end magic
|
|
|
|
writer.writeByte(0x7F);
|
|
|
|
|
|
|
|
return writer.toBuffer();
|
|
|
|
}
|
|
|
|
}
|