dredless 0.6.3 → 1.0.0
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/README.md +22 -57
- package/dist/browser/dredless.global.js +827 -1058
- package/dist/browser/dredless.global.js.map +4 -4
- package/docs/ACCOUNT.md +146 -0
- package/docs/API_REFERENCE.md +63 -2550
- package/docs/BROWSER_API.md +151 -70
- package/docs/CLIENT.md +183 -0
- package/docs/EVENTS.md +136 -0
- package/docs/GETTING_STARTED.md +135 -0
- package/docs/MACHINES.md +351 -0
- package/docs/MANAGEMENT.md +285 -0
- package/docs/NODE_API.md +120 -318
- package/docs/PLAYER_INVENTORY.md +235 -0
- package/docs/PROTOCOL.md +227 -0
- package/docs/README.md +44 -0
- package/docs/TROUBLESHOOTING.md +91 -0
- package/docs/WORLD_STATE.md +183 -0
- package/package.json +1 -2
- package/src/browser.d.ts +31 -1
- package/src/browser.js +10 -0
- package/src/client/core.js +302 -15
- package/src/client/domains/index.js +57 -7
- package/src/client/index.d.ts +1 -1
- package/src/index.d.ts +303 -14
- package/src/index.js +10 -0
- package/src/network/account.js +182 -0
- package/src/network/disconnect.d.ts +2 -0
- package/src/network/disconnect.js +53 -0
- package/src/network/index.d.ts +1 -0
- package/src/network/index.js +1 -0
- package/src/network/invites.d.ts +0 -3
- package/src/network/invites.js +3 -40
- package/src/network/session.d.ts +2 -0
- package/src/network/session.js +21 -0
- package/src/protocol/outbound/ship-management.d.ts +66 -1
- package/src/protocol/outbound/ship-management.js +83 -0
- package/src/state/model/decoder.js +101 -56
- package/src/state/model/index.d.ts +4 -0
- package/src/state/model/loader-config.js +0 -997
- package/src/state/model/state.js +28 -80
- package/src/state/world/index.js +27 -1
|
@@ -2,8 +2,7 @@ import {decoder} from "../../constants.js";
|
|
|
2
2
|
import {
|
|
3
3
|
LOADER_FILTER_MODE_NAMES,
|
|
4
4
|
LOADER_POSITION_NAMES,
|
|
5
|
-
LOADER_PRIORITY_NAMES
|
|
6
|
-
LoaderConfigTracker
|
|
5
|
+
LOADER_PRIORITY_NAMES
|
|
7
6
|
} from "./loader-config.js";
|
|
8
7
|
import {navigationDestinationFromEncodedValue, navigationZoneFromBaseId} from "../overworld.js";
|
|
9
8
|
import {maybeSolveGeneratorMazeSeed} from "../generator-maze.js";
|
|
@@ -254,6 +253,7 @@ export const MODEL_TABLE_SPECS = new Map([
|
|
|
254
253
|
}],
|
|
255
254
|
[55, {
|
|
256
255
|
name: "player",
|
|
256
|
+
defaults: {q20: 1, q60: 10, q64: 10, q68: 10},
|
|
257
257
|
packedBeforeValues: true,
|
|
258
258
|
fields: numericFields({
|
|
259
259
|
256: 20, 512: 24, 1024: 28, 2048: 32, 4096: 36, 8192: 40,
|
|
@@ -274,8 +274,10 @@ export const MODEL_TABLE_SPECS = new Map([
|
|
|
274
274
|
]
|
|
275
275
|
}],
|
|
276
276
|
[56, {
|
|
277
|
-
name: "
|
|
278
|
-
|
|
277
|
+
name: "player_appearance",
|
|
278
|
+
// Official StateOutfit: four body colors, then four color/style pairs.
|
|
279
|
+
defaults: {q20: 0, q24: 0, q28: 0, q32: 0, q36: 0, q40: 0, q44: 0, q48: 0, q52: 0, q56: 0, q60: 0, q64: 0},
|
|
280
|
+
fields: numericFields({1: 20, 2: 24, 4: 28, 8: 32, 16: 36, 32: 40, 64: 44, 128: 48, 256: 52, 512: 56, 1024: 60, 2048: 64})
|
|
279
281
|
}],
|
|
280
282
|
[59, {name: "numeric_sparse", fields: numericFields({1: 20, 2: 24, 4: 28, 8: 32, 16: 36})}],
|
|
281
283
|
[60, {name: "fluid_tank", packedBits: [{bit: 4, offset: 29}], ...TWO_FIELD_SPEC}],
|
|
@@ -284,7 +286,11 @@ export const MODEL_TABLE_SPECS = new Map([
|
|
|
284
286
|
[63, {name: "numeric_pair", ...TWO_FIELD_SPEC}],
|
|
285
287
|
[67, {name: "numeric_single", fields: numericFields({1: 20})}],
|
|
286
288
|
[69, {
|
|
287
|
-
name: "
|
|
289
|
+
name: "loader_config",
|
|
290
|
+
// The official loader component starts with these values. Wire section
|
|
291
|
+
// 162 sends a packed absolute flag word before the five numeric deltas.
|
|
292
|
+
defaults: {q20: 3, q24: 4, q28: 1, q32: 16, q36: 20, q41: 0, q42: 0},
|
|
293
|
+
packedBeforeValues: true,
|
|
288
294
|
packedBits: [{bit: 32, offset: 41}, {bit: 64, offset: 42}],
|
|
289
295
|
fields: numericFields({1: 20, 2: 24, 4: 28, 8: 32, 16: 36})
|
|
290
296
|
}],
|
|
@@ -407,7 +413,7 @@ export const WIRE_TAG_TABLES = new Map([
|
|
|
407
413
|
[158, 75],
|
|
408
414
|
[160, 76],
|
|
409
415
|
[161, 77],
|
|
410
|
-
[162,
|
|
416
|
+
[162, 69], // Loader configuration; distinct from navigation state (168).
|
|
411
417
|
[163, 72],
|
|
412
418
|
[164, 73],
|
|
413
419
|
[165, 74],
|
|
@@ -574,32 +580,6 @@ export function firstRecord(records, predicate = null) {
|
|
|
574
580
|
return null;
|
|
575
581
|
}
|
|
576
582
|
|
|
577
|
-
export const TABLE_78_FIELD_BITS = [
|
|
578
|
-
["q20", 1],
|
|
579
|
-
["q24", 2],
|
|
580
|
-
["q28", 4],
|
|
581
|
-
["q32", 8],
|
|
582
|
-
["q36", 16],
|
|
583
|
-
["q40", 32],
|
|
584
|
-
["q44", 64]
|
|
585
|
-
];
|
|
586
|
-
|
|
587
|
-
export function table78DeltaRecord(changed) {
|
|
588
|
-
const record = {lastMask: changed.mask};
|
|
589
|
-
for (const [field, bit] of TABLE_78_FIELD_BITS) {
|
|
590
|
-
if (!(changed.mask & bit)) continue;
|
|
591
|
-
if (field === "q44" && ((changed.mask & 32) || changed.record?.q44 == null)) continue;
|
|
592
|
-
record[field] = (changed.record?.[field] ?? 0) - (changed.previous?.[field] ?? 0);
|
|
593
|
-
}
|
|
594
|
-
return record;
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
export function isSemanticLoaderDelta(changed, full) {
|
|
598
|
-
return !full &&
|
|
599
|
-
changed.previous != null &&
|
|
600
|
-
(changed.mask & 96) === 96;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
583
|
export function decodeText(blob) {
|
|
604
584
|
if (!(blob instanceof Uint8Array)) return null;
|
|
605
585
|
try {
|
|
@@ -679,10 +659,18 @@ export function summarizeCargoEjector(entity, typeId, record) {
|
|
|
679
659
|
};
|
|
680
660
|
}
|
|
681
661
|
|
|
682
|
-
export function summarizeCannon(entity, record, typeId = null) {
|
|
662
|
+
export function summarizeCannon(entity, record, typeId = null, transformRecord = null) {
|
|
683
663
|
if (!record || !CANNON_TYPE_IDS.has(typeId)) return null;
|
|
664
|
+
const relativeAngleRadians = numberOrNull(record.q32, 200);
|
|
665
|
+
const mountingAngleRadians = numberOrNull(transformRecord?.q28, 127.324);
|
|
666
|
+
const tau = 2 * Math.PI;
|
|
667
|
+
const absoluteAngleRadians = relativeAngleRadians == null || mountingAngleRadians == null
|
|
668
|
+
? null
|
|
669
|
+
: (((mountingAngleRadians + relativeAngleRadians) % tau) + tau) % tau;
|
|
684
670
|
const ammoItemId = record.q24 == null ? null : CANNON_AMMO_COLOR_ITEM_IDS.get(record.q24) ?? null;
|
|
685
|
-
const
|
|
671
|
+
const indicatorValue = (record.q40 ?? 0) / 50;
|
|
672
|
+
const indicatorVisible = indicatorValue >= 1;
|
|
673
|
+
const indicatorBlinking = indicatorValue === 4;
|
|
686
674
|
const recoil = record.q36 ?? null;
|
|
687
675
|
const recoil2 = record.q48 ?? null;
|
|
688
676
|
return {
|
|
@@ -692,18 +680,29 @@ export function summarizeCannon(entity, record, typeId = null) {
|
|
|
692
680
|
ammoItemId,
|
|
693
681
|
ammoName: entityNameFromType(ammoItemId),
|
|
694
682
|
ammoCount: record.q28 ?? 0,
|
|
695
|
-
|
|
683
|
+
relativeAngleRadians,
|
|
684
|
+
absoluteAngleRadians,
|
|
696
685
|
recoil,
|
|
697
686
|
recoil2,
|
|
698
687
|
recoils: [recoil, recoil2],
|
|
699
|
-
|
|
700
|
-
|
|
688
|
+
indicatorValue,
|
|
689
|
+
indicatorVisible,
|
|
690
|
+
indicatorColor: !indicatorVisible ? null : indicatorBlinking ? 0xff0000 : cannonIndicatorColor(indicatorValue),
|
|
691
|
+
indicatorBlinking,
|
|
692
|
+
indicatorBlinkColor: indicatorBlinking ? 0xffff00 : null,
|
|
701
693
|
spin: record.q52 ?? null,
|
|
702
|
-
|
|
694
|
+
resourceCount: record.q56 ?? 0,
|
|
703
695
|
state: cloneRecord(record)
|
|
704
696
|
};
|
|
705
697
|
}
|
|
706
698
|
|
|
699
|
+
function cannonIndicatorColor(value) {
|
|
700
|
+
// Match the official renderer's float32 arithmetic before truncating to a byte.
|
|
701
|
+
const distance = Math.abs(Math.fround(2 - Math.fround(value)));
|
|
702
|
+
const channel = Math.max(0, Math.min(255, Math.trunc(Math.fround(Math.fround(1 - distance) * 255))));
|
|
703
|
+
return value < 2 ? (channel << 16) | 0x00ff00 : 0xff0000 | (channel << 8);
|
|
704
|
+
}
|
|
705
|
+
|
|
707
706
|
export function summarizeThruster(entity, typeId, record) {
|
|
708
707
|
if (!THRUSTER_TYPE_IDS.has(typeId)) return null;
|
|
709
708
|
const facing = Number.isFinite(Number(record?.q20)) ? Number(record.q20) : 0;
|
|
@@ -1142,15 +1141,14 @@ export function summarizeHugeThruster(entity, markerRecord, sizeRecord = null) {
|
|
|
1142
1141
|
};
|
|
1143
1142
|
}
|
|
1144
1143
|
|
|
1145
|
-
export function summarizeLoader(entity, loaderRecord, loaderFilterRecord = null, filterSlotsRecord = null,
|
|
1144
|
+
export function summarizeLoader(entity, loaderRecord, loaderFilterRecord = null, filterSlotsRecord = null, includeDefaults = false, itemHolderRecord = null) {
|
|
1146
1145
|
if (!loaderRecord && !loaderFilterRecord && !includeDefaults) return null;
|
|
1147
|
-
|
|
1148
|
-
const
|
|
1149
|
-
const
|
|
1150
|
-
const
|
|
1151
|
-
const
|
|
1152
|
-
const
|
|
1153
|
-
const filterMode = config.filterMode ?? null;
|
|
1146
|
+
// Match the official component defaults, then apply the already decoded state.
|
|
1147
|
+
const loader = {...MODEL_TABLE_SPECS.get(69).defaults, ...loaderRecord};
|
|
1148
|
+
const pick = loader.q20;
|
|
1149
|
+
const place = loader.q24;
|
|
1150
|
+
const priority = loader.q28 - 1;
|
|
1151
|
+
const filterMode = loaderFilterRecord?.q20 ?? 0;
|
|
1154
1152
|
const heldItem = itemSummary(itemHolderRecord?.q20, itemHolderRecord?.q24 ?? null);
|
|
1155
1153
|
return {
|
|
1156
1154
|
entity,
|
|
@@ -1160,13 +1158,15 @@ export function summarizeLoader(entity, loaderRecord, loaderFilterRecord = null,
|
|
|
1160
1158
|
placeName: enumValueName(LOADER_POSITION_NAMES, place),
|
|
1161
1159
|
priority,
|
|
1162
1160
|
priorityName: enumValueName(LOADER_PRIORITY_NAMES, priority),
|
|
1163
|
-
requireOutput:
|
|
1164
|
-
waitForStack:
|
|
1165
|
-
stack:
|
|
1166
|
-
cycle:
|
|
1161
|
+
requireOutput: Boolean(loader.q41),
|
|
1162
|
+
waitForStack: Boolean(loader.q42),
|
|
1163
|
+
stack: loader.q32,
|
|
1164
|
+
cycle: loader.q36 / 20,
|
|
1167
1165
|
filterMode,
|
|
1168
1166
|
filterModeName: enumValueName(LOADER_FILTER_MODE_NAMES, filterMode),
|
|
1169
|
-
filterSlots:
|
|
1167
|
+
filterSlots: filterSlotsRecord
|
|
1168
|
+
? [filterSlotsRecord.q20, filterSlotsRecord.q24, filterSlotsRecord.q28].map(value => value || null)
|
|
1169
|
+
: null,
|
|
1170
1170
|
heldItemId: heldItem.itemId,
|
|
1171
1171
|
heldItemName: heldItem.itemName,
|
|
1172
1172
|
heldCount: heldItem.count,
|
|
@@ -1206,7 +1206,7 @@ export function summarizePusher(entity, pusherRecord, filterSlotsRecord = null)
|
|
|
1206
1206
|
filteredModeName: enumValueName(PUSHER_MODE_NAMES, filteredMode),
|
|
1207
1207
|
angle: numberOrNull(pusherRecord.q28, 10),
|
|
1208
1208
|
speed: pusherRecord.q32 == null ? 20 : (pusherRecord.q32 / 100) + 20,
|
|
1209
|
-
length:
|
|
1209
|
+
length: 1000 + (pusherRecord.q40 ?? 0) / 10,
|
|
1210
1210
|
filterInventory: pusherRecord.q36 === 1,
|
|
1211
1211
|
filterSlots: filterSlotsRecord ? [filterSlotsRecord.q20 ?? null, filterSlotsRecord.q24 ?? null, filterSlotsRecord.q28 ?? null] : null,
|
|
1212
1212
|
state: cloneRecord(pusherRecord),
|
|
@@ -1372,7 +1372,37 @@ export function summarizePlayerPreview(entity, record, blueprintItems = []) {
|
|
|
1372
1372
|
};
|
|
1373
1373
|
}
|
|
1374
1374
|
|
|
1375
|
-
|
|
1375
|
+
function summarizePlayerAppearance(record, player) {
|
|
1376
|
+
if (!record) return null;
|
|
1377
|
+
const style = (colorOffset, styleOffset) => ({
|
|
1378
|
+
color: record[qKey(colorOffset)] ?? 0,
|
|
1379
|
+
styleId: record[qKey(styleOffset)] ?? 0
|
|
1380
|
+
});
|
|
1381
|
+
const override = (offset) => {
|
|
1382
|
+
const id = player[qKey(offset)] ?? 0;
|
|
1383
|
+
return id === 0 ? null : {itemId: id, itemName: entityNameFromType(id)};
|
|
1384
|
+
};
|
|
1385
|
+
return {
|
|
1386
|
+
bodyColor: record.q20 ?? 0,
|
|
1387
|
+
legsColor: record.q24 ?? 0,
|
|
1388
|
+
skinColor: record.q28 ?? 0,
|
|
1389
|
+
feetColor: record.q32 ?? 0,
|
|
1390
|
+
hair: style(36, 40),
|
|
1391
|
+
hat: style(44, 48),
|
|
1392
|
+
face1: style(52, 56),
|
|
1393
|
+
face2: style(60, 64),
|
|
1394
|
+
itemOverrides: {
|
|
1395
|
+
back: override(36),
|
|
1396
|
+
hands: override(40),
|
|
1397
|
+
feet: override(44),
|
|
1398
|
+
head1: override(48),
|
|
1399
|
+
head2: override(52)
|
|
1400
|
+
},
|
|
1401
|
+
state: cloneRecord(record)
|
|
1402
|
+
};
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
export function summarizePlayer(entity, record, previewRecord = null, blueprintItems = [], appearanceRecord = null, localPlayerEntity = null) {
|
|
1376
1406
|
if (!record) return null;
|
|
1377
1407
|
const rawHeldItemId = record.q28 == null ? null : Number(record.q28);
|
|
1378
1408
|
const heldItemId = Number.isFinite(rawHeldItemId) && rawHeldItemId !== 0 ? rawHeldItemId : null;
|
|
@@ -1383,14 +1413,29 @@ export function summarizePlayer(entity, record, previewRecord = null, blueprintI
|
|
|
1383
1413
|
name: decodeText(record.blob92),
|
|
1384
1414
|
heldItemId,
|
|
1385
1415
|
heldItemName: entityNameFromType(heldItemId),
|
|
1386
|
-
|
|
1387
|
-
|
|
1416
|
+
itemMode: record.q32 ?? null,
|
|
1417
|
+
buildMultiplier: numberOrNull(record.q68, 10),
|
|
1418
|
+
heldItemBeamDistance: numberOrNull(record.q56, 10),
|
|
1419
|
+
heldItemAngle: numberOrNull(record.q80, 200),
|
|
1388
1420
|
shipRank: PLAYER_SHIP_RANKS.get(teamRank) || null,
|
|
1389
1421
|
patronTier: patronTierName(gameRank),
|
|
1390
1422
|
isDeveloper: gameRank === 1,
|
|
1391
1423
|
isPatron: gameRank >= 2 && gameRank <= 6,
|
|
1392
|
-
|
|
1424
|
+
isLocal: entity === localPlayerEntity,
|
|
1425
|
+
zoomedOut: Boolean(record.q107),
|
|
1393
1426
|
muted: Boolean(record.q112),
|
|
1427
|
+
invisible: Boolean(record.q106),
|
|
1428
|
+
cheats: {
|
|
1429
|
+
noclip: Boolean(record.q105),
|
|
1430
|
+
sandboxBuild: Boolean(record.q108)
|
|
1431
|
+
},
|
|
1432
|
+
itemEffectActive: Boolean(record.q111),
|
|
1433
|
+
animation: {
|
|
1434
|
+
facing: record.q20 ?? 1,
|
|
1435
|
+
orientation: record.q24 ?? 0,
|
|
1436
|
+
walking: Boolean(record.q109)
|
|
1437
|
+
},
|
|
1438
|
+
appearance: summarizePlayerAppearance(appearanceRecord, record),
|
|
1394
1439
|
actionPreview: summarizePlayerPreview(entity, previewRecord, blueprintItems),
|
|
1395
1440
|
state: cloneRecord(record)
|
|
1396
1441
|
};
|
|
@@ -25,6 +25,10 @@ export {
|
|
|
25
25
|
type ShieldGeneratorSummary,
|
|
26
26
|
type HelmSummary,
|
|
27
27
|
type PlayerSummary,
|
|
28
|
+
type PlayerAnimationSummary,
|
|
29
|
+
type PlayerAppearanceSummary,
|
|
30
|
+
type PlayerAppearanceStyleSummary,
|
|
31
|
+
type PlayerAppearanceItemSummary,
|
|
28
32
|
type ShipControlSummary,
|
|
29
33
|
type ShipWorldMetadataSummary,
|
|
30
34
|
type ShipShieldSummary,
|