screeps-connectivity 0.2.0 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/package.json +1 -1
- package/src/socket/SocketClient.ts +12 -2
- package/src/stores/MapStatsStore.ts +2 -0
- package/src/stores/RoomStore.ts +23 -12
- package/src/types/events.ts +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b14a86d: Fix foreign creep badge and username display in observed rooms.
|
|
8
|
+
|
|
9
|
+
When observing a room from another player, newly spawned creeps weren't showing
|
|
10
|
+
the owner's badge and displayed player ID instead of username. Fixed by:
|
|
11
|
+
|
|
12
|
+
- Merging user data across ticks instead of replacing, preserving player info
|
|
13
|
+
- Adding `badge?: Badge` to the users type throughout the codebase
|
|
14
|
+
- Adding `refreshForeignCreepBadges()` to update creep visuals when badge data arrives
|
|
15
|
+
|
|
16
|
+
## 0.2.2
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- a42c89c: Guard against null or missing `objects` field in room update messages, and catch listener errors in `SocketClient.emit` so a bad listener cannot trigger a fatal socket error and kick the user out.
|
|
21
|
+
|
|
22
|
+
## 0.2.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- e761c02: Add `status` field to `MapStatsRoomData` so consumers can detect out-of-borders and restricted rooms. The client gains a "Show unclaimable rooms" toggle that highlights corridors, sector centres, owned rooms, and restricted areas on the world map.
|
|
27
|
+
|
|
3
28
|
## Unreleased
|
|
4
29
|
|
|
5
30
|
### Breaking Changes
|
package/package.json
CHANGED
|
@@ -87,7 +87,8 @@ export class SocketClient {
|
|
|
87
87
|
}
|
|
88
88
|
this.ws.onmessage = (event) => {
|
|
89
89
|
this.handleMessage(event).catch(err => {
|
|
90
|
-
|
|
90
|
+
const raw = typeof event.data === 'string' ? event.data.slice(0, 200) : '(binary)'
|
|
91
|
+
this.logger.log('message parse error', err instanceof Error ? err.stack ?? err.message : String(err), 'raw:', raw)
|
|
91
92
|
this.emit('socket:error', err instanceof Error ? err : new Error(String(err)))
|
|
92
93
|
})
|
|
93
94
|
}
|
|
@@ -157,7 +158,16 @@ export class SocketClient {
|
|
|
157
158
|
}
|
|
158
159
|
|
|
159
160
|
private emit(channel: string, data: unknown): void {
|
|
160
|
-
this.listeners.get(channel)?.forEach(cb =>
|
|
161
|
+
this.listeners.get(channel)?.forEach(cb => {
|
|
162
|
+
try {
|
|
163
|
+
cb(data)
|
|
164
|
+
} catch (err) {
|
|
165
|
+
// A listener error on one channel must not prevent other listeners from
|
|
166
|
+
// running, and must not be rethrown into handleMessage's .catch() where
|
|
167
|
+
// it would be treated as a fatal socket error and kick the user out.
|
|
168
|
+
this.logger.log('listener error on channel', channel, err instanceof Error ? err.stack ?? err.message : String(err))
|
|
169
|
+
}
|
|
170
|
+
})
|
|
161
171
|
}
|
|
162
172
|
|
|
163
173
|
private async handleMessage(event: MessageEvent): Promise<void> {
|
|
@@ -10,6 +10,7 @@ export interface MapStatsRoomData {
|
|
|
10
10
|
username?: string
|
|
11
11
|
safeMode?: boolean
|
|
12
12
|
badge?: ApiMapStatsBadge
|
|
13
|
+
status?: string
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export interface MapStatsStoreEvents {
|
|
@@ -110,6 +111,7 @@ export class MapStatsStore extends TypedStore<MapStatsStoreEvents> {
|
|
|
110
111
|
username: ownerId ? userMap[ownerId]?.username : undefined,
|
|
111
112
|
safeMode: stat.safeMode,
|
|
112
113
|
badge: ownerId ? userMap[ownerId]?.badge : undefined,
|
|
114
|
+
status: stat.status,
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
}
|
package/src/stores/RoomStore.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { TypedStore } from './TypedStore.js'
|
|
|
2
2
|
import { RoomTerrain } from '../types/game.js'
|
|
3
3
|
import type { Logger } from '../logger.js'
|
|
4
4
|
import type { RoomStoreEvents } from '../types/events.js'
|
|
5
|
-
import type { RoomObject, RoomObjectMap, RoomObjectDiff } from '../types/game.js'
|
|
5
|
+
import type { Badge, RoomObject, RoomObjectMap, RoomObjectDiff } from '../types/game.js'
|
|
6
6
|
import type { HttpClient } from '../http/HttpClient.js'
|
|
7
7
|
import type { SocketClient } from '../socket/SocketClient.js'
|
|
8
8
|
import type { Cache } from '../cache/Cache.js'
|
|
@@ -13,7 +13,7 @@ export class RoomStore extends TypedStore<RoomStoreEvents> {
|
|
|
13
13
|
private readonly socket: SocketClient
|
|
14
14
|
private readonly cache: Cache
|
|
15
15
|
private readonly roomObjects = new Map<string, RoomObjectMap>()
|
|
16
|
-
private readonly roomUsers = new Map<string, Record<string, { _id: string; username: string }>>()
|
|
16
|
+
private readonly roomUsers = new Map<string, Record<string, { _id: string; username: string; badge?: Badge }>>()
|
|
17
17
|
private readonly roomSubCount = new Map<string, number>()
|
|
18
18
|
private readonly lastFlagsString = new Map<string, string>()
|
|
19
19
|
|
|
@@ -175,21 +175,30 @@ export class RoomStore extends TypedStore<RoomStoreEvents> {
|
|
|
175
175
|
})
|
|
176
176
|
|
|
177
177
|
const listenerSub = this.socket.on(channel, (data) => {
|
|
178
|
-
const update = data as { objects
|
|
178
|
+
const update = data as { objects?: RoomObjectDiff | null; gameTime?: number; visual?: string; flags?: string; users?: Record<string, { _id: string; username: string; badge?: Badge }> }
|
|
179
179
|
const current: RoomObjectMap = { ...(this.roomObjects.get(mapKey) ?? {}) }
|
|
180
180
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
181
|
+
if (update.objects == null) {
|
|
182
|
+
// Some server implementations send tick messages without an objects field
|
|
183
|
+
// (e.g. heartbeat/visual-only ticks). Log it so it is visible in debug
|
|
184
|
+
// output but do not crash — we still process gameTime, visual and flags.
|
|
185
|
+
if (update.objects === null) {
|
|
186
|
+
this.logger.log('room update has null objects field', room, shard, `gameTime: ${update.gameTime}`)
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
for (const [id, obj] of Object.entries(update.objects)) {
|
|
190
|
+
if (obj === null) {
|
|
191
|
+
delete current[id]
|
|
192
|
+
} else if (current[id]) {
|
|
193
|
+
current[id] = { ...current[id], ...obj } as RoomObject
|
|
194
|
+
} else {
|
|
195
|
+
current[id] = obj as RoomObject
|
|
196
|
+
}
|
|
188
197
|
}
|
|
189
198
|
}
|
|
190
199
|
|
|
191
200
|
// Build diff that includes flags so ObjectLayer can incremental-update them
|
|
192
|
-
const diff: RoomObjectDiff = { ...update.objects }
|
|
201
|
+
const diff: RoomObjectDiff = { ...(update.objects ?? {}) }
|
|
193
202
|
|
|
194
203
|
// Parse and inject flags from the dedicated flags field — only when the
|
|
195
204
|
// server-sent flag string actually changed. The flags field is included
|
|
@@ -236,7 +245,8 @@ export class RoomStore extends TypedStore<RoomStoreEvents> {
|
|
|
236
245
|
}
|
|
237
246
|
|
|
238
247
|
if (update.users) {
|
|
239
|
-
this.roomUsers.
|
|
248
|
+
const existing = this.roomUsers.get(mapKey) ?? {}
|
|
249
|
+
this.roomUsers.set(mapKey, { ...existing, ...update.users })
|
|
240
250
|
}
|
|
241
251
|
this.roomObjects.set(mapKey, current)
|
|
242
252
|
const users = this.roomUsers.get(mapKey)
|
|
@@ -252,6 +262,7 @@ export class RoomStore extends TypedStore<RoomStoreEvents> {
|
|
|
252
262
|
if (remaining <= 0) {
|
|
253
263
|
this.roomSubCount.delete(mapKey)
|
|
254
264
|
this.roomObjects.delete(mapKey)
|
|
265
|
+
this.roomUsers.delete(mapKey)
|
|
255
266
|
this.lastFlagsString.delete(mapKey)
|
|
256
267
|
this.logger.log('unsubscribe', room, shard, '(last ref)', 'active:', this.activeRooms())
|
|
257
268
|
} else {
|
package/src/types/events.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
Badge,
|
|
2
3
|
ConsoleMessage,
|
|
3
4
|
CpuStats,
|
|
4
5
|
RoomMap2Data,
|
|
@@ -20,7 +21,7 @@ export interface RoomStoreEvents {
|
|
|
20
21
|
objects: RoomObjectMap;
|
|
21
22
|
diff: RoomObjectDiff;
|
|
22
23
|
visual: string;
|
|
23
|
-
users?: Record<string, { _id: string; username: string }>
|
|
24
|
+
users?: Record<string, { _id: string; username: string; badge?: Badge }>
|
|
24
25
|
}
|
|
25
26
|
'room:terrainavailable': { room: string; shard: string | null; terrain: RoomTerrain }
|
|
26
27
|
'room:error': { room: string; shard: string | null; message: string }
|