diff --git a/src/catalyst/core/cmdparser.ts b/src/catalyst/core/cmdparser.ts index e52d251..e9605f1 100644 --- a/src/catalyst/core/cmdparser.ts +++ b/src/catalyst/core/cmdparser.ts @@ -11,6 +11,7 @@ import { commandToken, } from "../@types/commands"; import { CommandError } from "./command.js"; +import { vec2, vec3, degToRad } from "./math.js"; const typeParsers: Record = {}; @@ -443,6 +444,7 @@ export function formatHelp(cmd: commandSub): string[] { registerCommandTypeParser('string', (argv, argDef) => { return { value: argv[0]?.text }; }); + registerCommandTypeParser('int', (argv, argDef) => { if (!/^[+-]?(?:0|[1-9][0-9]*)$/.test(argv[0]?.text)) { const err = new CommandError('not a valid integer: ' + argv[0]?.text); @@ -451,6 +453,7 @@ registerCommandTypeParser('int', (argv, argDef) => { } return { value: +argv[0]?.text }; }); + registerCommandTypeParser('boolean', (argv, argDef) => { if (!/^(true|false)$/.test(argv[0]?.text)) { const err = new CommandError('not a valid boolean'); @@ -458,5 +461,117 @@ registerCommandTypeParser('boolean', (argv, argDef) => { throw err; } return { value: argv[0]?.text == 'true' }; -}) +}); + +registerCommandTypeParser('xyz', (argv, argDef) => { + let arg = argv[0]; + let str = arg.text; + let idx = 0; + let offst = 0; + + let x = 0, xRel = false, xRot = false; + let y = 0, yRel = false, yRot = false; + let z = 0, zRel = false, zRot = false; + + // get next arg + const nextArg = () => { + // check if there's still args + if (argv.length <= ++idx) { + const err = new CommandError('unexpected end of input'); + err.token = { + text: "", + start: Infinity, + end: Infinity, + quoted: false, + }; + throw err; + } + + // get next arg + arg = argv[idx]; + str = arg.text; + offst = 0; + }; + + // increment the string + const inc = (n: number = 1) => { + str = str.slice(n); + offst += n; + }; + + // get next number + const getNum = (): number => { + const regex = /^([+-]?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?)(?:[~^+-]|$)/g; + + // check if match + if (!regex.test(str)) { + if (offst > 0) return 0; + const err = new CommandError('invalid number', offst); + err.token = arg; + throw err; + } + + regex.lastIndex = 0; + const raw = regex.exec(str)[1]; + inc(raw.length); + return +raw; + }; + + // ^^^offset + if (str.startsWith('^^^')) { + xRot = yRot = zRot = true; + inc(3); + x = y = z = getNum(); + } + + // 0 ~1 ^2 + else { + // x-coord + if (str[0] == '~') { xRel = true; inc(); } + else if (str[0] == '^') { xRot = true; inc(); } + x = getNum(); + + // y-coord + if (str.length == 0) nextArg(); + if (str[0] == '~') { yRel = true; inc(); } + else if (str[0] == '^') { yRot = true; inc(); } + y = getNum(); + + // z-coord + if (str.length == 0) nextArg(); + if (str[0] == '~') { zRel = true; inc(); } + else if (str[0] == '^') { zRot = true; inc(); } + z = getNum(); + + // auto-adjust block location + if (!xRot && x % 1 == 0) x += 0.5; + if (!zRot && z % 1 == 0) z += 0.5; + } + + function handleFn(org: vec3, rot: vec2): vec3 { + let finalX = org.x; + let finalY = org.y; + let finalZ = org.z; + + // radians of thr rot + const pitch = degToRad(rot.x); + const yaw = degToRad(rot.y); + + // compute modifiers + if (xRel) finalX += x; + if (xRot) finalX += x * Math.cos(pitch) * -Math.sin(yaw); + if (yRel) finalY += y; + if (yRot) finalY += y * -Math.sin(pitch); + if (zRel) finalZ += z; + if (zRot) finalZ += z * Math.cos(pitch) * Math.cos(yaw); + + return { + x: finalX, + y: finalY, + z: finalZ, + }; + } + + return { value: handleFn, step: idx + 1 }; +}); diff --git a/src/catalyst/core/database.ts b/src/catalyst/core/database.ts index 6edc764..3ff0475 100644 --- a/src/catalyst/core/database.ts +++ b/src/catalyst/core/database.ts @@ -119,6 +119,14 @@ export class Database> { return delete this._cache[key]; } + /** + * get a list of keys in the db + * @returns list of strings + */ + public keys(): K[] { + return Object.keys(this._cache) as K[]; + } + /** * clears the cache, use Database.save() to take changes */ diff --git a/src/catalyst/core/math.ts b/src/catalyst/core/math.ts index dfbe320..c3c81e4 100644 --- a/src/catalyst/core/math.ts +++ b/src/catalyst/core/math.ts @@ -6,32 +6,32 @@ * vec2 object interface */ export interface vec2 { - /** - * x field of the vector - */ - x: number, - /** - * y field of the vector - */ - y: number, + /** + * x field of the vector + */ + x: number, + /** + * y field of the vector + */ + y: number, } /** * vec3 object interface */ export interface vec3 { - /** - * x field of the vector - */ - x: number, - /** - * y field of the vector - */ - y: number, - /** - * z field of the vector - */ - z: number, + /** + * x field of the vector + */ + x: number, + /** + * y field of the vector + */ + y: number, + /** + * z field of the vector + */ + z: number, } /** @@ -65,7 +65,7 @@ export const UP: vec3 = { x: 0, y: 1, z: 0 }; * @returns radians */ export function degToRad(x: number): number { - return x * Math.PI / 180; + return x * Math.PI / 180; } /** @@ -74,7 +74,7 @@ export function degToRad(x: number): number { * @returns degrees */ export function radToDeg(x: number): number { - return x * 180 / Math.PI; + return x * 180 / Math.PI; } /** * calculates the coordinates of a point along a ray, given a rotational angle @@ -85,14 +85,14 @@ export function radToDeg(x: number): number { * @returns the resulting coordinates as a vec3. */ export function computeRayCoords(offset: vec3, angle: vec2, dist: number): vec3 { - const pitch = degToRad(angle.x); - const yaw = degToRad(angle.y); + const pitch = degToRad(angle.x); + const yaw = degToRad(angle.y); - return { - x: offset.x + dist * -Math.cos(pitch) * Math.sin(yaw), - y: offset.y + dist * -Math.sin(pitch), - z: offset.z + dist * Math.cos(pitch) * Math.cos(yaw), - }; + return { + x: offset.x + dist * Math.cos(pitch) * -Math.sin(yaw), + y: offset.y + dist * -Math.sin(pitch), + z: offset.z + dist * Math.cos(pitch) * Math.cos(yaw), + }; } /** @@ -102,11 +102,11 @@ export function computeRayCoords(offset: vec3, angle: vec2, dist: number): vec3 * @returns the distance of point a from point b */ export function vdist(a: vec3, b: vec3): number { - return Math.sqrt( - (a.x - b.x) ** 2 + - (a.y - b.y) ** 2 + - (a.z - b.z) ** 2 - ); + return Math.sqrt( + (a.x - b.x) ** 2 + + (a.y - b.y) ** 2 + + (a.z - b.z) ** 2 + ); } /** @@ -115,11 +115,11 @@ export function vdist(a: vec3, b: vec3): number { * @returns new vec3 that is fully independent from the source */ export function vclone(v: vec3): vec3 { - return { - x: v.x, - y: v.y, - z: v.z, - }; + return { + x: v.x, + y: v.y, + z: v.z, + }; } /** @@ -129,11 +129,11 @@ export function vclone(v: vec3): vec3 { * @returns sum */ export function vadd(a: vec3, b: vec3): vec3 { - return { - x: a.x + b.x, - y: a.y + b.y, - z: a.z + b.z, - }; + return { + x: a.x + b.x, + y: a.y + b.y, + z: a.z + b.z, + }; } /** @@ -143,11 +143,11 @@ export function vadd(a: vec3, b: vec3): vec3 { * @returns difference */ export function vsub(a: vec3, b: vec3): vec3 { - return { - x: a.x - b.x, - y: a.y - b.y, - z: a.z - b.z, - }; + return { + x: a.x - b.x, + y: a.y - b.y, + z: a.z - b.z, + }; } /** @@ -157,11 +157,11 @@ export function vsub(a: vec3, b: vec3): vec3 { * @returns product */ export function vmul(a: vec3, b: vec3): vec3 { - return { - x: a.x * b.x, - y: a.y * b.y, - z: a.z * b.z, - }; + return { + x: a.x * b.x, + y: a.y * b.y, + z: a.z * b.z, + }; } /** @@ -171,11 +171,11 @@ export function vmul(a: vec3, b: vec3): vec3 { * @returns quotient */ export function vdiv(a: vec3, b: vec3): vec3 { - return { - x: a.x / b.x, - y: a.y / b.y, - z: a.z / b.z, - }; + return { + x: a.x / b.x, + y: a.y / b.y, + z: a.z / b.z, + }; } /** @@ -185,11 +185,11 @@ export function vdiv(a: vec3, b: vec3): vec3 { * @returns min coords */ export function vmin(a: vec3, b: vec3): vec3 { - return { - x: Math.min(a.x, b.x), - y: Math.min(a.y, b.y), - z: Math.min(a.z, b.z), - }; + return { + x: Math.min(a.x, b.x), + y: Math.min(a.y, b.y), + z: Math.min(a.z, b.z), + }; } /** @@ -199,11 +199,11 @@ export function vmin(a: vec3, b: vec3): vec3 { * @returns max coords */ export function vmax(a: vec3, b: vec3): vec3 { - return { - x: Math.max(a.x, b.x), - y: Math.max(a.y, b.y), - z: Math.max(a.z, b.z), - }; + return { + x: Math.max(a.x, b.x), + y: Math.max(a.y, b.y), + z: Math.max(a.z, b.z), + }; } /** @@ -212,11 +212,11 @@ export function vmax(a: vec3, b: vec3): vec3 { * @returns flooring of v */ export function vfloor(v: vec3): vec3 { - return { - x: Math.floor(v.x), - y: Math.floor(v.y), - z: Math.floor(v.z), - }; + return { + x: Math.floor(v.x), + y: Math.floor(v.y), + z: Math.floor(v.z), + }; } /** @@ -225,11 +225,11 @@ export function vfloor(v: vec3): vec3 { * @returns ceiling of v */ export function vceil(v: vec3): vec3 { - return { - x: Math.ceil(v.x), - y: Math.ceil(v.y), - z: Math.ceil(v.z), - }; + return { + x: Math.ceil(v.x), + y: Math.ceil(v.y), + z: Math.ceil(v.z), + }; } /** @@ -238,11 +238,11 @@ export function vceil(v: vec3): vec3 { * @returns rounded v */ export function vround(v: vec3): vec3 { - return { - x: Math.round(v.x), - y: Math.round(v.y), - z: Math.round(v.z), - }; + return { + x: Math.round(v.x), + y: Math.round(v.y), + z: Math.round(v.z), + }; } /** @@ -251,9 +251,10 @@ export function vround(v: vec3): vec3 { * @returns abs of v */ export function vabs(v: vec3): vec3 { - return { - x: Math.abs(v.x), - y: Math.abs(v.y), - z: Math.abs(v.z), - }; + return { + x: Math.abs(v.x), + y: Math.abs(v.y), + z: Math.abs(v.z), + }; } + diff --git a/src/server/client.ts b/src/server/client.ts index a6bdcb2..1113a98 100644 --- a/src/server/client.ts +++ b/src/server/client.ts @@ -7,6 +7,8 @@ import { formatNumber, getPlayerByName, setTickInterval, + vec2, + vec3, config } from "../catalyst/index.js"; import { smpName, validationInterval, combatTime, ranks } from "./index.js"; @@ -68,6 +70,16 @@ export class Client { * the player's name */ public get name(): string { return this.player.name; } + /** + * the player's location + */ + public get loc(): vec3 { return this.player.location; } + public set loc(v: vec3) { this.player.teleport(v); } + /** + * the player's head rotation + */ + public get rot(): vec2 { return this.player.getRotation(); } + public set rot(r: vec2) { this.player.setRotation(r); } // player's db values /** rank */ diff --git a/src/server/commands/index.ts b/src/server/commands/index.ts index daf9198..4928e63 100644 --- a/src/server/commands/index.ts +++ b/src/server/commands/index.ts @@ -50,7 +50,9 @@ import("./data.js"); import("./help.js"); import("./home.js"); import("./kit.js"); +import("./mloc.js"); import("./perm.js"); import("./shop.js"); +import("./spawn.js"); import("./warp.js"); diff --git a/src/server/commands/mloc.ts b/src/server/commands/mloc.ts new file mode 100644 index 0000000..12285fd --- /dev/null +++ b/src/server/commands/mloc.ts @@ -0,0 +1,71 @@ +import { makeCommand } from "./index.js"; +import { commandSub } from "../../catalyst/@types/commands"; +import { config, vec3 } from "../../catalyst/index.js"; +import { options } from "../index.js"; +import { assertIsAdmin } from "../utils.js"; + +const info: commandSub = { + name: "mloc", + dest: "", + help: "manage server locations", + subs: [ + { + name: "add", + dest: "add", + args: [ + { + name: "id", + dest: "id", + type: "string", + required: true, + }, + { + name: "pos", + dest: "pos", + type: "xyz", + } + ] + }, + { + name: "rm", + dest: "rm", + args: [ + { + name: "id", + dest: "id", + type: "string", + required: true, + } + ] + } + ] +}; + +makeCommand(info, (args, ev, plr) => { + assertIsAdmin(plr); + options.load(); + const locs = options.get('locs', {}); + + if (args.add) { + if (locs[args.id]) throw 'location is already set!'; + let xyz = args.pos ? args.pos(plr.loc, plr.rot) as vec3 : plr.loc; + locs[args.id] = xyz; + options.set('locs', locs); + options.save(); + plr.msg('§alocation set: §6' + args.id); + return; + } + + if (args.rm) { + if (!locs[args.id]) throw 'location not found!'; + locs[args.id] = undefined; + options.set('locs', locs); + options.save(); + plr.msg('§alocation removed: §6' + args.id); + return; + } + + plr.msg('§emanage server locations§r\n' + + '§etype §b' + config.commandPrefix + 'help mloc§r§e for more info'); +}); + diff --git a/src/server/commands/spawn.ts b/src/server/commands/spawn.ts new file mode 100644 index 0000000..a1a2909 --- /dev/null +++ b/src/server/commands/spawn.ts @@ -0,0 +1,30 @@ +import { makeCommand } from "./index.js"; +import { commandSub } from "../../catalyst/@types/commands"; +import { config, setTickTimeout } from "../../catalyst/index.js"; +import { options } from "../index.js"; +import { assertNotInCombat } from "../utils.js"; + +const info: commandSub = { + name: "spawn", + dest: "", + help: "tp to spawn", +}; + +makeCommand(info, (args, ev, plr) => { + assertNotInCombat(plr); + options.load(); + const locs = options.get('locs', {}); + + if (!locs.spawn) { + throw 'spawn location not found\n' + + 'if you\'re an admin, please set it using §b' + + config.commandPrefix + 'mloc add spawn [pos]'; + } + + // tp the player + setTickTimeout(() => { + plr.player.teleport(locs.spawn); + plr.msg('§ayou have been teleported to: §6spawn§r\n'); + }); +}); + diff --git a/src/server/commands/warp.ts b/src/server/commands/warp.ts index 178ada2..a62ce34 100644 --- a/src/server/commands/warp.ts +++ b/src/server/commands/warp.ts @@ -1,7 +1,7 @@ import { makeCommand } from "./index.js"; import { commandSub } from "../../catalyst/@types/commands"; import { setTickTimeout } from "../../catalyst/index.js"; -import { serverLocs } from "../index.js"; +import { options } from "../index.js"; import { assertNotInCombat } from "../utils.js"; const info: commandSub = { @@ -21,15 +21,17 @@ const info: commandSub = { makeCommand(info, (args, ev, plr) => { assertNotInCombat(plr); + options.load(); + const locs = options.get('locs', {}); // validate option - if (!serverLocs.has(args.loc)) + if (!locs[args.loc]) throw `unknown location '${args.loc}'\n` + - 'options: ' + [...serverLocs.keys()].join(', '); + 'options: ' + [...Object.keys(locs)].join(', '); // tp the player setTickTimeout(() => { - plr.player.teleport(serverLocs.get(args.loc)); + plr.player.teleport(locs[args.loc]); plr.msg(`§ayou have been teleported to: §6${args.loc}§r\n`); }); }); diff --git a/src/server/index.ts b/src/server/index.ts index 1478535..b3f2045 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,10 +1,18 @@ /** * SMP server code (sample) */ -import { Logger, vec3 } from "../catalyst/index.js"; +import { Database, Logger, vec3 } from "../catalyst/index.js"; +/** + * a logger + */ export const logger = new Logger(); +/** + * server config db + */ +export const options = new Database('options'); + /** * the name of smp */ @@ -30,13 +38,6 @@ export const controlPassword = "hello_world"; */ export const combatTime = 20 * 15; // 15 seconds -/** - * server locations (used in \warp) - */ -export const serverLocs: Map = new Map([ - ['spawn', { x: 0, y: 7, z: 0 }], -]); - /** * ranks */