Skip to content

Commit e778563

Browse files
committed
Change /modlogin to only inherit rights, hide them better in !players
Fixes #673
1 parent 01d7068 commit e778563

File tree

11 files changed

+130
-18
lines changed

11 files changed

+130
-18
lines changed

data/server/callbacks.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ forward OnPlayerActivityChange(playerid, activity);
99
[Deferred] forward OnPlayerChecksumAvailable(playerid, address, checksum);
1010
forward OnPlayerLeaveActivity(playerid);
1111
forward OnPlayerLevelChange(playerid, newlevel, temporary);
12-
forward OnPlayerLogin(playerid, userid, vip, gangid, undercover);
12+
forward OnPlayerLogin(playerid, userid, vip, gangid);
1313
[Deferred] forward OnPlayerResolvedDeath(playerid, killerid, reason);
1414
forward OnSetiOwnershipChange(playerid);
1515
forward OnPlayerGuestLogin(playerId);
16+
forward OnPlayerModLogin(playerid, level, vip);
1617

1718
# Streamer callbacks
1819
[Deferred] forward OnDynamicObjectMoved(objectid);

javascript/entities/player_manager.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,25 @@ export class PlayerManager {
220220

221221
player.userId = event.userid;
222222
player.setVip(!!event.vip);
223-
player.setUndercover(!!event.undercover);
224223

225224
this.notifyObservers('onPlayerLogin', player, event);
226225
}
227226

227+
// Called when a player logs in to their account whilst undercover. This will mark them as such,
228+
// without overriding their account information to protect their identity.
229+
onPlayerModLogin(event) {
230+
const player = this.players_.get(event.playerid);
231+
if (!player)
232+
return; // the event has been received for an invalid player
233+
234+
player.level = event.level;
235+
236+
player.setUndercover(true);
237+
player.setVip(!!event.vip);
238+
239+
this.notifyObservers('onPlayerModLogin', player, event);
240+
}
241+
228242
// Called when a player has selected an object.
229243
onPlayerSelectObject(event) {
230244
const player = this.players_.get(event.playerid);

javascript/entities/test/mock_player.js

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ export class MockPlayer extends Player {
405405
// ---------------------------------------------------------------------------------------------
406406

407407
// Identifies the player to a fake account. The options can be specified optionally.
408-
async identify({ userId = 42, vip = 0, gangId = 0, undercover = 0 } = {}) {
408+
async identify({ userId = 42, level = null, vip = 0, gangId = 0, undercover = 0 } = {}) {
409409
let resolver = null;
410410

411411
const observerPromise = new Promise(resolve => resolver = resolve);
@@ -414,15 +414,28 @@ export class MockPlayer extends Player {
414414
server.playerManager.removeObserver(observer);
415415
resolver();
416416
}
417+
418+
onPlayerModLogin(player) {
419+
server.playerManager.removeObserver(observer);
420+
resolver();
421+
}
417422
};
418423

419424
server.playerManager.addObserver(observer);
420-
dispatchEvent('playerlogin', {
421-
playerid: this.id,
422-
userid: userId,
423-
gangid: gangId,
424-
undercover, vip,
425-
});
425+
if (undercover) {
426+
dispatchEvent('playermodlogin', {
427+
playerid: this.id,
428+
level: level ?? Player.LEVEL_PLAYER,
429+
vip
430+
});
431+
} else {
432+
dispatchEvent('playerlogin', {
433+
playerid: this.id,
434+
userid: userId,
435+
gangid: gangId,
436+
undercover, vip,
437+
});
438+
}
426439

427440
await observerPromise;
428441
}

javascript/features/account/account_nuwani_commands.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -332,10 +332,10 @@ export class AccountNuwaniCommands {
332332

333333
const name = player.name;
334334
const registered = player.account.isRegistered();
335-
const vip = player.isVip();
335+
const vip = player.account.isVip();
336336
const minimized = isPlayerMinimized(player.id);
337337
const temporary = player.isTemporaryAdministrator();
338-
const level = player.isUndercover() ? Player.LEVEL_PLAYER
338+
const level = player.isUndercover() ? player.account.level
339339
: player.level;
340340

341341
players.push({ name, registered, vip, temporary, level, minimized });

javascript/features/account_provider/account_manager.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ export class AccountManager {
2020
'playerlogin', AccountManager.prototype.onPlayerLoginEvent.bind(this));
2121
this.callbacks_.addEventListener(
2222
'playerguestlogin', AccountManager.prototype.onPlayerGuestLoginEvent.bind(this));
23+
this.callbacks_.addEventListener(
24+
'playermodlogin', AccountManager.prototype.onPlayerModLoginEvent.bind(this));
2325

2426
provideNative('SetIsRegistered', 'ii', AccountManager.prototype.setIsRegistered.bind(this));
2527

@@ -87,6 +89,19 @@ export class AccountManager {
8789
server.playerManager.onPlayerGuestSession(player);
8890
}
8991

92+
// Called when a player has logged in to an administrator's account, and their original level
93+
// and rights should be applied. Statistics will continue to aggregate as the other account.
94+
onPlayerModLoginEvent(event) {
95+
const player = server.playerManager.getById(event.playerid);
96+
if (!player)
97+
return; // the |player| does not exist (anymore)
98+
99+
// TODO: Mutate the |PlayerAccountSupplement| with the appropriate data when that's made
100+
// responsible for account information, including for undercover players.
101+
102+
server.playerManager.onPlayerModLogin(event);
103+
}
104+
90105
// Called when the |player| has disconnected from the server.
91106
onPlayerDisconnect(player) {
92107
if (!player.account.isIdentified())

javascript/features/account_provider/account_manager.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,36 @@ describe('AccountManager', (it, beforeEach) => {
2020

2121
assert.notEqual(gunther.name, 'Gunther');
2222
});
23+
24+
it('is able to log in players undercover, override their rights', async (assert) => {
25+
const gunther = server.playerManager.getById(/* Gunther= */ 0);
26+
27+
assert.equal(gunther.level, Player.LEVEL_PLAYER);
28+
assert.isFalse(gunther.isVip());
29+
assert.isFalse(gunther.isUndercover());
30+
assert.isFalse(gunther.account.isRegistered());
31+
assert.isFalse(gunther.account.isIdentified());
32+
33+
await gunther.identify({ userId: 42 });
34+
35+
assert.equal(gunther.level, Player.LEVEL_PLAYER);
36+
assert.isFalse(gunther.isVip());
37+
assert.isFalse(gunther.isUndercover());
38+
assert.isTrue(gunther.account.isRegistered());
39+
assert.isTrue(gunther.account.isIdentified());
40+
assert.equal(gunther.account.userId, 42);
41+
42+
dispatchEvent('playermodlogin', {
43+
playerid: gunther.id,
44+
level: Player.LEVEL_ADMINISTRATOR,
45+
vip: true,
46+
});
47+
48+
assert.equal(gunther.level, Player.LEVEL_ADMINISTRATOR);
49+
assert.isTrue(gunther.isVip());
50+
assert.isTrue(gunther.isUndercover());
51+
assert.isTrue(gunther.account.isRegistered());
52+
assert.isTrue(gunther.account.isIdentified());
53+
assert.equal(gunther.account.userId, 42);
54+
});
2355
});

javascript/features/account_provider/account_provider_database.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
const ACCOUNT_LOAD_QUERY = `
77
SELECT
88
users_mutable.user_id,
9+
users.level,
10+
users.is_vip,
911
users_mutable.online_time,
1012
users_mutable.kill_count,
1113
users_mutable.death_count,
@@ -20,6 +22,8 @@ const ACCOUNT_LOAD_QUERY = `
2022
users_mutable.muted
2123
FROM
2224
users_mutable
25+
LEFT JOIN
26+
users ON users.user_id = users_mutable.user_id
2327
WHERE
2428
users_mutable.user_id = ?`;
2529

javascript/features/account_provider/player_account_supplement.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@
44

55
import { Supplement } from 'base/supplementable.js';
66

7+
// Converts the database-bound level string to a Player.LEVEL_* constant.
8+
function toPlayerLevel(level) {
9+
switch (level) {
10+
case 'Management':
11+
return Player.LEVEL_MANAGEMENT;
12+
case 'Administrator':
13+
return Player.LEVEL_ADMINISTRATOR;
14+
}
15+
16+
return Player.LEVEL_PLAYER;
17+
}
18+
719
// Supplements the Player object with an `account` accessor, giving other features access to the
820
// information associated with a player's account. This supplement will be created for players who
921
// are registered on the server, as well as players who are visiting us as a guest.
@@ -14,6 +26,9 @@ export class PlayerAccountSupplement extends Supplement {
1426
hasRequestedUpdate_ = false;
1527

1628
userId_ = null;
29+
level_ = null;
30+
isVip_ = false;
31+
1732
bankAccountBalance_ = 0;
1833
cashBalance_ = 0;
1934
reactionTests_ = 0;
@@ -22,6 +37,12 @@ export class PlayerAccountSupplement extends Supplement {
2237
// Gets the permanent user Id that has been assigned to this user. Read-only.
2338
get userId() { return this.userId_; }
2439

40+
// Gets the level of this player as associated with their account. Read-only.
41+
get level() { return this.level_; }
42+
43+
// Gets whether this player is a VIP, as set in their account. Read-only.
44+
isVip() { return this.isVip_; }
45+
2546
// Gets or sets the balance this user has on their bank account. Writes will be processed as
2647
// high priority, because
2748
get bankAccountBalance() { return this.bankAccountBalance_; }
@@ -58,6 +79,9 @@ export class PlayerAccountSupplement extends Supplement {
5879
// data transformations to make the data types appropriate for JavaScript. (E.g. colours.)
5980
initializeFromDatabase(player, databaseRow) {
6081
this.userId_ = databaseRow.user_id;
82+
this.level_ = toPlayerLevel(databaseRow.level);
83+
this.isVip_ = !!databaseRow.is_vip;
84+
6185
this.bankAccountBalance_ = databaseRow.money_bank;
6286
this.cashBalance_ = databaseRow.money_cash;
6387
this.reactionTests_ = databaseRow.stats_reaction;

javascript/features/account_provider/test/mock_account_provider_database.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ export class MockAccountProviderDatabase extends AccountProviderDatabase {
1111
async loadAccountData(userId) {
1212
return {
1313
user_id: userId,
14+
level: 'Player',
15+
is_vip: 0,
1416
online_time: 0,
1517
kill_count: 0,
1618
death_count: 0,

pawn/Features/Account/Account.pwn

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ class Account <playerId (MAX_PLAYERS)> {
183183
Annotation::ExpandList<OnPlayerLogin>(playerId);
184184

185185
// Broadcast an OnPlayerLogin callback that can be intercepted by other scripts.
186-
CallRemoteFunction("OnPlayerLogin", "iiiii", playerId, m_userId, Player(playerId)->isVip(), AccountData(playerId)->gangId(), 0 /* undercover */);
186+
CallRemoteFunction("OnPlayerLogin", "iiii", playerId, m_userId, Player(playerId)->isVip(), AccountData(playerId)->gangId());
187187
}
188188

189189
/**
@@ -244,6 +244,7 @@ class Account <playerId (MAX_PLAYERS)> {
244244
*/
245245
public onSuccessfulModLoginAttempt(PlayerAccessLevel: level, originalUsername[], originalUserId) {
246246
AccountData(playerId)->applyPlayerLevel(level);
247+
247248
UndercoverAdministrator(playerId)->setIsUndercoverAdministrator(true);
248249
UndercoverAdministrator(playerId)->resetUndercoverLoginAttemptCount();
249250
UndercoverAdministrator(playerId)->setOriginalUsername(originalUsername);
@@ -260,12 +261,10 @@ class Account <playerId (MAX_PLAYERS)> {
260261

261262
EchoMessage("notice-crew", "z", notice);
262263

263-
// Broadcast an OnPlayerLogin callback that can be intercepted by other scripts.
264-
CallRemoteFunction("OnPlayerLogin", "iiiii", playerId, originalUserId, Player(playerId)->isVip(), AccountData(playerId)->gangId(), 1 /* undercover */);
264+
// Broadcast an OnPlayerModLogin callback that can be intercepted by other scripts.
265+
CallRemoteFunction("OnPlayerModLogin", "iii", playerId, _: level, Player(playerId)->isVip());
265266

266267
Annotation::ExpandList<OnPlayerModLogin>(playerId);
267-
268-
// TODO(Russell): Should this broadcast an event similar to OnPlayerLogin as well?
269268
}
270269

271270
/**
@@ -289,8 +288,11 @@ class Account <playerId (MAX_PLAYERS)> {
289288
}
290289
};
291290

292-
forward OnPlayerLogin(playerid, userid, vip, gangId, undercover);
293-
public OnPlayerLogin(playerid, userid, vip, gangId, undercover) {}
291+
forward OnPlayerLogin(playerid, userid, vip, gangId);
292+
public OnPlayerLogin(playerid, userid, vip, gangId) {}
294293

295294
forward OnPlayerGuestLogin(playerId);
296295
public OnPlayerGuestLogin(playerId) {}
296+
297+
forward OnPlayerModLogin(playerid, level, vip);
298+
public OnPlayerModLogin(playerid, level, vip) {}

0 commit comments

Comments
 (0)