format everything + fix plugin events
This commit is contained in:
parent
cf8772f0ed
commit
d40e90240c
|
@ -150,7 +150,6 @@ export class PacketDefinitions {
|
|||
await PacketDefinitions.levelFinish(world.size, player);
|
||||
|
||||
await PacketDefinitions.spawn(player, -1, player);
|
||||
|
||||
}
|
||||
static async levelFinish(size: Position, player: Player) {
|
||||
await player.writeToSocket(
|
||||
|
|
|
@ -28,7 +28,9 @@ export class Player {
|
|||
|
||||
// reassigns ID until finds available one
|
||||
// if we reach 255 players this will loop forever
|
||||
while(server.players.find(e => e.id == id)) id = Math.floor(Math.random() * 255);
|
||||
while (server.players.find((e) => e.id == id)) {
|
||||
id = Math.floor(Math.random() * 255);
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
}
|
||||
|
@ -63,8 +65,7 @@ export class Player {
|
|||
PacketDefinitions.sendPackets(this, world);
|
||||
|
||||
this.server.broadcastPacket(
|
||||
(e) =>
|
||||
PacketDefinitions.spawn(this, this.id, e),
|
||||
(e) => PacketDefinitions.spawn(this, this.id, e),
|
||||
this,
|
||||
);
|
||||
this.server.broadcastPacket(
|
||||
|
|
|
@ -139,9 +139,12 @@ export class Server {
|
|||
this.broadcast(`${player.username} has &cleft`);
|
||||
this.worlds.find((e) => e.name == player.world)!.save();
|
||||
|
||||
this.broadcastPacket((e) => PacketDefinitions.despawn(player.id, e), player);
|
||||
this.broadcastPacket(
|
||||
(e) => PacketDefinitions.despawn(player.id, e),
|
||||
player,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
async handlePacket(packet: PacketReader, connection: Deno.Conn) {
|
||||
const packetType = packet.readByte();
|
||||
if (packetType == 0x00) {
|
||||
|
@ -258,18 +261,25 @@ export class Server {
|
|||
|
||||
const world = this.worlds.find((e) => e.name == player.world)!;
|
||||
|
||||
const before = world.getBlock(position);
|
||||
let pluginAnswer: boolean[] = [];
|
||||
|
||||
this.worlds.find((e) => e.name == player.world)!.setBlock(position, id);
|
||||
for await (const [_k, v] of this.plugins) {
|
||||
pluginAnswer = pluginAnswer.concat(
|
||||
await v.plugin.emit("setblock", player, mode, id, position),
|
||||
);
|
||||
}
|
||||
|
||||
if (pluginAnswer.some((e) => e == true)) {
|
||||
PacketDefinitions.setBlock(position, world.getBlock(position), player);
|
||||
return;
|
||||
}
|
||||
|
||||
world.setBlock(position, id);
|
||||
|
||||
this.broadcastPacket(
|
||||
(e) => PacketDefinitions.setBlock(position, id, e),
|
||||
player,
|
||||
);
|
||||
|
||||
this.plugins.forEach((value) => { // TODO: Rework this to work with proper block disabling (not resetting bullshit)
|
||||
value.plugin.emit("setblock", player, mode, id, position, before);
|
||||
});
|
||||
}
|
||||
|
||||
if (packet.buffer.length - 1 >= packet.pos) { // TODO: This logic is wrong! Sometimes, TCP packets are still dropped. Need to rewrite this properly.
|
||||
|
@ -298,7 +308,6 @@ export class Server {
|
|||
} else {
|
||||
const packet = new PacketReader(buffer.subarray(0, count));
|
||||
this.handlePacket(packet, connection);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,8 +29,7 @@ export abstract class Plugin extends EventEmitter<{
|
|||
mode: number,
|
||||
id: number,
|
||||
position: Position,
|
||||
blockBefore: number,
|
||||
): void;
|
||||
): boolean;
|
||||
|
||||
stop(): void;
|
||||
}> {
|
||||
|
|
4
deps.ts
4
deps.ts
|
@ -5,7 +5,7 @@ import { S3 } from "https://aws-api.deno.dev/v0.3/services/s3.ts";
|
|||
|
||||
export * as log from "https://deno.land/std@0.136.0/log/mod.ts";
|
||||
export { crypto } from "https://deno.land/std@0.136.0/crypto/mod.ts";
|
||||
export { EventEmitter } from "https://deno.land/x/eventemitter@1.2.1/mod.ts";
|
||||
export { EventEmitter } from "./events.ts";
|
||||
import "https://deno.land/x/dotenv@v3.2.0/load.ts";
|
||||
export const toHexString = (bytes: Uint8Array) =>
|
||||
bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
||||
|
@ -23,5 +23,5 @@ export const config = {
|
|||
ops: Deno.env.get("OPS") ? JSON.parse(Deno.env.get("OPS")!) : [],
|
||||
port: +Deno.env.get("PORT")!,
|
||||
hash: Deno.env.get("HASH"),
|
||||
onlineMode: Deno.env.get("ONLINEMODE") == "true"
|
||||
onlineMode: Deno.env.get("ONLINEMODE") == "true",
|
||||
};
|
||||
|
|
231
events.ts
Normal file
231
events.ts
Normal file
|
@ -0,0 +1,231 @@
|
|||
// deno-lint-ignore-file
|
||||
|
||||
/** The callback type. */
|
||||
type Callback = (...args: any[]) => any | Promise<any>;
|
||||
|
||||
/** A listener type. */
|
||||
type Listener = Callback & { __once__?: true };
|
||||
|
||||
/** The name of an event. */
|
||||
type EventName = string | number;
|
||||
|
||||
type EventsType =
|
||||
& { [key: string]: Callback }
|
||||
& { [key: number]: Callback };
|
||||
|
||||
/**
|
||||
* The event emitter.
|
||||
*/
|
||||
export class EventEmitter<E extends EventsType = {}> {
|
||||
/**
|
||||
* This is where the events and listeners are stored.
|
||||
*/
|
||||
private _events_: Map<keyof E, Set<Listener>> = new Map();
|
||||
|
||||
/**
|
||||
* Listen for a typed event.
|
||||
* @param event The typed event name to listen for.
|
||||
* @param listener The typed listener function.
|
||||
*/
|
||||
public on<K extends keyof E>(event: K, listener: E[K]): this;
|
||||
|
||||
/**
|
||||
* Listen for an event.
|
||||
* @param event The event name to listen for.
|
||||
* @param listener The listener function.
|
||||
*/
|
||||
public on(event: EventName, listener: Callback): this {
|
||||
if (!this._events_.has(event)) this._events_.set(event, new Set());
|
||||
this._events_.get(event)!.add(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a typed event once.
|
||||
* @param event The typed event name to listen for.
|
||||
* @param listener The typed listener function.
|
||||
*/
|
||||
public once<K extends keyof E>(event: K, listener: E[K]): this;
|
||||
|
||||
/**
|
||||
* Listen for an event once.
|
||||
* @param event The event name to listen for.
|
||||
* @param listener The listener function.
|
||||
*/
|
||||
public once(event: EventName, listener: Callback): this {
|
||||
const l: Listener = listener;
|
||||
l.__once__ = true;
|
||||
return this.on(event, l as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific listener in the event emitter on a specific
|
||||
* typed event.
|
||||
* @param event The typed event name.
|
||||
* @param listener The typed event listener function.
|
||||
*/
|
||||
public off<K extends keyof E>(event: K, listener: E[K]): this;
|
||||
|
||||
/**
|
||||
* Remove all listeners on a specific typed event.
|
||||
* @param event The typed event name.
|
||||
*/
|
||||
public off<K extends keyof E>(event: K): this;
|
||||
|
||||
/**
|
||||
* Remove all events from the event listener.
|
||||
*/
|
||||
public off(): this;
|
||||
|
||||
/**
|
||||
* Remove a specific listener on a specific event if both `event`
|
||||
* and `listener` is defined, or remove all listeners on a
|
||||
* specific event if only `event` is defined, or lastly remove
|
||||
* all listeners on every event if `event` is not defined.
|
||||
* @param event The event name.
|
||||
* @param listener The event listener function.
|
||||
*/
|
||||
public off(event?: EventName, listener?: Callback): this {
|
||||
if (!event && listener) {
|
||||
throw new Error("Why is there a listener defined here?");
|
||||
} else if (!event && !listener) {
|
||||
this._events_.clear();
|
||||
} else if (event && !listener) {
|
||||
this._events_.delete(event);
|
||||
} else if (event && listener && this._events_.has(event)) {
|
||||
const _ = this._events_.get(event)!;
|
||||
_.delete(listener);
|
||||
if (_.size === 0) this._events_.delete(event);
|
||||
} else {
|
||||
throw new Error("Unknown action!");
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a typed event without waiting for each listener to
|
||||
* return.
|
||||
* @param event The typed event name to emit.
|
||||
* @param args The arguments to pass to the typed listeners.
|
||||
*/
|
||||
public emitSync<K extends keyof E>(event: K, ...args: Parameters<E[K]>): this;
|
||||
|
||||
/**
|
||||
* Emit an event without waiting for each listener to return.
|
||||
* @param event The event name to emit.
|
||||
* @param args The arguments to pass to the listeners.
|
||||
*/
|
||||
public emitSync(event: EventName, ...args: Parameters<Callback>): this {
|
||||
if (!this._events_.has(event)) return this;
|
||||
const _ = this._events_.get(event)!;
|
||||
for (let [, listener] of _.entries()) {
|
||||
const r = listener(...args);
|
||||
if (r instanceof Promise) r.catch(console.error);
|
||||
if (listener.__once__) {
|
||||
delete listener.__once__;
|
||||
_.delete(listener);
|
||||
}
|
||||
}
|
||||
if (_.size === 0) this._events_.delete(event);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit a typed event and wait for each typed listener to return.
|
||||
* @param event The typed event name to emit.
|
||||
* @param args The arguments to pass to the typed listeners.
|
||||
*/
|
||||
public async emit<K extends keyof E>(
|
||||
event: K,
|
||||
...args: Parameters<E[K]>
|
||||
): Promise<any[]>;
|
||||
|
||||
/**
|
||||
* Emit an event and wait for each listener to return.
|
||||
* @param event The event name to emit.
|
||||
* @param args The arguments to pass to the listeners.
|
||||
*/
|
||||
public async emit(
|
||||
event: EventName,
|
||||
...args: Parameters<Callback>
|
||||
): Promise<any[]> {
|
||||
let returns: any[] = [];
|
||||
|
||||
if (!this._events_.has(event)) return returns;
|
||||
const _ = this._events_.get(event)!;
|
||||
for (let [, listener] of _.entries()) {
|
||||
try {
|
||||
returns.push(await listener(...args));
|
||||
if (listener.__once__) {
|
||||
delete listener.__once__;
|
||||
_.delete(listener);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
if (_.size === 0) this._events_.delete(event);
|
||||
return returns;
|
||||
}
|
||||
|
||||
/**
|
||||
* The same as emitSync, but wait for each typed listener to
|
||||
* return before calling the next typed listener.
|
||||
* @param event The typed event name.
|
||||
* @param args The arguments to pass to the typed listeners.
|
||||
*/
|
||||
public queue<K extends keyof E>(event: K, ...args: Parameters<E[K]>): this;
|
||||
|
||||
/**
|
||||
* The same as emitSync, but wait for each listener to return
|
||||
* before calling the next listener.
|
||||
* @param event The event name.
|
||||
* @param args The arguments to pass to the listeners.
|
||||
*/
|
||||
public queue(event: EventName, ...args: Parameters<Callback>): this {
|
||||
(async () => await this.emit(event, ...args as any))().catch(console.error);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a typed event to be emitted and return the arguments.
|
||||
* @param event The typed event name to wait for.
|
||||
* @param timeout An optional amount of milliseconds to wait
|
||||
* before throwing.
|
||||
*/
|
||||
public pull<K extends keyof E>(
|
||||
event: K,
|
||||
timeout?: number,
|
||||
): Promise<Parameters<E[K]>>;
|
||||
/**
|
||||
* Wait for an event to be emitted and return the arguments.
|
||||
* @param event The event name to wait for.
|
||||
* @param timeout An optional amount of milliseconds to wait
|
||||
* before throwing.
|
||||
*/
|
||||
public pull(
|
||||
event: EventName,
|
||||
timeout?: number,
|
||||
): Promise<Parameters<Callback>> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
let timeoutId: number | null;
|
||||
|
||||
let listener = (...args: any[]) => {
|
||||
if (timeoutId !== null) clearTimeout(timeoutId);
|
||||
resolve(args);
|
||||
};
|
||||
|
||||
timeoutId = typeof timeout !== "number"
|
||||
? null
|
||||
: setTimeout(() => (this.off(event, listener as any),
|
||||
reject(
|
||||
new Error("Timed out!"),
|
||||
))
|
||||
);
|
||||
|
||||
this.once(event, listener as any);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default EventEmitter;
|
|
@ -13,7 +13,7 @@ export default class CommandPlugin extends Plugin {
|
|||
super();
|
||||
|
||||
this.server = server;
|
||||
|
||||
|
||||
this.on("command", async (command, player) => {
|
||||
if (command == "help") {
|
||||
let allComamnds = "";
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { PacketDefinitions, Plugin, World } from "../classes/classes.ts";
|
||||
import { Plugin, World } from "../classes/classes.ts";
|
||||
import { Server } from "../classes/Server.ts";
|
||||
import { config } from "../deps.ts";
|
||||
|
||||
|
@ -13,20 +13,16 @@ export default class CommandPlugin extends Plugin {
|
|||
super();
|
||||
|
||||
this.server = server;
|
||||
this.on("setblock", (player, _mode, _id, position, blockBefore) => {
|
||||
this.on("setblock", (player, _mode, _id) => {
|
||||
const world = server.worlds.find((e) => e.name == player.world)!;
|
||||
if (!world.optionalJson?.builders?.includes("*")) {
|
||||
if (!world.optionalJson?.builders?.includes(player.username)) {
|
||||
player.message("You are %cnot allowed &fto build in this world!");
|
||||
world.setBlock(position, blockBefore);
|
||||
|
||||
server.players.forEach(async (e) => {
|
||||
if (e.world == player.world) {
|
||||
await PacketDefinitions.setBlock(position, blockBefore, e);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
this.on("command", async (command, player, args) => {
|
||||
if (command == "g") {
|
||||
|
|
28
readme.md
28
readme.md
|
@ -1,8 +1,9 @@
|
|||
# cla66ic
|
||||
|
||||
## sucessor of cla55ic
|
||||
## sucessor of cla55ic
|
||||
|
||||
### features:
|
||||
|
||||
1. written in typescript (types and shit)
|
||||
2. entierly cloud based (meaning you can host it anywhere)
|
||||
3. extremely extensive plugin system
|
||||
|
@ -10,8 +11,10 @@
|
|||
5. DENO!! It's not node, and a classic server.
|
||||
|
||||
### setup tutorial (be warned it's not the easiest)
|
||||
|
||||
1. make a backblaze b2 account, make a bucket, and get your keys from the bucket
|
||||
2. configure .env file to look something like
|
||||
|
||||
```
|
||||
PORT=6969
|
||||
HASH=RandomHashIlIke
|
||||
|
@ -21,19 +24,24 @@ ONLINEMODE=true
|
|||
S3_ACCESS_KEY_ID="MyAccessKey"
|
||||
S3_SECRET_KEY="SecretKey"
|
||||
```
|
||||
NOTE: if you are running inside of a cloud provider, just set these as
|
||||
your environment variables
|
||||
|
||||
3. install deno
|
||||
NOTE: if you are running inside of a cloud provider, just set these as your
|
||||
environment variables
|
||||
|
||||
3. install deno
|
||||
4. run `deno run --allow-env --allow-net --allow-read index.ts`
|
||||
|
||||
### insipration taken from:
|
||||
|
||||
1. mcgalaxy (obviuouuusly!!)
|
||||
2. https://github.com/Patbox/Cobblestone-Classic (some protocol information and worldhandling)
|
||||
2. https://github.com/Patbox/Cobblestone-Classic (some protocol information and
|
||||
worldhandling)
|
||||
3. cla55ic (world data too)
|
||||
|
||||
### issues:
|
||||
1. plugin system event handling is lackluster in some palces
|
||||
2. tcp packet splitting fails sometimes
|
||||
3. no cpe support! i want to get all of the above issues fixed before implementing CPE support
|
||||
4. proper rank support (implemented as plugin)
|
||||
5. no discord bridge (implemented as plugin)
|
||||
|
||||
1. tcp packet splitting fails sometimes
|
||||
2. no cpe support! i want to get all of the above issues fixed before
|
||||
implementing CPE support
|
||||
3. proper rank support (implemented as plugin)
|
||||
4. no discord bridge (implemented as plugin)
|
||||
|
|
Loading…
Reference in a new issue