implement tile entity add + remove, and make loading more robust.
All checks were successful
Node.js Build / build (20.x) (push) Successful in 5m18s
All checks were successful
Node.js Build / build (20.x) (push) Successful in 5m18s
This commit is contained in:
parent
c0872ead39
commit
d82b86546a
15 changed files with 75 additions and 1133 deletions
1133
package-lock.json
generated
1133
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -172,6 +172,18 @@ export default class Chunk {
|
|||
return this.skyLight.set(x << 11 | z << 7 | y, value);
|
||||
}
|
||||
|
||||
public getTileEntity(x:number, y:number, z:number) {
|
||||
return this.tileEntities.get((x & 0xf) << 11 | (z & 0xf) << 7 | y);
|
||||
}
|
||||
|
||||
public setTileEntity(tileEntity:TileEntity, x:number, y:number, z:number) {
|
||||
return this.tileEntities.set((x & 0xf) << 11 | (z & 0xf) << 7 | y, tileEntity);
|
||||
}
|
||||
|
||||
public removeTileEntity(x:number, y:number, z:number) {
|
||||
return this.tileEntities.remove((x & 0xf) << 11 | (z & 0xf) << 7 | y);
|
||||
}
|
||||
|
||||
public getBlockBuffer() {
|
||||
return Buffer.from(this.blocks);
|
||||
}
|
||||
|
|
|
@ -196,6 +196,7 @@ export default class MPClient {
|
|||
//this.inventory.addItemStack(new ItemStack(Block.blockBehaviours[brokenBlockId].droppedItem(brokenBlockId), 1, metadata));
|
||||
//this.send(new PacketWindowItems(0, this.inventory.getInventorySize(), this.inventory.constructInventoryPayload()).writeData());
|
||||
const blockBehaviour = Block.blockBehaviours[brokenBlockId];
|
||||
blockBehaviour?.destroyed(this.entity.world, x, y, z);
|
||||
const itemId = blockBehaviour.droppedItem(brokenBlockId);
|
||||
if (itemId !== -1) {
|
||||
const itemCount = blockBehaviour.droppedCount(brokenBlockId);
|
||||
|
@ -277,6 +278,7 @@ export default class MPClient {
|
|||
if (this.entity.world.getBlockId(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z) === 0) {
|
||||
itemStack.size--;
|
||||
this.entity.world.setBlockAndMetadataWithNotify(this.diggingAt.x, this.diggingAt.y, this.diggingAt.z, itemStack.itemID, itemStack.damage);
|
||||
Block.blockBehaviours[itemStack.itemID]?.placed(this.entity.world, this.diggingAt.x, this.diggingAt.y, this.diggingAt.z);
|
||||
this.inventory.dropEmptyItemStacks();
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -16,4 +16,8 @@ export default class Rotation extends Vec2 {
|
|||
public set pitch(value:number) {
|
||||
this.y = value;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Rotation(${this.x},${this.y})`;
|
||||
}
|
||||
}
|
|
@ -32,4 +32,8 @@ export default class Vec2 {
|
|||
this.x = this.y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Vec2(${this.x},${this.y})`;
|
||||
}
|
||||
}
|
|
@ -72,4 +72,8 @@ export default class Vec3 {
|
|||
toAbs() {
|
||||
return new Vec3(Math.round(this.x * 32), Math.round(this.y * 32), Math.round(this.z * 32));
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Vec3(${this.x},${this.y},${this.z})`;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import SaveCompressionType from "./enums/SaveCompressionType";
|
|||
import TileEntityLoader from "./tileentities/TileEntityLoader";
|
||||
import UnsupportedError from "./errors/UnsupportedError";
|
||||
import World from "./World";
|
||||
import Block from "./blocks/Block";
|
||||
|
||||
enum FileMagic {
|
||||
Chunk = 0xFC,
|
||||
|
@ -267,7 +268,12 @@ export default class WorldSaveManager {
|
|||
const tileEntityCount = chunkData.readUShort();
|
||||
for (let i = 0; i < tileEntityCount; i++) {
|
||||
const tileEntity = TileEntityLoader.FromSave(chunkData);
|
||||
chunk.tileEntities.set(tileEntity.pos.x << 11 | tileEntity.pos.z << 7 | tileEntity.pos.y, tileEntity);
|
||||
const blockAtTileEntity = chunk.getBlockId(tileEntity.pos.x, tileEntity.pos.y, tileEntity.pos.z);
|
||||
if (blockAtTileEntity === tileEntity.forBlock.blockId) {
|
||||
chunk.tileEntities.set(tileEntity.pos.x << 11 | tileEntity.pos.z << 7 | tileEntity.pos.y, tileEntity);
|
||||
} else {
|
||||
Console.printWarn(`Tile entity in chunk ${chunk.x},${chunk.z} block ${tileEntity.pos} has no associated block of type ${tileEntity.forBlock.blockName}, instead found ${Block.blockNames[blockAtTileEntity] ?? "Air"}. Skipping...`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import BlockBehaviourStone from "./BlockBehaviourStone";
|
|||
import BlockBehaviourTallGrass from "./BlockBehaviourTallGrass";
|
||||
import IBlockBehaviour from "./IBlockBehaviour";
|
||||
import World from "../World";
|
||||
import BlockBehaviourChest from "./BlockBehaviourChest";
|
||||
|
||||
abstract class Behaviour {
|
||||
public static base = new BlockBehaviour();
|
||||
|
@ -23,6 +24,8 @@ abstract class Behaviour {
|
|||
public static tallGrass = new BlockBehaviourTallGrass();
|
||||
public static flower = new BlockBehaviourFlower();
|
||||
|
||||
public static chest = new BlockBehaviourChest();
|
||||
|
||||
public static redstoneOre = new BlockBehaviourRedstoneOre();
|
||||
|
||||
public static clay = new BlockBehaviourClay();
|
||||
|
@ -210,7 +213,7 @@ export default class Block {
|
|||
static readonly fire = new Block(51).setHardness(0).setLightEmission(1).setBlockName("Fire"); // TODO: Behavior script
|
||||
static readonly mobSpawner = new Block(52).setHardness(5).setBlockName("Mob Spawner"); // TODO: Behavior script
|
||||
static readonly woodenStairs = new Block(53).setBlockName("Wooden Stairs"); // TODO: Behavior script
|
||||
static readonly chest = new Block(54).setHardness(2.5).setBlockName("Chest"); // TODO: Behavior script
|
||||
static readonly chest = new Block(54).setHardness(2.5).setBehaviour(Behaviour.chest).setBlockName("Chest"); // TODO: Behavior script
|
||||
static readonly redstoneDust = new Block(55).setHardness(0).setBlockName("Redstone Dust"); // TODO: Behavior script
|
||||
static readonly diamondOre = new Block(56).setHardness(3).setBlockName("Diamond Ore"); // TODO: Behavior script
|
||||
static readonly diamondBlock = new Block(57).setHardness(5).setBlockName("Diamond Block"); // TODO: Behavior script
|
||||
|
|
|
@ -7,6 +7,8 @@ import World from "../World";
|
|||
export default class BlockBehaviour implements IBlockBehaviour {
|
||||
public block!:Block;
|
||||
|
||||
public placed(world:World, x:number, y:number, z:number) {}
|
||||
public destroyed(world:World, x:number, y:number, z:number) {}
|
||||
public neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number) {}
|
||||
public droppedItem(blockId:number) { return blockId; }
|
||||
public droppedCount(blockId:number) { return 1; }
|
||||
|
|
16
server/blocks/BlockBehaviourChest.ts
Normal file
16
server/blocks/BlockBehaviourChest.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import TileEntityChest from "../tileentities/TileEntityChest";
|
||||
import Vec3 from "../Vec3";
|
||||
import World from "../World";
|
||||
import BlockBehaviour from "./BlockBehaviour";
|
||||
|
||||
export default class BlockBehaviourChest extends BlockBehaviour {
|
||||
public placed(world:World, x:number, y:number, z:number) {
|
||||
const chunk = world.getChunk(x >> 4, z >> 4);
|
||||
chunk.setTileEntity(new TileEntityChest(new Vec3(x & 0xf, y, z & 0xf)), x, y, z);
|
||||
}
|
||||
|
||||
public destroyed(world:World, x:number, y:number, z:number) {
|
||||
const chunk = world.getChunk(x >> 4, z >> 4);
|
||||
chunk.removeTileEntity(x, y, z);
|
||||
}
|
||||
}
|
|
@ -6,6 +6,8 @@ import World from "../World";
|
|||
export default interface IBlockBehaviour {
|
||||
block:Block,
|
||||
|
||||
placed(world:World, x:number, y:number, z:number): void,
|
||||
destroyed(world:World, x:number, y:number, z:number): void,
|
||||
neighborBlockChange(world:World, x:number, y:number, z:number, blockId:number): void,
|
||||
droppedItem: (blockId:number) => number,
|
||||
droppedCount: (blockId:number) => number,
|
||||
|
|
|
@ -42,6 +42,7 @@ export default class Player extends EntityLiving {
|
|||
this.inventory.setSlotItemStack(37, new ItemStack(Item.ironPickaxe, 1));
|
||||
this.inventory.setSlotItemStack(38, new ItemStack(Item.ironShovel, 1));
|
||||
this.inventory.setSlotItemStack(39, new ItemStack(Item.ironAxe, 1));
|
||||
this.inventory.setSlotItemStack(42, new ItemStack(Block.chest, 32));
|
||||
this.inventory.setSlotItemStack(43, new ItemStack(Block.dirt, 32));
|
||||
|
||||
this.trackedEquipment = new Array<ItemStack | null>();
|
||||
|
|
|
@ -2,9 +2,8 @@ import { IReader, IWriter } from "bufferstuff";
|
|||
import Block from "../blocks/Block";
|
||||
import TileEntityType from "../enums/TileEntityType";
|
||||
import Vec3 from "../Vec3";
|
||||
import TileEntityChest from "./TileEntityChest";
|
||||
|
||||
export default class TileEntity {
|
||||
export default abstract class TileEntity {
|
||||
public readonly type: TileEntityType;
|
||||
public readonly forBlock: Block;
|
||||
public readonly pos: Vec3;
|
||||
|
@ -19,7 +18,6 @@ export default class TileEntity {
|
|||
|
||||
public toSave(writer:IWriter) {
|
||||
writer.writeUByte(this.type);
|
||||
writer.writeUByte(this.forBlock.blockId);
|
||||
writer.writeUByte(this.pos.x).writeUByte(this.pos.y).writeUByte(this.pos.z);
|
||||
}
|
||||
}
|
|
@ -8,8 +8,8 @@ import Inventory from "../inventories/Inventory";
|
|||
export default class TileEntityChest extends TileEntity {
|
||||
public inventory:Inventory;
|
||||
|
||||
public constructor(type: TileEntityType, forBlockId: Block, position: Vec3) {
|
||||
super(type, forBlockId, position);
|
||||
public constructor(position: Vec3) {
|
||||
super(TileEntityType.Chest, Block.chest, position);
|
||||
|
||||
this.inventory = new Inventory(9 * 3, "Chest");
|
||||
}
|
||||
|
|
|
@ -1,21 +1,20 @@
|
|||
import { IReader } from "bufferstuff";
|
||||
import Block from "../blocks/Block";
|
||||
import TileEntity from "./TileEntity";
|
||||
import TileEntityChest from "./TileEntityChest";
|
||||
import TileEntityType from "../enums/TileEntityType";
|
||||
import Vec3 from "../Vec3";
|
||||
import UnsupportedError from "../errors/UnsupportedError";
|
||||
|
||||
// I would like this to be in TileEntity, but recursive dependency.
|
||||
export default abstract class TileEntityLoader {
|
||||
public static FromSave(reader:IReader) : TileEntity {
|
||||
let tileEntity:TileEntity;
|
||||
const type: TileEntityType = reader.readUByte();
|
||||
const forBlock = Block.blocks[reader.readUByte()];
|
||||
const position = new Vec3(reader.readUByte(), reader.readUByte(), reader.readUByte());
|
||||
if (type === TileEntityType.Chest) {
|
||||
tileEntity = new TileEntityChest(type, forBlock, position);
|
||||
tileEntity = new TileEntityChest(position);
|
||||
} else {
|
||||
tileEntity = new TileEntity(type, forBlock, position);
|
||||
throw new UnsupportedError("Unsupported tile entity type encountered");
|
||||
}
|
||||
// Call instantiated class' fromSave
|
||||
tileEntity.fromSave(reader);
|
||||
|
|
Loading…
Reference in a new issue