switch to Bun
This commit is contained in:
parent
ef586df9a6
commit
7159558e5e
13 changed files with 146 additions and 159 deletions
|
|
@ -108,7 +108,6 @@ export class PacketWriter {
|
|||
}
|
||||
}
|
||||
|
||||
import { gzip } from "https://cdn.skypack.dev/pako";
|
||||
import { Position, World } from "./classes.ts";
|
||||
import { Player } from "./Player.ts";
|
||||
|
||||
|
|
@ -137,7 +136,7 @@ export class PacketDefinitions {
|
|||
|
||||
player.position = world.getSpawn();
|
||||
|
||||
const compressedMap = gzip(world.data)!;
|
||||
const compressedMap = Bun.gzipSync(world.data)!;
|
||||
|
||||
for (let i = 0; i < compressedMap.length; i += 1024) {
|
||||
const chunk = compressedMap.slice(
|
||||
|
|
|
|||
|
|
@ -3,8 +3,10 @@ import { PacketDefinitions, PacketWriter } from "./Packets.ts";
|
|||
import { config, log } from "../deps.ts";
|
||||
import { Server } from "./Server.ts";
|
||||
|
||||
import {Socket} from 'bun';
|
||||
|
||||
export class Player {
|
||||
socket: Deno.Conn;
|
||||
socket: Socket<{dataBuffer?: Buffer}>;
|
||||
private server: Server;
|
||||
|
||||
username: string;
|
||||
|
|
@ -15,7 +17,7 @@ export class Player {
|
|||
rotation: Rotation = { yaw: 0, pitch: 0 };
|
||||
|
||||
constructor(
|
||||
socket: Deno.Conn,
|
||||
socket: Socket<{dataBuffer?: Buffer}>,
|
||||
username: string,
|
||||
position: Position,
|
||||
server: Server,
|
||||
|
|
@ -24,7 +26,7 @@ export class Player {
|
|||
this.username = username;
|
||||
this.position = position;
|
||||
this.server = server;
|
||||
this.ip = (this.socket.remoteAddr as Deno.NetAddr).hostname;
|
||||
this.ip = this.socket.remoteAddress;
|
||||
|
||||
let id = Math.floor(Math.random() * server.maxUsers);
|
||||
|
||||
|
|
@ -37,13 +39,15 @@ export class Player {
|
|||
}
|
||||
|
||||
async writeToSocket(ar: Uint8Array) {
|
||||
await this.socket.write(ar).catch(async (e) => {
|
||||
try {
|
||||
this.socket.write(ar)
|
||||
} catch(e) {
|
||||
log.critical(e);
|
||||
await this.server.removeUser(
|
||||
this.socket,
|
||||
"Write failed" + e.message.split("\n")[0],
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
message(text: string, id = 0) {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import {
|
|||
Plugin,
|
||||
World,
|
||||
} from "./classes.ts";
|
||||
import { Socket, TCPSocketListener } from "bun"
|
||||
|
||||
import { config, crypto, log, toHexString } from "../deps.ts";
|
||||
import { config, log } from "../deps.ts";
|
||||
import * as fs from "node:fs/promises"
|
||||
|
||||
type PlayerFunction = (a: Player) => void;
|
||||
|
||||
|
|
@ -16,7 +18,7 @@ interface PluginUpdateTime {
|
|||
}
|
||||
|
||||
export class Server {
|
||||
server!: Deno.Listener;
|
||||
server!: TCPSocketListener;
|
||||
|
||||
players: Player[] = [];
|
||||
plugins: Map<string, PluginUpdateTime> = new Map();
|
||||
|
|
@ -33,17 +35,54 @@ export class Server {
|
|||
worlds: World[] = [new World({ x: 64, y: 64, z: 64 }, config.main)];
|
||||
|
||||
async start(port: number) {
|
||||
this.server = Deno.listen({ port: port });
|
||||
this.server = Bun.listen<{dataBuffer?: Buffer}>({
|
||||
hostname: "localhost",
|
||||
port: +process.env.PORT!,
|
||||
socket: {
|
||||
data: (socket, data) => {
|
||||
if(socket.data.dataBuffer) {
|
||||
const newBuffer = Buffer.concat([socket.data.dataBuffer, data]);
|
||||
socket.data.dataBuffer = newBuffer;
|
||||
} else {
|
||||
socket.data.dataBuffer = data;
|
||||
}
|
||||
|
||||
const parseBuffer = () => {
|
||||
if(!socket.data.dataBuffer) return;
|
||||
|
||||
const packetId = socket.data.dataBuffer.readUint8(0);
|
||||
const packetLength = this.lengthMap.get(packetId);
|
||||
if(!packetLength) {
|
||||
return;
|
||||
};
|
||||
if(socket.data.dataBuffer.byteLength < packetLength) return;
|
||||
|
||||
this.handlePacket(socket.data.dataBuffer.copyWithin(0, 1), packetId, socket);
|
||||
socket.data.dataBuffer = socket.data.dataBuffer.subarray(packetLength+1);
|
||||
parseBuffer();
|
||||
}
|
||||
|
||||
parseBuffer();
|
||||
},
|
||||
open: (socket) => {
|
||||
socket.data = {}
|
||||
},
|
||||
close(socket) {},
|
||||
drain(socket) {},
|
||||
error(socket, error) {},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
await Deno.stat("worlds/");
|
||||
for await (const dirEntry of Deno.readDir("worlds/")) {
|
||||
await fs.stat("worlds/");
|
||||
for await (const dirEntry of await fs.readdir("worlds/", {withFileTypes: true})) {
|
||||
const world = new World({ x: 0, y: 0, z: 0 }, dirEntry.name.replace(".buf", ""));
|
||||
|
||||
this.worlds.push(world);
|
||||
}
|
||||
} catch {
|
||||
await Deno.mkdir("worlds")
|
||||
await fs.mkdir("worlds")
|
||||
}
|
||||
|
||||
if (config.onlineMode) {
|
||||
|
|
@ -65,9 +104,6 @@ export class Server {
|
|||
|
||||
log.info(`Listening on port ${config.port}!`);
|
||||
|
||||
for await (const socket of this.server) {
|
||||
this.startSocket(socket);
|
||||
}
|
||||
}
|
||||
|
||||
broadcast(text: string) {
|
||||
|
|
@ -89,13 +125,13 @@ export class Server {
|
|||
}
|
||||
|
||||
async updatePlugins() {
|
||||
for await (const file of Deno.readDir("./plugins")) {
|
||||
if (file.isFile) {
|
||||
for await (const file of await fs.readdir("./plugins", {withFileTypes:true})) {
|
||||
if (file.isFile()) {
|
||||
const name = file.name.split(".ts")[0];
|
||||
|
||||
if (!this.plugins.has(name)) {
|
||||
this.plugins.set(name, {
|
||||
lastUpdated: Deno.statSync(`./plugins/${file.name}`).mtime!,
|
||||
lastUpdated: (await fs.stat(`./plugins/${file.name}`)).mtime!,
|
||||
plugin: new ((await import(`../plugins/${file.name}`)).default)(
|
||||
this,
|
||||
),
|
||||
|
|
@ -104,12 +140,12 @@ export class Server {
|
|||
const plugin = this.plugins.get(name);
|
||||
|
||||
if (
|
||||
Deno.statSync(`./plugins/${file.name}`).mtime!.getTime() !==
|
||||
(await fs.stat(`./plugins/${file.name}`)).mtime!.getTime() !==
|
||||
plugin?.lastUpdated.getTime()
|
||||
) {
|
||||
plugin?.plugin.emit("stop");
|
||||
this.plugins.set(name, {
|
||||
lastUpdated: Deno.statSync(`./plugins/${file.name}`).mtime!,
|
||||
lastUpdated: (await fs.stat(`./plugins/${file.name}`)).mtime!,
|
||||
plugin:
|
||||
new ((await import(`../plugins/${file.name}#` + Math.random()))
|
||||
.default)(
|
||||
|
|
@ -121,7 +157,7 @@ export class Server {
|
|||
}
|
||||
}
|
||||
}
|
||||
async removeUser(conn: Deno.Conn, text: string) {
|
||||
async removeUser(conn: Socket<{dataBuffer?: Buffer}>, text: string) {
|
||||
const player = this.players.find((e) => e.socket == conn);
|
||||
|
||||
if (!player) return;
|
||||
|
|
@ -129,7 +165,7 @@ export class Server {
|
|||
this.players = this.players.filter((e) => e != player);
|
||||
|
||||
try {
|
||||
conn.close();
|
||||
conn.end();
|
||||
} catch {
|
||||
// whatever
|
||||
}
|
||||
|
|
@ -147,7 +183,7 @@ export class Server {
|
|||
async handlePacket(
|
||||
buffer: Uint8Array,
|
||||
packetType: number,
|
||||
connection: Deno.Conn,
|
||||
connection: Socket<{dataBuffer?: Buffer}>,
|
||||
) {
|
||||
const packet = new PacketReader(buffer);
|
||||
if (packetType == 0x00) {
|
||||
|
|
@ -158,7 +194,7 @@ export class Server {
|
|||
const verification = packet.readString();
|
||||
|
||||
if (this.players.length >= this.maxUsers) {
|
||||
connection.close();
|
||||
connection.end()
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
@ -170,31 +206,25 @@ export class Server {
|
|||
);
|
||||
|
||||
if (!verification) {
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
return true;
|
||||
}
|
||||
const hasher = new Bun.CryptoHasher("md5");
|
||||
hasher.update(config.hash + player.username);
|
||||
|
||||
const str = toHexString(
|
||||
new Uint8Array(
|
||||
await crypto.subtle.digest(
|
||||
"MD5",
|
||||
new TextEncoder().encode(config.hash + player.username),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (
|
||||
config.onlineMode && verification != config.hash &&
|
||||
!this.players.find((e) => e.socket == connection)
|
||||
) {
|
||||
if (
|
||||
str !== verification
|
||||
hasher.digest("hex") !== verification
|
||||
) {
|
||||
await PacketDefinitions.disconnect(
|
||||
"Refresh your playerlist! Incorrect hash!",
|
||||
player,
|
||||
);
|
||||
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -205,7 +235,7 @@ export class Server {
|
|||
"Your name is already being used!",
|
||||
player,
|
||||
);
|
||||
player.socket.close();
|
||||
player.socket.end();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -295,63 +325,4 @@ export class Server {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
async startSocket(connection: Deno.Conn) {
|
||||
while (true) {
|
||||
const packetID = new Uint8Array(1);
|
||||
let packetIDReadAttempt;
|
||||
|
||||
try {
|
||||
packetIDReadAttempt = await connection.read(packetID);
|
||||
} catch {
|
||||
await this.removeUser(connection, "Packet ID read failed");
|
||||
break;
|
||||
}
|
||||
|
||||
if (packetIDReadAttempt) {
|
||||
const packetLength = this.lengthMap.get(packetID[0]);
|
||||
|
||||
if (!packetLength) {
|
||||
log.critical("Unknown Packet: " + packetID[0]);
|
||||
await this.removeUser(connection, "Unknown packet ID " + packetID[0]); // TODO: add a reason to this
|
||||
break;
|
||||
}
|
||||
|
||||
let rawPacket = new Uint8Array(packetLength);
|
||||
let packetReadAttempt;
|
||||
|
||||
try {
|
||||
packetReadAttempt = await connection.read(rawPacket);
|
||||
} catch {
|
||||
await this.removeUser(connection, "Packet read attempt failed."); // TODO: add a reason to this
|
||||
break;
|
||||
}
|
||||
|
||||
let fullRead = packetReadAttempt!;
|
||||
|
||||
while (fullRead < packetLength) {
|
||||
const halfPacket = new Uint8Array(packetLength - fullRead);
|
||||
rawPacket = new Uint8Array([...rawPacket, ...halfPacket]);
|
||||
|
||||
try {
|
||||
fullRead += (await connection.read(halfPacket))!;
|
||||
} catch {
|
||||
await this.removeUser(
|
||||
connection,
|
||||
"Couldn't read all of packet " + packetID[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.handlePacket(rawPacket, packetID[0], connection);
|
||||
} else {
|
||||
await this.removeUser(
|
||||
connection,
|
||||
"Packet ID read returned null. Packet " + packetID[0],
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { gzip, ungzip } from "https://cdn.skypack.dev/pako";
|
||||
import { decode, encode } from "cbor-x";
|
||||
import { Position } from "./classes.ts";
|
||||
import { cbor } from "../deps.ts";
|
||||
|
||||
import { unlink, readFile, writeFile} from "node:fs/promises";
|
||||
|
||||
export class World {
|
||||
size: Position;
|
||||
data: Uint8Array;
|
||||
|
|
@ -32,7 +34,7 @@ export class World {
|
|||
}
|
||||
|
||||
findID(block: number): Position[] {
|
||||
const position = [];
|
||||
const position: Position[] = [];
|
||||
for (let z = 0; z < this.size.z; z++) {
|
||||
for (let y = 0; y < this.size.y; y++) {
|
||||
for (let x = 0; x < this.size.x; x++) {
|
||||
|
|
@ -71,7 +73,7 @@ export class World {
|
|||
|
||||
async delete() {
|
||||
try {
|
||||
await Deno.remove(`worlds/${this.name}.buf`)
|
||||
await unlink(`worlds/${this.name}.buf`)
|
||||
} catch {
|
||||
// gang
|
||||
}
|
||||
|
|
@ -79,15 +81,15 @@ export class World {
|
|||
|
||||
private async load() {
|
||||
try {
|
||||
const ungziped = ungzip(
|
||||
await Deno.readFile(`worlds/${this.name}.buf`)
|
||||
const ungziped = Bun.gunzipSync(
|
||||
await readFile(`worlds/${this.name}.buf`)
|
||||
);
|
||||
|
||||
if (!(ungziped instanceof Uint8Array)) return;
|
||||
const dv = new DataView(ungziped.buffer);
|
||||
|
||||
const cborSize = dv.getUint32(0);
|
||||
this.metadata = cbor.decode(new Uint8Array(ungziped.buffer.slice(4, cborSize+4)));
|
||||
this.metadata = decode(new Uint8Array(ungziped.buffer.slice(4, cborSize+4)));
|
||||
|
||||
this.size = {
|
||||
x: this.metadata.x!,
|
||||
|
|
@ -117,12 +119,12 @@ export class World {
|
|||
z: this.size.z!,
|
||||
...this.metadata
|
||||
}
|
||||
const cborData = cbor.encode(metadata);
|
||||
const cborData = encode(metadata);
|
||||
const buffer = new Uint8Array(4 + cborData.byteLength + this.data.byteLength);
|
||||
const dv = new DataView(buffer.buffer);
|
||||
dv.setUint32(0, cborData.byteLength);
|
||||
buffer.set(cborData, 4);
|
||||
buffer.set(this.data, 4 + cborData.byteLength);
|
||||
await Deno.writeFile(`worlds/${this.name}.buf`, gzip(buffer)!);
|
||||
await writeFile(`worlds/${this.name}.buf`, Bun.gzipSync(buffer)!);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { EventEmitter } from "../deps.ts";
|
||||
import EventEmitter from "./../events";
|
||||
import { Player } from "./Player.ts";
|
||||
import { Server } from "./Server.ts";
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue