mirror of
https://github.com/bdnugget/rs-sdk.git
synced 2026-11-07 09:07:30 +00:00
fix(engine): allow staff ::kick and ::ban regardless of NODE_PRODUCTION
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c06f209270
commit
76391328ec
4 files changed
+88
-5
No files matched your search
+1
-1
@@ -6,7 +6,7 @@
|
||||
"docs:api": "bun sdk/generate-api-docs.ts",
|
||||
"docs:api:check": "bun sdk/generate-api-docs.ts --check",
|
||||
"fetch-collision-data": "bun sdk/fetch-collision-data.ts",
|
||||
"test": "bun test sdk/test server/webclient/src/bot server/webclient/src/util server/webclient/src/lite server/webclient/src/mapview server/engine/test/market.test.ts server/engine/test/market-web.test.ts server/engine/test/item-search.test.ts server/engine/test/file-stream.test.ts server/engine/test/get-ip.test.ts server/engine/test/bank-hiscores.test.ts server/engine/test/reconnect.test.ts server/engine/test/worldfeed.test.ts server/gateway/state-fanout.test.ts server/engine/test/hiscores-performance.test.ts server/engine/test/miscellania-coffers.test.ts",
|
||||
"test": "bun test sdk/test server/webclient/src/bot server/webclient/src/util server/webclient/src/lite server/webclient/src/mapview server/engine/test/market.test.ts server/engine/test/market-web.test.ts server/engine/test/item-search.test.ts server/engine/test/file-stream.test.ts server/engine/test/get-ip.test.ts server/engine/test/bank-hiscores.test.ts server/engine/test/reconnect.test.ts server/engine/test/worldfeed.test.ts server/gateway/state-fanout.test.ts server/engine/test/hiscores-performance.test.ts server/engine/test/miscellania-coffers.test.ts server/engine/test/live-moderation.test.ts",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"typecheck:webclient": "tsc --noEmit -p server/webclient/tsconfig.json",
|
||||
"test:market:integration": "cd server/engine && bun test/market-integration.ts && bun test/market-recovery.ts && bun test/market-protocol.ts && bun test/market-disabled.ts"
|
||||
|
||||
@@ -633,8 +633,8 @@ export default class ClientCheatHandler extends ClientGameMessageHandler<ClientC
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else if (cmd === 'ban' && Environment.node.production) {
|
||||
// custom
|
||||
} else if (cmd === 'ban') {
|
||||
// Moderation is staff-only, regardless of the world's production setting.
|
||||
if (args.length < 2) {
|
||||
// ::ban <username> <minutes>
|
||||
player.messageGame('Usage: ::ban <username> <minutes>');
|
||||
@@ -659,8 +659,8 @@ export default class ClientCheatHandler extends ClientGameMessageHandler<ClientC
|
||||
|
||||
World.notifyPlayerMute(player.username, username, Date.now() + minutes * 60 * 1000);
|
||||
player.messageGame(`Player '${args[0]}' has been muted for ${minutes} minutes.`);
|
||||
} else if (cmd === 'kick' && Environment.node.production) {
|
||||
// custom
|
||||
} else if (cmd === 'kick') {
|
||||
// Moderation is staff-only, regardless of the world's production setting.
|
||||
if (args.length < 1) {
|
||||
// ::kick <username>
|
||||
player.messageGame('Usage: ::kick <username>');
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
import { afterEach, expect, mock, spyOn, test } from 'bun:test';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
class TestWorker extends EventEmitter {
|
||||
postMessage() {}
|
||||
}
|
||||
mock.module('worker_threads', () => ({ Worker: TestWorker }));
|
||||
|
||||
const { default: World } = await import('../../src/engine/World.js');
|
||||
const { default: Environment } = await import('../../src/util/Environment.js');
|
||||
const { default: ClientCheatHandler } = await import('../../src/network/game/client/handler/ClientCheatHandler.js');
|
||||
const { default: ClientCheat } = await import('../../src/network/game/client/model/ClientCheat.js');
|
||||
const { NetworkPlayer } = await import('../../src/engine/entity/NetworkPlayer.js');
|
||||
const { default: WSClientSocket } = await import('../../src/server/ws/WSClientSocket.js');
|
||||
|
||||
const originalProduction = Environment.node.production;
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
Environment.node.production = originalProduction;
|
||||
});
|
||||
|
||||
function staff(level: number) {
|
||||
return { staffModLevel: level, username: 'moderator', addSessionLog: mock(() => {}), messageGame: mock(() => {}) } as any;
|
||||
}
|
||||
|
||||
for (const production of [false, true]) {
|
||||
test(`staff can kick through the normal logout path, production=${production}`, () => {
|
||||
Environment.node.production = production;
|
||||
const client = new WSClientSocket();
|
||||
const target = new NetworkPlayer('target', 1n, 1n, client);
|
||||
const logout = spyOn(target, 'logout').mockImplementation(() => {});
|
||||
const close = spyOn(client, 'close').mockImplementation(() => {});
|
||||
spyOn(World, 'getPlayerByUsername').mockReturnValue(target);
|
||||
new ClientCheatHandler().handle(new ClientCheat('kick target'), staff(2));
|
||||
expect(target.loggingOut).toBe(true);
|
||||
expect(logout).toHaveBeenCalledTimes(1);
|
||||
expect(close).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test(`staff can ban immediately, production=${production}`, () => {
|
||||
Environment.node.production = production;
|
||||
const ban = spyOn(World, 'notifyPlayerBan').mockImplementation(() => {});
|
||||
const before = Date.now();
|
||||
new ClientCheatHandler().handle(new ClientCheat('ban target 60'), staff(2));
|
||||
expect(ban).toHaveBeenCalledTimes(1);
|
||||
expect(ban.mock.calls[0][0]).toBe('moderator');
|
||||
expect(ban.mock.calls[0][1]).toBe('target');
|
||||
expect(ban.mock.calls[0][2]).toBeGreaterThanOrEqual(before + 60 * 60_000);
|
||||
});
|
||||
|
||||
test(`players below moderator level cannot kick or ban, production=${production}`, () => {
|
||||
Environment.node.production = production;
|
||||
const lookup = spyOn(World, 'getPlayerByUsername');
|
||||
const ban = spyOn(World, 'notifyPlayerBan');
|
||||
const handler = new ClientCheatHandler();
|
||||
for (const level of [0, 1]) {
|
||||
handler.handle(new ClientCheat('kick target'), staff(level));
|
||||
handler.handle(new ClientCheat('ban target 60'), staff(level));
|
||||
}
|
||||
expect(lookup).not.toHaveBeenCalled();
|
||||
expect(ban).not.toHaveBeenCalled();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import { expect, test } from 'bun:test';
|
||||
import { copyFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
test('live moderation preserves staff authorization in either production mode', () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'live-moderation-'));
|
||||
try {
|
||||
mkdirSync(join(dir, 'data/config'), { recursive: true });
|
||||
copyFileSync(join(import.meta.dir, '../data/config/private.pem'), join(dir, 'data/config/private.pem'));
|
||||
const result = Bun.spawnSync([process.execPath, 'test', join(import.meta.dir, 'fixtures/live-moderation.ts')], {
|
||||
cwd: dir,
|
||||
env: { ...process.env, GE_DATABASE: join(dir, 'market.sqlite') }
|
||||
});
|
||||
if (result.exitCode !== 0) throw new Error(result.stdout.toString() + result.stderr.toString());
|
||||
expect(result.exitCode).toBe(0);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
}, 15000);
|
||||
Reference in new issue
Block a user