···4848 playerState: Map<string, string> = new Map();
4949 playerCleanupDiscoveryAttempt: Map<string, boolean> = new Map();
50505151+ deceasedPlayers: Map<string, Dayjs> = new Map();
5252+5153 scheduler: ToadScheduler = new ToadScheduler();
52545355 protected isPositional: boolean = false;
···196198 return this.players.has(id);
197199 }
198200201201+ isZombiePlayer = (id: string, lastUpdated: Dayjs): boolean => {
202202+ return this.deceasedPlayers.has(id) && this.deceasedPlayers.get(id).isSame(lastUpdated);
203203+ }
204204+199205 genPlayerId = (data: PlayObject | PlayerStateDataMaybePlay): string => {
200206 return genGroupIdStr(getPlatformIdFromData(data));
201207 }
···208214 this.players.get(id)?.logger.debug(reason);
209215 }
210216 using player = this.players.get(id);
217217+ this.deceasedPlayers.set(id, player.stateLastUpdatedAt);
211218 player[Symbol.dispose]();
212219 this.players.delete(id);
213220 this.playerState.delete(id);
···236243 const id = getPlatformIdFromData(data);
237244 const idStr = this.genPlayerId(data);
238245 if (!this.players.has(idStr)) {
246246+ if(asPlayerStateDataMaybePlay(data) && data.stateUpdatedAt !== undefined) {
247247+ if(this.isZombiePlayer(idStr, data.stateUpdatedAt)) {
248248+ this.logger.trace(`Not creating player for ${idStr} because last state update timestamp has not changed since it was deleted.`);
249249+ continue;
250250+ } else {
251251+ // cleaning up in case it already existed but has new timestamp
252252+ this.deceasedPlayers.delete(idStr);
253253+ }
254254+ }
239255 this.setNewPlayer(idStr, this.logger, id);
240256241257 if(!this.multiPlatform && this.players.size > 1) {
···257273 return player.platformEquals(id);
258274 });
259275260260- // we've received some form of communication from the source for this player
276276+ let hasFreshState = false;
261277 if (relevantDatas.length > 0) {
278278+ hasFreshState = true;
279279+ this.lastActivityAt = dayjs();
280280+ incomingData = this.pickPlatformSession(relevantDatas, player);
281281+ if(asPlayerStateDataMaybePlay(incomingData) && incomingData.stateUpdatedAt !== undefined && player.stateLastUpdatedAt.isSame(incomingData.stateUpdatedAt)) {
282282+ hasFreshState = false;
283283+ player.logger.trace('Skipping update because it has the same timestamp as the previous update');
284284+ }
285285+ }
286286+287287+ // we've received some form of communication from the source for this player
288288+ if (hasFreshState) {
262289 this.lastActivityAt = dayjs();
263290264291 // reset any player cleanup state since we got fresh data