WIP: Inventories
This commit is contained in:
parent
74374cb0d6
commit
d8d1eabcf4
19 changed files with 374 additions and 108 deletions
63
package-lock.json
generated
63
package-lock.json
generated
|
@ -9,18 +9,18 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bufferstuff": "^1.3.0",
|
"bufferstuff": "^1.3.4",
|
||||||
"hsconsole": "^1.0.2"
|
"hsconsole": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.5.1",
|
"@types/node": "^20.8.9",
|
||||||
"check-outdated": "^2.12.0",
|
"check-outdated": "^2.12.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.22.0",
|
||||||
"ts-loader": "^9.4.4",
|
"ts-loader": "^9.5.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@cspotcode/source-map-support": {
|
"node_modules/@cspotcode/source-map-support": {
|
||||||
|
@ -170,10 +170,13 @@
|
||||||
"peer": true
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "20.5.1",
|
"version": "20.8.9",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz",
|
||||||
"integrity": "sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==",
|
"integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"undici-types": "~5.26.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@webassemblyjs/ast": {
|
"node_modules/@webassemblyjs/ast": {
|
||||||
"version": "1.11.6",
|
"version": "1.11.6",
|
||||||
|
@ -550,9 +553,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/bufferstuff": {
|
"node_modules/bufferstuff": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/bufferstuff/-/bufferstuff-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/bufferstuff/-/bufferstuff-1.3.4.tgz",
|
||||||
"integrity": "sha512-w+9eS70G3pzEAy8PzLt1ZX/h5SYEf+c2FYDu1zMPLldVTqMIoJaNLwCYKAzav3CWFAjJYXTTXCuO51uufBZtZw=="
|
"integrity": "sha512-oBaMs5roIpkG242dWSoS38BWeVxlpTKVhRTRnGmZy5/6H+XU6YJMGa6jmPl7ychEnOqpuSA8wiq2k12zPt4BDA=="
|
||||||
},
|
},
|
||||||
"node_modules/call-bind": {
|
"node_modules/call-bind": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -2325,9 +2328,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.19.2",
|
"version": "5.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.22.0.tgz",
|
||||||
"integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==",
|
"integrity": "sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/source-map": "^0.3.3",
|
"@jridgewell/source-map": "^0.3.3",
|
||||||
|
@ -2402,15 +2405,16 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-loader": {
|
"node_modules/ts-loader": {
|
||||||
"version": "9.4.4",
|
"version": "9.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.4.tgz",
|
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.0.tgz",
|
||||||
"integrity": "sha512-MLukxDHBl8OJ5Dk3y69IsKVFRA/6MwzEqBgh+OXMPB/OD01KQuWPFd1WAQP8a5PeSCAxfnkhiuWqfmFJzJQt9w==",
|
"integrity": "sha512-LLlB/pkB4q9mW2yLdFMnK3dEHbrBjeZTYguaaIfusyojBgAGf5kF+O6KcWqiGzWqHk0LBsoolrp4VftEURhybg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^4.1.0",
|
"chalk": "^4.1.0",
|
||||||
"enhanced-resolve": "^5.0.0",
|
"enhanced-resolve": "^5.0.0",
|
||||||
"micromatch": "^4.0.0",
|
"micromatch": "^4.0.0",
|
||||||
"semver": "^7.3.4"
|
"semver": "^7.3.4",
|
||||||
|
"source-map": "^0.7.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=12.0.0"
|
"node": ">=12.0.0"
|
||||||
|
@ -2435,6 +2439,15 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ts-loader/node_modules/source-map": {
|
||||||
|
"version": "0.7.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
|
||||||
|
"integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ts-node": {
|
"node_modules/ts-node": {
|
||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||||
|
@ -2493,9 +2506,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.1.6",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
|
||||||
"integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==",
|
"integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"bin": {
|
"bin": {
|
||||||
"tsc": "bin/tsc",
|
"tsc": "bin/tsc",
|
||||||
|
@ -2526,6 +2539,12 @@
|
||||||
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
"integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/undici-types": {
|
||||||
|
"version": "5.26.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
|
||||||
|
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"node_modules/update-browserslist-db": {
|
"node_modules/update-browserslist-db": {
|
||||||
"version": "1.0.11",
|
"version": "1.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz",
|
||||||
|
|
10
package.json
10
package.json
|
@ -25,17 +25,17 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/tgpholly/mc-beta-server#readme",
|
"homepage": "https://github.com/tgpholly/mc-beta-server#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bufferstuff": "^1.3.0",
|
"bufferstuff": "^1.3.4",
|
||||||
"hsconsole": "^1.0.2"
|
"hsconsole": "^1.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^20.5.1",
|
"@types/node": "^20.8.9",
|
||||||
"check-outdated": "^2.12.0",
|
"check-outdated": "^2.12.0",
|
||||||
"nodemon": "^3.0.1",
|
"nodemon": "^3.0.1",
|
||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"terser": "^5.19.2",
|
"terser": "^5.22.0",
|
||||||
"ts-loader": "^9.4.4",
|
"ts-loader": "^9.5.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^5.1.6"
|
"typescript": "^5.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,11 +154,11 @@ export class Chunk {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBlockLightBuffer() {
|
public getBlockLightBuffer() {
|
||||||
return this.metadata.toBuffer();
|
return this.blockLight.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getSkyLightBuffer() {
|
public getSkyLightBuffer() {
|
||||||
return this.metadata.toBuffer();
|
return this.skyLight.toBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public getBlockData() {
|
public getBlockData() {
|
||||||
|
|
26
server/EntityMetadata.ts
Normal file
26
server/EntityMetadata.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { MetadataEntry, MetadataWriter } from "./MetadataWriter";
|
||||||
|
import { MetadataFieldType } from "./enums/MetadataFieldType";
|
||||||
|
|
||||||
|
export default class EntityMetadata {
|
||||||
|
public onFire:boolean = false;
|
||||||
|
public crouched:boolean = false;
|
||||||
|
public ridingEntity:boolean = false;
|
||||||
|
|
||||||
|
private finalValue:number = 0;
|
||||||
|
|
||||||
|
private static readonly ENTITY_ON_FIRE = 1 << 0;
|
||||||
|
private static readonly ENTITY_CROUCHING = 1 << 1;
|
||||||
|
private static readonly ENTITY_RIDING = 1 << 2;
|
||||||
|
|
||||||
|
writeMetadata() {
|
||||||
|
const metadataWriter = new MetadataWriter();
|
||||||
|
this.finalValue =
|
||||||
|
(this.onFire ? EntityMetadata.ENTITY_ON_FIRE : 0) | // On Fire
|
||||||
|
(this.crouched ? EntityMetadata.ENTITY_CROUCHING : 0) | // Crouching
|
||||||
|
(this.ridingEntity ? EntityMetadata.ENTITY_CROUCHING : 0); // Riding entity
|
||||||
|
|
||||||
|
metadataWriter.addMetadataEntry(0, new MetadataEntry(MetadataFieldType.Byte, this.finalValue));
|
||||||
|
|
||||||
|
return metadataWriter.writeBuffer();
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,23 +15,30 @@ import { Socket } from "net";
|
||||||
import { Vec3 } from "./Vec3";
|
import { Vec3 } from "./Vec3";
|
||||||
import { PacketRespawn } from "./packets/Respawn";
|
import { PacketRespawn } from "./packets/Respawn";
|
||||||
import { PacketSpawnPosition } from "./packets/SpawnPosition";
|
import { PacketSpawnPosition } from "./packets/SpawnPosition";
|
||||||
|
import { PacketPlayerBlockPlacement } from "./packets/PlayerBlockPlacement";
|
||||||
|
import { Inventory } from "./inventories/Inventory";
|
||||||
|
import { PacketHoldingChange } from "./packets/HoldingChange";
|
||||||
|
import { PacketDisconnectKick } from "./packets/DisconnectKick";
|
||||||
|
|
||||||
export class MPClient {
|
export class MPClient {
|
||||||
private readonly mcServer:MinecraftServer;
|
private readonly mcServer:MinecraftServer;
|
||||||
private readonly socket:Socket;
|
private readonly socket:Socket;
|
||||||
public entity:Player;
|
public entity:Player;
|
||||||
|
private inventory:Inventory;
|
||||||
|
|
||||||
|
private holdingIndex:number = 36; // First hotbar slot.
|
||||||
private diggingAt:Vec3;
|
private diggingAt:Vec3;
|
||||||
|
|
||||||
public constructor(mcServer:MinecraftServer, socket:Socket, entity:Player) {
|
public constructor(mcServer:MinecraftServer, socket:Socket, entity:Player) {
|
||||||
this.mcServer = mcServer;
|
this.mcServer = mcServer;
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.entity = entity;
|
this.entity = entity;
|
||||||
|
this.inventory = entity.inventory;
|
||||||
|
|
||||||
this.diggingAt = new Vec3();
|
this.diggingAt = new Vec3();
|
||||||
}
|
}
|
||||||
|
|
||||||
private mapCoordsToFace(pos:Vec3, face:number) {
|
private mapCoordsFromFace(pos:Vec3, face:number) {
|
||||||
switch (face) {
|
switch (face) {
|
||||||
case 0:
|
case 0:
|
||||||
pos.y--;
|
pos.y--;
|
||||||
|
@ -64,8 +71,8 @@ export class MPClient {
|
||||||
case Packet.PlayerLook: this.handlePacketPlayerLook(new PacketPlayerLook().readData(reader)); break;
|
case Packet.PlayerLook: this.handlePacketPlayerLook(new PacketPlayerLook().readData(reader)); break;
|
||||||
case Packet.PlayerPositionLook: this.handlePacketPlayerPositionLook(new PacketPlayerPositionLook().readData(reader)); break;
|
case Packet.PlayerPositionLook: this.handlePacketPlayerPositionLook(new PacketPlayerPositionLook().readData(reader)); break;
|
||||||
case Packet.PlayerDigging: this.handlePacketPlayerDigging(new PacketPlayerDigging().readData(reader)); break;
|
case Packet.PlayerDigging: this.handlePacketPlayerDigging(new PacketPlayerDigging().readData(reader)); break;
|
||||||
//case Packets.PlayerBlockPlacement: break;
|
case Packet.PlayerBlockPlacement: this.handlePacketBlockPlacement(new PacketPlayerBlockPlacement().readData(reader)); break;
|
||||||
//case Packets.HoldingChange: break;
|
case Packet.HoldingChange: this.handlePacketHoldingChange(new PacketHoldingChange().readData(reader)); break;
|
||||||
//case Packets.UseBed: break;
|
//case Packets.UseBed: break;
|
||||||
case Packet.Animation: this.handlePacketAnimation(new PacketAnimation().readData(reader)); break;
|
case Packet.Animation: this.handlePacketAnimation(new PacketAnimation().readData(reader)); break;
|
||||||
case Packet.EntityAction: this.handlePacketEntityAction(new PacketEntityAction().readData(reader)); break;
|
case Packet.EntityAction: this.handlePacketEntityAction(new PacketEntityAction().readData(reader)); break;
|
||||||
|
@ -154,6 +161,27 @@ export class MPClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private handlePacketBlockPlacement(packet:PacketPlayerBlockPlacement) {
|
||||||
|
this.diggingAt.set(packet.x, packet.y, packet.z);
|
||||||
|
this.mapCoordsFromFace(this.diggingAt, packet.face);
|
||||||
|
|
||||||
|
const itemStack = this.inventory.getSlotItemStack(this.holdingIndex);
|
||||||
|
if (itemStack != null && itemStack.size > 0 && 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handlePacketHoldingChange(packet:PacketHoldingChange) {
|
||||||
|
if (packet.slotId < 0 || packet.slotId > 8) {
|
||||||
|
this.send(new PacketDisconnectKick("Out of Bounds Holding Index!").writeData());
|
||||||
|
this.socket.end();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.holdingIndex = 36 + packet.slotId;
|
||||||
|
}
|
||||||
|
|
||||||
// Animation start
|
// Animation start
|
||||||
private handlePacketAnimation(packet:PacketAnimation) {
|
private handlePacketAnimation(packet:PacketAnimation) {
|
||||||
// Forward this packet to all nearby clients
|
// Forward this packet to all nearby clients
|
||||||
|
|
|
@ -35,6 +35,8 @@ export class MetadataWriter {
|
||||||
case MetadataFieldType.String:
|
case MetadataFieldType.String:
|
||||||
if (typeof(entry.value) === "string") {
|
if (typeof(entry.value) === "string") {
|
||||||
size += 2 + entry.value.length * 2; break;
|
size += 2 + entry.value.length * 2; break;
|
||||||
|
} else {
|
||||||
|
throw "Non-string value assigned to a String MetadataEntry";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { Chunk } from "./Chunk";
|
||||||
import { PacketTimeUpdate } from "./packets/TimeUpdate";
|
import { PacketTimeUpdate } from "./packets/TimeUpdate";
|
||||||
import { HillyGenerator } from "./generators/Hilly";
|
import { HillyGenerator } from "./generators/Hilly";
|
||||||
import { NetherGenerator } from "./generators/Nether";
|
import { NetherGenerator } from "./generators/Nether";
|
||||||
|
import { PacketWindowItems } from "./packets/WindowItems";
|
||||||
|
|
||||||
export class MinecraftServer {
|
export class MinecraftServer {
|
||||||
private static readonly PROTOCOL_VERSION = 14;
|
private static readonly PROTOCOL_VERSION = 14;
|
||||||
|
@ -233,6 +234,10 @@ export class MinecraftServer {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.write(new PacketPlayerPositionLook(8, 70, 70.62, 8, 0, 0, false).writeData());
|
socket.write(new PacketPlayerPositionLook(8, 70, 70.62, 8, 0, 0, false).writeData());
|
||||||
|
|
||||||
|
const playerInventory = clientEntity.inventory;
|
||||||
|
socket.write(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload()).writeData());
|
||||||
|
console.log(new PacketWindowItems(0, playerInventory.getInventorySize(), playerInventory.constructInventoryPayload()).writeData());
|
||||||
} else {
|
} else {
|
||||||
socket.write(new PacketDisconnectKick("Failed to find world to put player in.").writeData());
|
socket.write(new PacketDisconnectKick("Failed to find world to put player in.").writeData());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
import { ItemStack } from "./ItemStack";
|
|
||||||
import { ContainerSlot } from "./Slot";
|
|
||||||
|
|
||||||
export abstract class Container {
|
|
||||||
public itemSlots:Array<ContainerSlot>;
|
|
||||||
public itemStacks:Array<ItemStack>;
|
|
||||||
|
|
||||||
public constructor() {
|
|
||||||
this.itemSlots = new Array<ContainerSlot>();
|
|
||||||
this.itemStacks = new Array<ItemStack>();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
import { Block } from "../blocks/Block";
|
|
||||||
import { Item } from "../items/Item";
|
|
||||||
|
|
||||||
export class ItemStack {
|
|
||||||
public readonly itemID:number;
|
|
||||||
public stackSize:number;
|
|
||||||
public damage:number;
|
|
||||||
|
|
||||||
public constructor(blockOrItemOrItemID:Block|Item|number, stackSize?:number, damage?:number) {
|
|
||||||
if (blockOrItemOrItemID instanceof Block && stackSize === undefined && damage === undefined) {
|
|
||||||
this.itemID = blockOrItemOrItemID.blockId;
|
|
||||||
this.stackSize = 1;
|
|
||||||
this.damage = 0;
|
|
||||||
} else if (blockOrItemOrItemID instanceof Block && typeof(stackSize) === "number" && damage === undefined) {
|
|
||||||
this.itemID = blockOrItemOrItemID.blockId;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
this.damage = 0;
|
|
||||||
} else if (blockOrItemOrItemID instanceof Block && typeof(stackSize) === "number" && typeof(damage) === "number") {
|
|
||||||
this.itemID = blockOrItemOrItemID.blockId;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
this.damage = damage;
|
|
||||||
} else if (blockOrItemOrItemID instanceof Item && stackSize === undefined && damage === undefined) {
|
|
||||||
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
|
||||||
this.stackSize = 1;
|
|
||||||
this.damage = 0;
|
|
||||||
} else if (blockOrItemOrItemID instanceof Item && typeof(stackSize) === "number" && damage === undefined) {
|
|
||||||
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
this.damage = 0;
|
|
||||||
} else if (blockOrItemOrItemID instanceof Item && typeof(stackSize) === "number" && typeof(damage) === "number") {
|
|
||||||
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
this.damage = damage;
|
|
||||||
} else if (typeof(blockOrItemOrItemID) === "number" && typeof(stackSize) === "number" && typeof(damage) === "number") {
|
|
||||||
this.itemID = blockOrItemOrItemID;
|
|
||||||
this.stackSize = stackSize;
|
|
||||||
this.damage = damage;
|
|
||||||
} else {
|
|
||||||
this.itemID = Number.MIN_VALUE;
|
|
||||||
this.stackSize = Number.MIN_VALUE;
|
|
||||||
this.damage = Number.MIN_VALUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
split(amount:number) {
|
|
||||||
this.stackSize -= amount;
|
|
||||||
return new ItemStack(this.itemID, amount, this.damage);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import { IInventory } from "../inventories/IInventory";
|
|
||||||
|
|
||||||
export class ContainerSlot {
|
|
||||||
public inventory:IInventory;
|
|
||||||
public index:number;
|
|
||||||
|
|
||||||
public constructor(inventory:IInventory, index:number) {
|
|
||||||
this.inventory = inventory;
|
|
||||||
this.index = index;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { World } from "../World";
|
import { World } from "../World";
|
||||||
import { ItemStack } from "../containers/ItemStack";
|
import { ItemStack } from "../inventories/ItemStack";
|
||||||
import { Entity } from "./Entity";
|
import { Entity } from "./Entity";
|
||||||
|
|
||||||
export class EntityItem extends Entity {
|
export class EntityItem extends Entity {
|
|
@ -6,6 +6,9 @@ import { PacketMapChunk } from "../packets/MapChunk";
|
||||||
import { EntityLiving } from "./EntityLiving";
|
import { EntityLiving } from "./EntityLiving";
|
||||||
import { PacketPreChunk } from "../packets/PreChunk";
|
import { PacketPreChunk } from "../packets/PreChunk";
|
||||||
import { PacketUpdateHealth } from "../packets/UpdateHealth";
|
import { PacketUpdateHealth } from "../packets/UpdateHealth";
|
||||||
|
import { Inventory } from "../inventories/Inventory";
|
||||||
|
import { ItemStack } from "../inventories/ItemStack";
|
||||||
|
import { Block } from "../blocks/Block";
|
||||||
|
|
||||||
const CHUNK_LOAD_RANGE = 5;
|
const CHUNK_LOAD_RANGE = 5;
|
||||||
|
|
||||||
|
@ -16,6 +19,7 @@ export class Player extends EntityLiving {
|
||||||
public loadedChunks:Array<number>;
|
public loadedChunks:Array<number>;
|
||||||
public justUnloaded:Array<number>;
|
public justUnloaded:Array<number>;
|
||||||
public mpClient?:MPClient;
|
public mpClient?:MPClient;
|
||||||
|
public inventory:Inventory;
|
||||||
|
|
||||||
private lastHealth:number;
|
private lastHealth:number;
|
||||||
|
|
||||||
|
@ -26,6 +30,12 @@ export class Player extends EntityLiving {
|
||||||
this.loadedChunks = new Array<number>();
|
this.loadedChunks = new Array<number>();
|
||||||
this.justUnloaded = new Array<number>();
|
this.justUnloaded = new Array<number>();
|
||||||
|
|
||||||
|
this.inventory = new Inventory(44, "Player Inventory");
|
||||||
|
|
||||||
|
this.inventory.setSlotItemStack(36, new ItemStack(Block.dirt, 1));
|
||||||
|
this.inventory.setSlotItemStack(37, new ItemStack(Block.dirt, 2));
|
||||||
|
this.inventory.setSlotItemStack(38, new ItemStack(Block.dirt, 3));
|
||||||
|
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.x = 8;
|
this.x = 8;
|
||||||
this.y = 64;
|
this.y = 64;
|
||||||
|
|
|
@ -36,5 +36,10 @@ export enum Packet {
|
||||||
EntityLookRelativeMove = 0x21,
|
EntityLookRelativeMove = 0x21,
|
||||||
EntityTeleport = 0x22,
|
EntityTeleport = 0x22,
|
||||||
|
|
||||||
|
CloseWindow = 0x65,
|
||||||
|
WindowClick = 0x66,
|
||||||
|
SetSlot = 0x67,
|
||||||
|
WindowItems = 0x68,
|
||||||
|
|
||||||
DisconnectKick = 0xff
|
DisconnectKick = 0xff
|
||||||
}
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
import { ItemStack } from "../containers/ItemStack";
|
import { ItemStack } from "./ItemStack";
|
||||||
|
|
||||||
export interface IInventory {
|
export default interface IInventory {
|
||||||
getInventoryName:() => string,
|
getInventoryName:() => string,
|
||||||
getInventorySize:() => number,
|
getInventorySize:() => number,
|
||||||
getSlotItemStack:(slotId:number) => ItemStack
|
getSlotItemStack:(slotId:number) => ItemStack | null
|
||||||
|
setSlotItemStack:(slotId:number, itemStack:ItemStack | null) => IInventory
|
||||||
}
|
}
|
67
server/inventories/Inventory.ts
Normal file
67
server/inventories/Inventory.ts
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
import { Endian, createWriter } from "bufferstuff";
|
||||||
|
import { ItemStack } from "./ItemStack";
|
||||||
|
import IInventory from "./IInventory";
|
||||||
|
|
||||||
|
export class Inventory implements IInventory {
|
||||||
|
private itemStacks:Array<ItemStack | null>;
|
||||||
|
|
||||||
|
private size:number;
|
||||||
|
private name:string;
|
||||||
|
|
||||||
|
public constructor(size:number, name:string) {
|
||||||
|
this.itemStacks = new Array<ItemStack | null>();
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
this.itemStacks.push(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.size = size;
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInventoryName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
getInventorySize() {
|
||||||
|
return this.itemStacks.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSlotItemStack(slotId:number) {
|
||||||
|
return this.itemStacks[slotId];
|
||||||
|
}
|
||||||
|
|
||||||
|
setSlotItemStack(slotId:number, itemStack: ItemStack | null) {
|
||||||
|
if (slotId < 0 || slotId > this.size - 1) {
|
||||||
|
throw new Error(`Tried to set an Inventory ItemStack out of bounds! Requested slot: ${slotId}, Inventory Size: ${this.size}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.itemStacks[slotId] = itemStack;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculateInventoryPayloadSize() {
|
||||||
|
let bufferSize = 0;
|
||||||
|
for (const stack of this.itemStacks) {
|
||||||
|
if (stack) {
|
||||||
|
bufferSize += 5; // short + byte + short
|
||||||
|
} else {
|
||||||
|
bufferSize += 2; // short
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bufferSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructInventoryPayload() {
|
||||||
|
const writer = createWriter(Endian.BE, this.calculateInventoryPayloadSize());
|
||||||
|
for (const stack of this.itemStacks) {
|
||||||
|
writer.writeShort(stack == null ? -1 : stack.itemID);
|
||||||
|
if (stack != null) {
|
||||||
|
writer.writeByte(stack.size);
|
||||||
|
writer.writeShort(stack.damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.toBuffer();
|
||||||
|
}
|
||||||
|
}
|
47
server/inventories/ItemStack.ts
Normal file
47
server/inventories/ItemStack.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import { Block } from "../blocks/Block";
|
||||||
|
import { Item } from "../items/Item";
|
||||||
|
|
||||||
|
export class ItemStack {
|
||||||
|
public readonly itemID:number;
|
||||||
|
public size:number;
|
||||||
|
public damage:number;
|
||||||
|
|
||||||
|
public constructor(blockOrItemOrItemID:Block|Item|number, size?:number, damage?:number) {
|
||||||
|
if (blockOrItemOrItemID instanceof Block && size === undefined && damage === undefined) {
|
||||||
|
this.itemID = blockOrItemOrItemID.blockId;
|
||||||
|
this.size = 1;
|
||||||
|
this.damage = 0;
|
||||||
|
} else if (blockOrItemOrItemID instanceof Block && typeof(size) === "number" && damage === undefined) {
|
||||||
|
this.itemID = blockOrItemOrItemID.blockId;
|
||||||
|
this.size = size;
|
||||||
|
this.damage = 0;
|
||||||
|
} else if (blockOrItemOrItemID instanceof Block && typeof(size) === "number" && typeof(damage) === "number") {
|
||||||
|
this.itemID = blockOrItemOrItemID.blockId;
|
||||||
|
this.size = size;
|
||||||
|
this.damage = damage;
|
||||||
|
} else if (blockOrItemOrItemID instanceof Item && size === undefined && damage === undefined) {
|
||||||
|
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
||||||
|
this.size = 1;
|
||||||
|
this.damage = 0;
|
||||||
|
} else if (blockOrItemOrItemID instanceof Item && typeof(size) === "number" && damage === undefined) {
|
||||||
|
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
||||||
|
this.size = size;
|
||||||
|
this.damage = 0;
|
||||||
|
} else if (blockOrItemOrItemID instanceof Item && typeof(size) === "number" && typeof(damage) === "number") {
|
||||||
|
this.itemID = blockOrItemOrItemID.shiftedItemID;
|
||||||
|
this.size = size;
|
||||||
|
this.damage = damage;
|
||||||
|
} else if (typeof(blockOrItemOrItemID) === "number" && typeof(size) === "number" && typeof(damage) === "number") {
|
||||||
|
this.itemID = blockOrItemOrItemID;
|
||||||
|
this.size = size;
|
||||||
|
this.damage = damage;
|
||||||
|
} else {
|
||||||
|
throw new Error(`ItemStack created with invalid properties (${typeof(blockOrItemOrItemID)}, ${typeof(size)}, ${typeof(damage)})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
split(amount:number) {
|
||||||
|
this.size -= amount;
|
||||||
|
return new ItemStack(this.itemID, amount, this.damage);
|
||||||
|
}
|
||||||
|
}
|
26
server/packets/HoldingChange.ts
Normal file
26
server/packets/HoldingChange.ts
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { createWriter, IReader, Endian } from "bufferstuff";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
import { Packet } from "../enums/Packet";
|
||||||
|
|
||||||
|
export class PacketHoldingChange implements IPacket {
|
||||||
|
public packetId = Packet.HoldingChange;
|
||||||
|
public slotId:number;
|
||||||
|
|
||||||
|
public constructor(slotId?:number) {
|
||||||
|
if (typeof(slotId) === "number") {
|
||||||
|
this.slotId = slotId;
|
||||||
|
} else {
|
||||||
|
this.slotId = Number.MIN_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:IReader) {
|
||||||
|
this.slotId = reader.readShort();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return createWriter(Endian.BE, 3).writeUByte(this.packetId).writeShort(this.slotId).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
70
server/packets/PlayerBlockPlacement.ts
Normal file
70
server/packets/PlayerBlockPlacement.ts
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { createWriter, IReader, Endian } from "bufferstuff";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
import { Packet } from "../enums/Packet";
|
||||||
|
|
||||||
|
export class PacketPlayerBlockPlacement implements IPacket {
|
||||||
|
public packetId = Packet.PlayerBlockPlacement;
|
||||||
|
public x:number;
|
||||||
|
public y:number;
|
||||||
|
public z:number;
|
||||||
|
public face:number;
|
||||||
|
public blockOrItemId:number;
|
||||||
|
public amount?:number;
|
||||||
|
public damage?:number;
|
||||||
|
|
||||||
|
public constructor(x?:number, y?:number, z?:number, face?:number, blockOrItemId?:number, amount?:number, damage?:number) {
|
||||||
|
if (typeof(x) === "number" && typeof(y) === "number" && typeof(z) === "number" && typeof(face) === "number" && typeof(blockOrItemId) === "number") {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.z = z;
|
||||||
|
this.face = face;
|
||||||
|
this.blockOrItemId = blockOrItemId;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.x = Number.MIN_VALUE;
|
||||||
|
this.y = Number.MIN_VALUE;
|
||||||
|
this.z = Number.MIN_VALUE;
|
||||||
|
this.face = Number.MIN_VALUE;
|
||||||
|
this.blockOrItemId = Number.MIN_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.amount = amount;
|
||||||
|
this.damage = damage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:IReader) {
|
||||||
|
this.x = reader.readInt();
|
||||||
|
this.y = reader.readByte();
|
||||||
|
this.z = reader.readInt();
|
||||||
|
this.face = reader.readByte();
|
||||||
|
this.blockOrItemId = reader.readShort();
|
||||||
|
if (this.blockOrItemId >= 0) {
|
||||||
|
this.amount = reader.readByte();
|
||||||
|
this.damage = reader.readShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private calculatePacketSize() {
|
||||||
|
return this.blockOrItemId >= 0 && this.amount != null && this.damage != null ? 16 : 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
const packetSize = this.calculatePacketSize();
|
||||||
|
|
||||||
|
const writer = createWriter(Endian.BE, packetSize)
|
||||||
|
.writeUByte(this.packetId)
|
||||||
|
.writeInt(this.x)
|
||||||
|
.writeByte(this.y)
|
||||||
|
.writeInt(this.z)
|
||||||
|
.writeByte(this.face)
|
||||||
|
.writeShort(this.blockOrItemId);
|
||||||
|
|
||||||
|
if (this.amount != null && this.damage != null) {
|
||||||
|
writer.writeByte(this.amount).writeShort(this.damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return writer.toBuffer();
|
||||||
|
}
|
||||||
|
}
|
32
server/packets/WindowItems.ts
Normal file
32
server/packets/WindowItems.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { createWriter, IReader, Endian } from "bufferstuff";
|
||||||
|
import { IPacket } from "./IPacket";
|
||||||
|
import { Packet } from "../enums/Packet";
|
||||||
|
|
||||||
|
export class PacketWindowItems implements IPacket {
|
||||||
|
public packetId = Packet.WindowItems;
|
||||||
|
public windowId:number;
|
||||||
|
public count:number;
|
||||||
|
public payload:Buffer;
|
||||||
|
|
||||||
|
public constructor(windowId?:number, count?:number, payload?:Buffer) {
|
||||||
|
if (typeof(windowId) === "number" && typeof(count) === "number" && payload instanceof Buffer) {
|
||||||
|
this.windowId = windowId;
|
||||||
|
this.count = count;
|
||||||
|
this.payload = payload;
|
||||||
|
} else {
|
||||||
|
this.windowId = Number.MIN_VALUE;
|
||||||
|
this.count = Number.MIN_VALUE;
|
||||||
|
this.payload = Buffer.alloc(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public readData(reader:IReader) {
|
||||||
|
reader.readByte();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public writeData() {
|
||||||
|
return createWriter(Endian.BE, 4).writeUByte(this.packetId).writeByte(this.windowId).writeShort(this.count).writeBuffer(this.payload).toBuffer();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue