Skip to content

Commit 23b48ab

Browse files
committed
Add teaminfo structure
1 parent 0c0439b commit 23b48ab

3 files changed

Lines changed: 334 additions & 7 deletions

File tree

src/managers/rustPlusManager.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { RustPlusInfo } from '../structures/rustPlusInfo';
3535
import { RustPlusTime } from '../structures/rustPlusTime';
3636
import { RustPlusMap } from '../structures/rustPlusMap';
3737
import { RustPlusMapMarkers } from '../structures/rustPlusMapMarkers';
38+
import { RustPlusTeamInfo } from '../structures/rustPlusTeamInfo';
3839
import * as discordMessages from '../discordUtils/discordMessages';
3940
import * as discordVoice from '../discordUtils/discordVoice';
4041

@@ -144,7 +145,7 @@ export class RustPlusInstance {
144145
public rpInfo: RustPlusInfo | null;
145146
public rpTime: RustPlusTime | null;
146147
public rpMap: RustPlusMap | null;
147-
//private appTeamInfo: rustplus.AppTeamInfo | null;
148+
public rpTeamInfo: RustPlusTeamInfo | null;
148149
public rpMapMarkers: RustPlusMapMarkers | null;
149150

150151

@@ -177,12 +178,9 @@ export class RustPlusInstance {
177178
this.rpInfo = null;
178179
this.rpTime = null;
179180
this.rpMap = null;
181+
this.rpTeamInfo = null;
180182
this.rpMapMarkers = null;
181183

182-
//this.appTeamInfo = null;
183-
184-
185-
186184
//this.leaderSteamId = '0'; /* 0 When there is no leader. */
187185
}
188186

@@ -254,6 +252,7 @@ export class RustPlusInstance {
254252
this.rpInfo = null;
255253
this.rpTime = null;
256254
this.rpMap = null;
255+
this.rpTeamInfo = null;
257256
this.rpMapMarkers = null;
258257

259258
// TODO! Remove timers example: pollingTimer, inGameChatTimeout, customTimers like lockedCrate,
@@ -367,13 +366,14 @@ export class RustPlusInstance {
367366
const info = ((rpInfo as rp.AppResponse).info as rp.AppInfo);
368367
const time = ((rpTime as rp.AppResponse).time as rp.AppTime);
369368
const mapMarkers = ((rpMapMarkers as rp.AppResponse).mapMarkers as rp.AppMapMarkers);
369+
const teamInfo = ((rpTeamInfo as rp.AppResponse).teamInfo as rp.AppTeamInfo);
370370

371371
if (firstPoll || this.rpInfo === null || this.rpTime === null) {
372372
console.log('FIRST POLL')
373-
// TODO! Set rpInfo, rpTime, rpTeamInfo, rpMapMarkers
374373
this.rpInfo = new RustPlusInfo(this, info);
375374
this.rpTime = new RustPlusTime(this, time);
376375
this.rpMapMarkers = new RustPlusMapMarkers(this, mapMarkers);
376+
this.rpTeamInfo = new RustPlusTeamInfo(this, teamInfo);
377377
}
378378
else {
379379
console.log('POLL')
@@ -391,7 +391,7 @@ export class RustPlusInstance {
391391
(this.rpInfo as RustPlusInfo).updateInfo(info);
392392
(this.rpTime as RustPlusTime).updateTime(time);
393393
(this.rpMapMarkers as RustPlusMapMarkers).updateMapMarkers(mapMarkers);
394-
// TODO! update rpInfo
394+
(this.rpTeamInfo as RustPlusTeamInfo).updateTeamInfo(teamInfo);
395395

396396
// TODO! smartAlarmHandler
397397
// TODO! storageMonitorHandler

src/structures/rustPlusTeamInfo.ts

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
/*
2+
Copyright (C) 2025 Alexander Emanuelsson (alexemanuelol)
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
https://github.com/alexemanuelol/rustplusplus
18+
19+
*/
20+
21+
import * as rp from 'rustplus-ts';
22+
23+
import { guildInstanceManager as gim } from '../../index';
24+
import { RustPlusInstance } from '../managers/rustPlusManager';
25+
import * as types from '../utils/types';
26+
import { RustPlusTeamInfoMember } from './rustPlusTeamInfoMember';
27+
import { GuildInstance } from '../managers/guildInstanceManager';
28+
29+
export class RustPlusTeamInfo {
30+
public rpInstance: RustPlusInstance;
31+
public appTeamInfo: rp.AppTeamInfo;
32+
public members: Map<types.SteamId, RustPlusTeamInfoMember>;
33+
public allOnline: boolean;
34+
public allOffline: boolean;
35+
36+
constructor(rpInstance: RustPlusInstance, appTeamInfo: rp.AppTeamInfo) {
37+
this.rpInstance = rpInstance;
38+
this.appTeamInfo = appTeamInfo;
39+
this.members = new Map<types.SteamId, RustPlusTeamInfoMember>();
40+
this.allOnline = false;
41+
this.allOffline = false;
42+
}
43+
44+
public updateTeamInfo(appTeamInfo: rp.AppTeamInfo) {
45+
const gInstance = gim.getGuildInstance(this.rpInstance.guildId) as GuildInstance;
46+
47+
if (this.isLeaderSteamIdChanged(appTeamInfo)) {
48+
// TODO! Notify about leader changed
49+
}
50+
51+
for (const member of this.getNewMembers(appTeamInfo)) {
52+
this.addMember(member);
53+
54+
if (!Object.hasOwn(gInstance.teamMemberChatColorMap, member.steamId)) {
55+
const letters = '0123456789ABCDEF';
56+
let color = '#';
57+
for (let i = 0; i < 6; i++) {
58+
color += letters[Math.floor(Math.random() * 16)];
59+
}
60+
61+
gInstance.teamMemberChatColorMap[member.steamId] = color;
62+
}
63+
}
64+
65+
for (const member of this.getLeftMembers(appTeamInfo)) {
66+
this.removeMember(member.steamId);
67+
}
68+
69+
for (const member of this.getReminingMembers(appTeamInfo)) {
70+
const teamMember = this.getMember(member.steamId);
71+
if (teamMember) {
72+
teamMember.updateTeamInfoMember(member);
73+
}
74+
}
75+
76+
/* Update variables */
77+
this.allOnline = true;
78+
this.allOffline = true;
79+
for (const member of this.members.values()) {
80+
this.allOnline = (this.allOnline && member.appTeamInfoMember.isOnline);
81+
this.allOffline = (this.allOffline && !member.appTeamInfoMember.isOnline);
82+
}
83+
84+
gim.updateGuildInstance(this.rpInstance.guildId);
85+
this.appTeamInfo = appTeamInfo;
86+
}
87+
88+
public isLeaderSteamIdChanged(appTeamInfo: rp.AppTeamInfo): boolean {
89+
return this.appTeamInfo.leaderSteamId !== appTeamInfo.leaderSteamId;
90+
}
91+
92+
93+
/**
94+
* Other methods
95+
*/
96+
97+
public addMember(appTeamInfoMember: rp.AppTeamInfo_Member) {
98+
if (this.members.has(appTeamInfoMember.steamId)) return;
99+
const member = new RustPlusTeamInfoMember(this.rpInstance, appTeamInfoMember);
100+
this.members.set(appTeamInfoMember.steamId, member);
101+
}
102+
103+
public removeMember(steamId: types.SteamId) {
104+
this.members.delete(steamId);
105+
}
106+
107+
public getMember(steamId: types.SteamId): RustPlusTeamInfoMember | null {
108+
return this.members.get(steamId) || null;
109+
}
110+
111+
public isPlayerInTeam(steamId: types.SteamId): boolean {
112+
return this.members.has(steamId);
113+
}
114+
115+
public getLongestAliveMember(): RustPlusTeamInfoMember | null {
116+
if (this.members.size === 0) return null;
117+
118+
let longest: RustPlusTeamInfoMember | null = null;
119+
for (const member of this.members.values()) {
120+
if (longest === null || member.getAliveSeconds() > longest.getAliveSeconds()) {
121+
longest = member;
122+
}
123+
}
124+
return longest;
125+
}
126+
127+
public getNewMembers(appTeamInfo: rp.AppTeamInfo): rp.AppTeamInfo_Member[] {
128+
const newMembers: rp.AppTeamInfo_Member[] = [];
129+
for (const appMember of appTeamInfo.members) {
130+
if (!this.isPlayerInTeam(appMember.steamId)) {
131+
newMembers.push(appMember);
132+
}
133+
}
134+
return newMembers;
135+
}
136+
137+
public getLeftMembers(appTeamInfo: rp.AppTeamInfo): rp.AppTeamInfo_Member[] {
138+
const leftMembers: rp.AppTeamInfo_Member[] = [];
139+
for (const member of this.members.values()) {
140+
if (!appTeamInfo.members.find(m => m.steamId === member.appTeamInfoMember.steamId)) {
141+
leftMembers.push(member.appTeamInfoMember);
142+
}
143+
}
144+
return leftMembers;
145+
}
146+
147+
public getReminingMembers(appTeamInfo: rp.AppTeamInfo): rp.AppTeamInfo_Member[] {
148+
const remainingMembers: rp.AppTeamInfo_Member[] = [];
149+
for (const member of this.members.values()) {
150+
if (appTeamInfo.members.find(m => m.steamId === member.appTeamInfoMember.steamId)) {
151+
remainingMembers.push(member.appTeamInfoMember);
152+
}
153+
}
154+
return remainingMembers;
155+
}
156+
}
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
/*
2+
Copyright (C) 2025 Alexander Emanuelsson (alexemanuelol)
3+
4+
This program is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
This program is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
17+
https://github.com/alexemanuelol/rustplusplus
18+
19+
*/
20+
21+
import * as rp from 'rustplus-ts';
22+
23+
import { RustPlusInstance } from '../managers/rustPlusManager';
24+
import { secondsToFullScale } from '../utils/timer';
25+
import * as constants from '../utils/constants';
26+
27+
export class RustPlusTeamInfoMember {
28+
public rpInstance: RustPlusInstance;
29+
public appTeamInfoMember: rp.AppTeamInfo_Member;
30+
public lastMovementDate: Date | null;
31+
public wentOfflineDate: Date | null;
32+
public wasAfk: boolean;
33+
34+
constructor(rpInstance: RustPlusInstance, appTeamInfoMember: rp.AppTeamInfo_Member) {
35+
this.rpInstance = rpInstance;
36+
this.appTeamInfoMember = appTeamInfoMember;
37+
this.lastMovementDate = null;
38+
this.wentOfflineDate = null;
39+
this.wasAfk = false;
40+
}
41+
42+
public updateTeamInfoMember(appTeamInfoMember: rp.AppTeamInfo_Member) {
43+
if (this.isGoneOffline(appTeamInfoMember)) {
44+
this.wentOfflineDate = new Date();
45+
}
46+
47+
if (this.isGoneOnline(appTeamInfoMember)) {
48+
this.lastMovementDate = new Date();
49+
this.wasAfk = false;
50+
}
51+
52+
if (this.isMoved(appTeamInfoMember)) {
53+
this.lastMovementDate = new Date();
54+
this.wasAfk = false;
55+
}
56+
else {
57+
if (!this.appTeamInfoMember.isOnline && !this.isGoneOnline(appTeamInfoMember)) {
58+
this.wasAfk = false;
59+
}
60+
}
61+
62+
this.appTeamInfoMember = appTeamInfoMember;
63+
}
64+
65+
public isSteamIdChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
66+
return this.appTeamInfoMember.steamId !== appTeamInfoMember.steamId;
67+
}
68+
69+
public isNameChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
70+
return this.appTeamInfoMember.name !== appTeamInfoMember.name;
71+
}
72+
73+
public isXChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
74+
return this.appTeamInfoMember.x !== appTeamInfoMember.x;
75+
}
76+
77+
public isYChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
78+
return this.appTeamInfoMember.y !== appTeamInfoMember.y;
79+
}
80+
81+
public isOnlineChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
82+
return this.appTeamInfoMember.isOnline !== appTeamInfoMember.isOnline;
83+
}
84+
85+
public isSpawnTimeChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
86+
return this.appTeamInfoMember.spawnTime !== appTeamInfoMember.spawnTime;
87+
}
88+
89+
public isAliveChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
90+
return this.appTeamInfoMember.isAlive !== appTeamInfoMember.isAlive;
91+
}
92+
93+
public isDeathTimeChanged(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
94+
return this.appTeamInfoMember.deathTime !== appTeamInfoMember.deathTime;
95+
}
96+
97+
/**
98+
* Other methods
99+
*/
100+
101+
public isGoneOnline(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
102+
return (this.appTeamInfoMember.isOnline === false && appTeamInfoMember.isOnline === true);
103+
}
104+
105+
public isGoneOffline(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
106+
return (this.appTeamInfoMember.isOnline === true && appTeamInfoMember.isOnline === false);
107+
}
108+
109+
public isGoneAlive(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
110+
return (this.appTeamInfoMember.isAlive === false && appTeamInfoMember.isAlive === true);
111+
}
112+
113+
public isGoneDead(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
114+
return (this.appTeamInfoMember.isAlive === true && appTeamInfoMember.isAlive === false) ||
115+
this.isDeathTimeChanged(appTeamInfoMember);
116+
}
117+
118+
public isMoved(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
119+
return (this.isXChanged(appTeamInfoMember) || this.isYChanged(appTeamInfoMember));
120+
}
121+
122+
public isAfk(): boolean {
123+
if (this.lastMovementDate === null) {
124+
return false;
125+
}
126+
127+
return (Date.now() - this.lastMovementDate.getTime()) >= constants.AFK_TIME_SECONDS;
128+
}
129+
130+
public isGoneAfk(appTeamInfoMember: rp.AppTeamInfo_Member): boolean {
131+
return (
132+
!this.wasAfk &&
133+
!this.isMoved(appTeamInfoMember) &&
134+
this.appTeamInfoMember.isOnline);
135+
}
136+
137+
public getAfkSeconds(): number {
138+
if (this.lastMovementDate === null) {
139+
return 0;
140+
}
141+
return (new Date().getTime() - this.lastMovementDate.getTime()) / 1000;
142+
}
143+
144+
public getAfkTime(ignore: string = ''): string {
145+
return secondsToFullScale(this.getAfkSeconds(), ignore);
146+
}
147+
148+
public getAliveSeconds(): number {
149+
if (this.appTeamInfoMember.spawnTime === 0) return 0;
150+
return (new Date().getTime() - new Date(this.appTeamInfoMember.spawnTime * 1000).getTime()) / 1000;
151+
}
152+
153+
public getAliveTime(ignore: string = ''): string {
154+
return secondsToFullScale(this.getAliveSeconds(), ignore);
155+
}
156+
157+
public getDeathSeconds(): number {
158+
if (this.appTeamInfoMember.deathTime === 0) return 0;
159+
return (new Date().getTime() - new Date(this.appTeamInfoMember.deathTime * 1000).getTime()) / 1000;
160+
}
161+
162+
public getDeathTime(ignore: string = ''): string {
163+
return secondsToFullScale(this.getDeathSeconds(), ignore);
164+
}
165+
166+
public getOfflineTime(ignore: string = ''): string | null {
167+
if (this.wentOfflineDate === null) return null;
168+
const seconds = (new Date().getTime() - this.wentOfflineDate.getTime()) / 1000;
169+
return (secondsToFullScale(seconds, ignore));
170+
}
171+
}

0 commit comments

Comments
 (0)