}
if(msg.startsWith('/server') && req.player.permissions.includes('admin')) {
+ const sender = io.sockets.sockets.get(cache.get(`socket:${req.player.id}`));
try {
- const output = await handleChatCommands(msg, req.player, io);
- if(output) {
- io.to(cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(output));
- }
+ await handleChatCommands(msg, req.player, io, sender);
}
catch(e) {
- io.to(cache.get(`socket:${req.player.id}`)).emit('chat', renderChatMessage(broadcastMessage('server', e.message)));
+ sender.emit('chat', renderChatMessage(broadcastMessage('server', e.message)));
}
}
else if(msg === '/online') {
-import { Server } from 'socket.io';
-import { maxHp, maxVigor, Player } from '../shared/player';
-import { createMonsters } from '../../seeds/monsters';
-import { createAllCitiesAndLocations } from '../../seeds/cities';
-import { createShopItems, createShopEquipment } from '../../seeds/shop_items';
-import { broadcastMessage, Message } from '../shared/message';
-import { updatePlayer } from './player';
+import { Server, Socket } from 'socket.io';
+import { Player } from '../shared/player';
+import { broadcastMessage } from '../shared/message';
+import { renderChatMessage } from './views/chat';
+import { Commands } from './chat-commands/';
-export async function handleChatCommands(msg: string, player: Player, io: Server): Promise<Message> {
- let message: Message;
- if(msg === '/server refresh-monsters') {
- await createMonsters();
- message = broadcastMessage('server', 'Monster refresh!');
- }
- else if(msg === '/server refresh-cities') {
- await createAllCitiesAndLocations();
- message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!');
- }
- else if(msg === '/server refresh-shops') {
- await createShopItems();
- await createShopEquipment();
- message = broadcastMessage('server', 'Refresh shop items');
- }
- else if(msg.startsWith('/server set-level')) {
- const level = parseInt(msg.split(' ').pop());
- if(level < 1) {
- message = broadcastMessage('server', 'Needs to be at least level 1');
- }
- else {
- message = broadcastMessage('server', `Set player level: ${level}`);
- player.level = level;
- player.strength = 4;
- player.constitution = 4;
- player.dexterity = 4;
- player.intelligence = 4;
- player.hp = maxHp(player.constitution, player.level);
- player.vigor = maxVigor(player.constitution, player.level);
- player.stat_points = level-1;
+export async function handleChatCommands(msg: string, player: Player, io: Server, sender: Socket): Promise<void> {
+ const rawCommand = msg.split('/server ')[1];
- await updatePlayer(player);
+ let matched = false;
+ Commands.forEach(async command => {
+ if(command.regex.test(rawCommand)) {
+ matched = true;
+ console.log(`${player.username} running command: [${rawCommand}]`);
+ await command.handler(rawCommand, sender, player, io);
}
- }
- else {
- const str = msg.split('/server ')[1];
- if(str) {
- message = broadcastMessage('server', str);
- }
- }
+ });
- return message;
+ if(!matched) {
+ const message = broadcastMessage('server', `Invalid command: [${rawCommand}]`);
+ sender.emit('chat', renderChatMessage(message));
+ }
}
--- /dev/null
+import type { Player } from '../../shared/player';
+import type { Server, Socket } from 'socket.io';
+
+type ChatCommandHandler = (command: string, sender: Socket, player: Player, io: Server) => Promise<void>;
+
+export class ChatCommand {
+ constructor(
+ public name: string,
+ public regex: RegExp,
+ public handler: ChatCommandHandler
+ ) {
+
+ }
+}
--- /dev/null
+import { ChatCommand } from "./base";
+import { refreshMonsters } from "./refresh-monsters";
+import { refreshCities } from './refresh-cities';
+import { refreshShops } from './refresh-shops';
+import { setLevel } from './set-level';
+import { say } from './say';
+
+export const Commands = new Set<ChatCommand>();
+
+Commands.add(refreshMonsters);
+Commands.add(refreshCities);
+Commands.add(refreshShops);
+Commands.add(setLevel);
+Commands.add(say);
--- /dev/null
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createAllCitiesAndLocations } from '../../../seeds/cities';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+ if(player.permissions.includes('admin')) {
+ await createAllCitiesAndLocations();
+ const message = broadcastMessage('server', 'Cities, Locations, and Paths refreshed!');
+ sender.emit('chat', renderChatMessage(message));
+ }
+}
+
+export const refreshCities = new ChatCommand('refresh-cities', new RegExp(/^refresh-cities/), handler);
--- /dev/null
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createMonsters } from '../../../seeds/monsters';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+ if(player.permissions.includes('admin')) {
+ await createMonsters();
+ const message = broadcastMessage('server', 'Monsters refreshed!');
+ sender.emit('chat', renderChatMessage(message));
+ }
+}
+
+export const refreshMonsters = new ChatCommand('refresh-monsters', new RegExp(/^refresh-monsters$/), handler);
--- /dev/null
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { createShopItems, createShopEquipment } from '../../../seeds/shop_items';
+import { broadcastMessage } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+ if(player.permissions.includes('admin')) {
+ await createShopItems();
+ await createShopEquipment();
+ const message = broadcastMessage('server', 'Shop items refreshed!');
+ sender.emit('chat', renderChatMessage(message));
+
+ }
+}
+
+export const refreshShops = new ChatCommand('refresh-shops', new RegExp(/^refresh-shops/), handler);
--- /dev/null
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { broadcastMessage, Message } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+ if(player.permissions.includes('moderator') || player.permissions.includes('admin')) {
+ let message: Message = broadcastMessage('server', rawCommand.split('say ')[1].trim());
+ sender.emit('chat', renderChatMessage(message));
+ }
+}
+
+export const say = new ChatCommand('say', new RegExp(/^say (.*)+/), handler);
--- /dev/null
+import { ChatCommand } from './base';
+import { Socket } from 'socket.io';
+import { broadcastMessage, Message } from '../../shared/message';
+import { renderChatMessage } from './../views/chat';
+import { updatePlayer } from '../player';
+import { maxHp, maxVigor, Player } from '../../shared/player';
+
+async function handler(rawCommand: string, sender: Socket, player: Player) {
+ if(player.permissions.includes('admin') || player.permissions.includes('tester')) {
+ let message: Message;
+ // command in set-level username level
+ const pieces = rawCommand.split(' ');
+ if(pieces.length !== 2) {
+ message = broadcastMessage('server', 'format: /set-level level');
+ }
+ else {
+ const level = parseInt(pieces.pop() || '0');
+ if(level < 1) {
+ message = broadcastMessage('server', 'format: /set-level [level >= 1]');
+ }
+ else {
+ message = broadcastMessage('server', `Set to level ${level}. Please reload.`);
+
+ player.level = level;
+ player.strength = 4;
+ player.constitution = 4;
+ player.dexterity = 4;
+ player.intelligence = 4;
+ player.hp = maxHp(player.constitution, player.level);
+ player.vigor = maxVigor(player.constitution, player.level);
+ player.stat_points = level-1;
+
+ await updatePlayer(player);
+ }
+ }
+
+ sender.emit('chat', renderChatMessage(message));
+
+ }
+}
+
+export const setLevel = new ChatCommand('set-level', new RegExp(/^set-level \d+$/), handler);
return res;
}
+export async function findPlayerByUsername(username: string): Promise<Player> {
+ const res = await db.first()
+ .select(
+ 'players.*',
+ 'profession_levels.level',
+ 'profession_levels.exp',
+ db.raw(`coalesce(pp.permissions, '[]'::json) as permissions`)
+ )
+ .from<Player>('players')
+ .join('profession_levels', function() {
+ this.on(function() {
+ this.on('profession_levels.player_id', '=', 'players.id')
+ this.andOn('profession_levels.profession', '=', 'players.profession')
+ })
+ })
+ .leftJoin(
+ db.raw(`(select json_agg(pp.permission) as permissions, pp.player_id from player_permissions pp group by pp.player_id) pp`),
+ 'pp.player_id','=', 'players.id'
+ )
+ .where({
+ 'players.username': username
+ });
+
+ return res;
+}
+
export async function createPlayer(): Promise<Player> {
const raw: Partial<Player> = {
username: `Player${Date.now().toString().substr(-7)}`,
import { SkillDefinition, Skill } from './skills';
import { EquippedItemDetails } from './equipped';
-export type Permission = 'admin' | 'moderator';
+export type Permission = 'admin' | 'moderator' | 'tester';
export type Player = {
id: string,