switch to Bun

This commit is contained in:
Soph :3 2024-04-28 18:33:00 +03:00
parent ef586df9a6
commit 7159558e5e
Signed by: sophie
GPG key ID: EDA5D222A0C270F2
13 changed files with 146 additions and 159 deletions

View file

@ -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(

View file

@ -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) {

View file

@ -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;
}
}
}
}

View file

@ -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)!);
}
}

View file

@ -1,4 +1,4 @@
import { EventEmitter } from "../deps.ts";
import EventEmitter from "./../events";
import { Player } from "./Player.ts";
import { Server } from "./Server.ts";