dredless 0.6.1 → 0.6.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/README.md +14 -0
- package/dist/browser/dredless.global.js +372 -178
- package/dist/browser/dredless.global.js.map +4 -4
- package/docs/API_REFERENCE.md +76 -0
- package/docs/NODE_API.md +20 -1
- package/package.json +2 -1
- package/src/browser.d.ts +3 -0
- package/src/client/core.js +3 -0
- package/src/client/domains/index.js +12 -0
- package/src/client/domains/tips.js +153 -0
- package/src/client/index.d.ts +1 -1
- package/src/index.d.ts +38 -2
- package/src/network/invites.d.ts +6 -2
- package/src/network/invites.js +43 -6
package/README.md
CHANGED
|
@@ -37,6 +37,20 @@ const ship = client.currentShip();
|
|
|
37
37
|
const loader = ship?.machines.loaders()[0];
|
|
38
38
|
loader?.configure({ cycle: 5, stack: 12 });
|
|
39
39
|
|
|
40
|
+
const door = ship?.machines.doors()[0];
|
|
41
|
+
if (door) await door.setRank("crew");
|
|
42
|
+
const spawn = ship?.machines.spawnPoints()[0];
|
|
43
|
+
if (spawn) await spawn.setRank("captain");
|
|
44
|
+
|
|
45
|
+
// Other context-menu actions can be selected by their displayed text.
|
|
46
|
+
const cannon = ship?.machines.cannons()[0];
|
|
47
|
+
if (cannon) await client.tips.choose(cannon, "Unload Cannon");
|
|
48
|
+
|
|
49
|
+
if (door) {
|
|
50
|
+
console.log((await door.openTips()).options);
|
|
51
|
+
await client.tips.close();
|
|
52
|
+
}
|
|
53
|
+
|
|
40
54
|
const sign = ship?.machines.signs()[0];
|
|
41
55
|
sign?.open();
|
|
42
56
|
sign?.setText("Dock here", SignDisplayMode.WhenNear);
|
|
@@ -588,6 +588,367 @@
|
|
|
588
588
|
}
|
|
589
589
|
};
|
|
590
590
|
|
|
591
|
+
// src/protocol/outbound/ship-management.js
|
|
592
|
+
var SHIP_MANAGEMENT_TYPE = 4;
|
|
593
|
+
var SET_PRIVACY_ACTION = "set_privacy";
|
|
594
|
+
var STARTER_RECOVERY_ACTION = "starter_recovery";
|
|
595
|
+
var PLAYER_LIST_ACTION = "player_list";
|
|
596
|
+
var INVITE_RESET_ACTION = "invite_reset";
|
|
597
|
+
var SET_RANK_ACTION = "set_rank";
|
|
598
|
+
var KICK_ACTION = "kick";
|
|
599
|
+
var BAN_ACTION = "ban";
|
|
600
|
+
var DEMOTE_SELF_ACTION = "demote_self";
|
|
601
|
+
function requireNonNegativeInteger(value, name) {
|
|
602
|
+
const number = Number(value);
|
|
603
|
+
if (!Number.isInteger(number) || number < 0) throw new RangeError(`${name} must be a non-negative integer`);
|
|
604
|
+
return number;
|
|
605
|
+
}
|
|
606
|
+
function normalizePrivacy(value) {
|
|
607
|
+
if (value === true || value === 1 || value === "private") return 1;
|
|
608
|
+
if (value === false || value === 0 || value === "public") return 0;
|
|
609
|
+
throw new RangeError(`privacy must be 0, 1, true, false, "public", or "private"`);
|
|
610
|
+
}
|
|
611
|
+
function normalizePlayerRef(value) {
|
|
612
|
+
return requireNonNegativeInteger(value, "refId");
|
|
613
|
+
}
|
|
614
|
+
function normalizePlayerRank(value) {
|
|
615
|
+
if (value === 0 || value === "guest") return 0;
|
|
616
|
+
if (value === 1 || value === "crew") return 1;
|
|
617
|
+
if (value === 3 || value === "captain") return 3;
|
|
618
|
+
throw new RangeError(`rank must be 0, 1, 3, "guest", "crew", or "captain"`);
|
|
619
|
+
}
|
|
620
|
+
function buildShipManagementMessage(act, arg = null, extra = null) {
|
|
621
|
+
if (typeof act !== "string" || !act) throw new TypeError(`act must be a non-empty string`);
|
|
622
|
+
const message = { type: SHIP_MANAGEMENT_TYPE, act, arg };
|
|
623
|
+
if (extra && typeof extra === "object") Object.assign(message, extra);
|
|
624
|
+
return message;
|
|
625
|
+
}
|
|
626
|
+
function buildShipPrivacyMessage(privacy) {
|
|
627
|
+
return buildShipManagementMessage(SET_PRIVACY_ACTION, normalizePrivacy(privacy));
|
|
628
|
+
}
|
|
629
|
+
function buildStarterRecoveryMessage(itemId) {
|
|
630
|
+
return buildShipManagementMessage(STARTER_RECOVERY_ACTION, requireNonNegativeInteger(itemId, "itemId"));
|
|
631
|
+
}
|
|
632
|
+
function buildPlayerListMessage() {
|
|
633
|
+
return buildShipManagementMessage(PLAYER_LIST_ACTION);
|
|
634
|
+
}
|
|
635
|
+
function buildInviteResetMessage() {
|
|
636
|
+
return buildShipManagementMessage(INVITE_RESET_ACTION);
|
|
637
|
+
}
|
|
638
|
+
function buildSetPlayerRankMessage(refId, rank) {
|
|
639
|
+
return buildShipManagementMessage(SET_RANK_ACTION, normalizePlayerRef(refId), { rank: normalizePlayerRank(rank) });
|
|
640
|
+
}
|
|
641
|
+
function buildKickPlayerMessage(refId) {
|
|
642
|
+
return buildShipManagementMessage(KICK_ACTION, normalizePlayerRef(refId));
|
|
643
|
+
}
|
|
644
|
+
function buildBanPlayerMessage(refId) {
|
|
645
|
+
return buildShipManagementMessage(BAN_ACTION, normalizePlayerRef(refId));
|
|
646
|
+
}
|
|
647
|
+
function buildDemoteSelfMessage() {
|
|
648
|
+
return buildShipManagementMessage(DEMOTE_SELF_ACTION);
|
|
649
|
+
}
|
|
650
|
+
function normalizeShipConfig(event = {}) {
|
|
651
|
+
const source = event && typeof event === "object" ? event : {};
|
|
652
|
+
const config = source.config && typeof source.config === "object" ? source.config : {};
|
|
653
|
+
return {
|
|
654
|
+
privacy: config.privacy ?? null,
|
|
655
|
+
privacyName: config.privacy === 0 ? "public" : config.privacy === 1 ? "private" : null,
|
|
656
|
+
inviteKey: config.invite_key ?? null,
|
|
657
|
+
teamId: source.team_id ?? null,
|
|
658
|
+
patronPerks: Array.isArray(source.patron_perks) ? source.patron_perks.slice() : []
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
function normalizeCaptainSubrank(event = {}) {
|
|
662
|
+
const source = event && typeof event === "object" ? event : {};
|
|
663
|
+
return {
|
|
664
|
+
subrank: source.subrank ?? null,
|
|
665
|
+
enableCheats: Boolean(source.enable_cheats)
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function normalizeShipPlayerList(event = {}, previous = null, currentCaptainSubrank = null) {
|
|
669
|
+
const source = event && typeof event === "object" ? event : {};
|
|
670
|
+
const changes = Array.isArray(source.player_list) ? source.player_list.map(normalizePlayerListChange) : [];
|
|
671
|
+
const removedPlayers = changes.filter((change) => change.remove && Number.isFinite(Number(change.refId))).map((change) => Number(change.refId));
|
|
672
|
+
const changedPlayers = changes.filter((change) => !change.remove).map(({ remove: _remove, ...change }) => change);
|
|
673
|
+
const players = mergePlayerListChanges(previous?.players, changes);
|
|
674
|
+
const ownerCaptainRank = ownerRankForPlayers(players);
|
|
675
|
+
applyPlayerControlState(players, currentCaptainSubrank);
|
|
676
|
+
applyPlayerControlState(changedPlayers, currentCaptainSubrank);
|
|
677
|
+
return {
|
|
678
|
+
ownerCaptainRank,
|
|
679
|
+
shipOwners: players.filter((player) => player.isShipOwner),
|
|
680
|
+
players,
|
|
681
|
+
changes: changedPlayers,
|
|
682
|
+
removedPlayers
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
function mergePlayerListChanges(previousPlayers, changes) {
|
|
686
|
+
if (!Array.isArray(previousPlayers) || !previousPlayers.length) return changes.filter((change) => !change.remove).map(({
|
|
687
|
+
remove: _remove,
|
|
688
|
+
...player
|
|
689
|
+
}) => ({ ...player }));
|
|
690
|
+
const existing = /* @__PURE__ */ new Map();
|
|
691
|
+
for (const player of previousPlayers) {
|
|
692
|
+
if (player?.refId == null) continue;
|
|
693
|
+
existing.set(player.refId, { ...player, isShipOwner: false });
|
|
694
|
+
}
|
|
695
|
+
const changedRefs = /* @__PURE__ */ new Set();
|
|
696
|
+
const merged = [];
|
|
697
|
+
for (const change of changes) {
|
|
698
|
+
if (change.refId == null) {
|
|
699
|
+
if (!change.remove) {
|
|
700
|
+
const { remove: _remove2, ...player2 } = change;
|
|
701
|
+
merged.push({ ...player2, isShipOwner: false });
|
|
702
|
+
}
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
changedRefs.add(change.refId);
|
|
706
|
+
if (change.remove) {
|
|
707
|
+
existing.delete(change.refId);
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
const { remove: _remove, ...changePlayer } = change;
|
|
711
|
+
const player = { ...existing.get(change.refId) || {}, ...changePlayer, isShipOwner: false };
|
|
712
|
+
existing.set(change.refId, player);
|
|
713
|
+
merged.push(player);
|
|
714
|
+
}
|
|
715
|
+
for (const player of previousPlayers) {
|
|
716
|
+
if (player?.refId == null || changedRefs.has(player.refId)) continue;
|
|
717
|
+
const retained = existing.get(player.refId);
|
|
718
|
+
if (retained) merged.push(retained);
|
|
719
|
+
}
|
|
720
|
+
return merged;
|
|
721
|
+
}
|
|
722
|
+
function normalizePlayerListChange(entry = {}) {
|
|
723
|
+
const source = entry && typeof entry === "object" ? entry : {};
|
|
724
|
+
if (source._removed) return { refId: source.ref_id ?? null, remove: true };
|
|
725
|
+
return {
|
|
726
|
+
refId: source.ref_id ?? null,
|
|
727
|
+
remove: false,
|
|
728
|
+
discrim: source.discrim ?? null,
|
|
729
|
+
discrimColor: source.discrim_color ?? null,
|
|
730
|
+
teamRank: source.team_rank ?? null,
|
|
731
|
+
captainRank: source.captain_rank ?? null,
|
|
732
|
+
isCaptain: Number(source.captain_rank) > 0,
|
|
733
|
+
isShipOwner: false,
|
|
734
|
+
canBeManaged: false,
|
|
735
|
+
time: source.time ?? null,
|
|
736
|
+
items: Array.isArray(source.items) ? source.items.slice() : [],
|
|
737
|
+
aliasDiscrims: Array.isArray(source.alias_discrims) ? source.alias_discrims.slice() : [],
|
|
738
|
+
extraAliasCount: normalizeNonNegativeNumber(source.extra_aliases),
|
|
739
|
+
onlineCount: source.online_count ?? null
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
function normalizeNonNegativeNumber(value) {
|
|
743
|
+
const number = Number(value);
|
|
744
|
+
return Number.isFinite(number) && number > 0 ? number : 0;
|
|
745
|
+
}
|
|
746
|
+
function ownerRankForPlayers(players) {
|
|
747
|
+
const ranks = players.filter((player) => Number(player.captainRank) > 0).map((player) => Number(player.captainRank)).filter(Number.isFinite);
|
|
748
|
+
if (!ranks.length) return null;
|
|
749
|
+
const ownerRank = Math.min(...ranks);
|
|
750
|
+
for (const player of players) player.isShipOwner = Number(player.captainRank) === ownerRank;
|
|
751
|
+
return ownerRank;
|
|
752
|
+
}
|
|
753
|
+
function applyPlayerControlState(players, currentCaptainSubrank) {
|
|
754
|
+
const currentRank = currentCaptainRank(currentCaptainSubrank);
|
|
755
|
+
for (const player of players) player.canBeManaged = playerCanBeManagedBy(player, currentRank);
|
|
756
|
+
}
|
|
757
|
+
function currentCaptainRank(currentCaptainSubrank) {
|
|
758
|
+
const rank = Number(currentCaptainSubrank?.subrank);
|
|
759
|
+
return Number.isFinite(rank) && rank > 0 ? rank : null;
|
|
760
|
+
}
|
|
761
|
+
function playerCanBeManagedBy(player, currentRank) {
|
|
762
|
+
if (!Number.isFinite(currentRank) || !player) return false;
|
|
763
|
+
const targetCaptainRank = Number(player.captainRank);
|
|
764
|
+
if (Number.isFinite(targetCaptainRank) && targetCaptainRank > 0) return currentRank < targetCaptainRank;
|
|
765
|
+
const targetTeamRank = Number(player.teamRank);
|
|
766
|
+
return targetTeamRank === 0 || targetTeamRank === 1;
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// src/client/domains/tips.js
|
|
770
|
+
function timeoutValue(options) {
|
|
771
|
+
const timeout = options.timeout ?? 5e3;
|
|
772
|
+
if (!Number.isFinite(timeout) || timeout <= 0) throw new RangeError("timeout must be positive milliseconds");
|
|
773
|
+
return timeout;
|
|
774
|
+
}
|
|
775
|
+
function waitFor(client, eventType, read, timeout, label, send = null) {
|
|
776
|
+
return new Promise((resolve, reject) => {
|
|
777
|
+
const cleanup = () => {
|
|
778
|
+
clearTimeout(timer);
|
|
779
|
+
client.off(eventType, check);
|
|
780
|
+
client.off("close", closed);
|
|
781
|
+
};
|
|
782
|
+
const check = (...args) => {
|
|
783
|
+
const value = read(...args);
|
|
784
|
+
if (value) {
|
|
785
|
+
cleanup();
|
|
786
|
+
resolve(value);
|
|
787
|
+
}
|
|
788
|
+
};
|
|
789
|
+
const closed = () => {
|
|
790
|
+
cleanup();
|
|
791
|
+
reject(new Error(`Disconnected while waiting for ${label}`));
|
|
792
|
+
};
|
|
793
|
+
const timer = setTimeout(() => {
|
|
794
|
+
cleanup();
|
|
795
|
+
reject(new Error(`Timed out waiting for ${label}`));
|
|
796
|
+
}, timeout);
|
|
797
|
+
client.on(eventType, check);
|
|
798
|
+
client.on("close", closed);
|
|
799
|
+
if (send) {
|
|
800
|
+
try {
|
|
801
|
+
send();
|
|
802
|
+
} catch (error) {
|
|
803
|
+
cleanup();
|
|
804
|
+
reject(error);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
check();
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
var TipDomain = class {
|
|
812
|
+
#busy = false;
|
|
813
|
+
#target = null;
|
|
814
|
+
#current = null;
|
|
815
|
+
constructor(client) {
|
|
816
|
+
this.client = client;
|
|
817
|
+
client.on("close", () => {
|
|
818
|
+
this.#current = null;
|
|
819
|
+
this.#target = null;
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
get current() {
|
|
823
|
+
return this.#current;
|
|
824
|
+
}
|
|
825
|
+
receive(event, world) {
|
|
826
|
+
const options = Array.isArray(event.options) ? event.options.map(String) : [];
|
|
827
|
+
const entity = this.#target?.world === world?.id ? this.#target.entity : null;
|
|
828
|
+
const menu = Object.freeze({
|
|
829
|
+
entity,
|
|
830
|
+
world: world?.id ?? null,
|
|
831
|
+
filter: event.filter ?? null,
|
|
832
|
+
x: event.x ?? 0,
|
|
833
|
+
y: event.y ?? 0,
|
|
834
|
+
options: Object.freeze(options)
|
|
835
|
+
});
|
|
836
|
+
this.#current = options.length ? menu : null;
|
|
837
|
+
this.client.emit("tip-list", menu, world);
|
|
838
|
+
}
|
|
839
|
+
async #exclusive(action) {
|
|
840
|
+
if (this.#busy) throw new Error("A tip menu operation is already in progress");
|
|
841
|
+
this.#busy = true;
|
|
842
|
+
try {
|
|
843
|
+
return await action();
|
|
844
|
+
} catch (error) {
|
|
845
|
+
this.#current = null;
|
|
846
|
+
this.#target = null;
|
|
847
|
+
throw error;
|
|
848
|
+
} finally {
|
|
849
|
+
this.#busy = false;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
async #open(entity, options) {
|
|
853
|
+
if (!this.client.ready || !this.client.connected) throw new Error("Client must be connected and ready to open tips");
|
|
854
|
+
const timeout = timeoutValue(options);
|
|
855
|
+
const handle = typeof entity === "number" ? this.client.currentShip()?.entities.get(entity) : entity;
|
|
856
|
+
if (!handle || handle.client !== this.client || !handle.exists()) throw new Error("Tip target must be an existing entity on this client");
|
|
857
|
+
const world = this.client.worlds.currentWorldId;
|
|
858
|
+
const scopeWorld = handle.scope === "ship" ? this.client.worlds.shipWorld() : handle.scope === "current" ? this.client.worlds.worlds.get(world) : handle.scope === "overworld" ? this.client.worlds.overworld() : this.client.worlds.worlds.get(Number(handle.scope));
|
|
859
|
+
if (scopeWorld?.id !== world) {
|
|
860
|
+
throw new Error("Tip target must be in the current world");
|
|
861
|
+
}
|
|
862
|
+
const position = handle.position;
|
|
863
|
+
if (!position) throw new Error("Tip target has no position");
|
|
864
|
+
this.#current = null;
|
|
865
|
+
this.#target = { entity: handle.id, world, command: {
|
|
866
|
+
...options.command,
|
|
867
|
+
focus_ent: handle.id,
|
|
868
|
+
mx: position.x,
|
|
869
|
+
my: position.y
|
|
870
|
+
} };
|
|
871
|
+
return waitFor(this.client, "tip-list", () => {
|
|
872
|
+
return this.#current?.entity === handle.id && this.#current.world === world ? this.#current : null;
|
|
873
|
+
}, timeout, "tip menu", () => {
|
|
874
|
+
this.client.send({ ...this.#target.command, act1: false, act2: true, tip_select: null });
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
open(entity, options = {}) {
|
|
878
|
+
return this.#exclusive(() => this.#open(entity, options));
|
|
879
|
+
}
|
|
880
|
+
close(options = {}) {
|
|
881
|
+
return this.#exclusive(async () => {
|
|
882
|
+
const timeout = timeoutValue(options);
|
|
883
|
+
const menu = this.#current;
|
|
884
|
+
if (!menu) return this.client;
|
|
885
|
+
if (!this.client.connected || !this.client.ready) throw new Error("Client must be connected and ready to close tips");
|
|
886
|
+
if (menu.world !== this.client.worlds.currentWorldId) throw new Error("Tip menu belongs to another world");
|
|
887
|
+
await waitFor(this.client, "tip-list", (update) => update?.world === menu.world && update.options.length === 0, timeout, "tip menu dismissal", () => {
|
|
888
|
+
this.client.send({
|
|
889
|
+
...options.command,
|
|
890
|
+
focus_ent: null,
|
|
891
|
+
config_ent: null,
|
|
892
|
+
act1: true,
|
|
893
|
+
act1_held: true,
|
|
894
|
+
act2: false,
|
|
895
|
+
tip_select: null
|
|
896
|
+
});
|
|
897
|
+
});
|
|
898
|
+
this.#target = null;
|
|
899
|
+
return this.client;
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
select(option, command = {}) {
|
|
903
|
+
if (this.#busy) throw new Error("A tip menu operation is already in progress");
|
|
904
|
+
return this.#select(option, command);
|
|
905
|
+
}
|
|
906
|
+
#select(option, command = {}) {
|
|
907
|
+
if (!this.client.connected || !this.client.ready) throw new Error("Client must be connected and ready to select tips");
|
|
908
|
+
const menu = this.#current;
|
|
909
|
+
if (!menu || menu.entity == null || !this.#target) throw new Error("Open an entity's tip menu before selecting an option");
|
|
910
|
+
if (menu.world !== this.client.worlds.currentWorldId) throw new Error("Tip menu belongs to another world");
|
|
911
|
+
const index = typeof option === "string" ? menu.options.indexOf(option) : option;
|
|
912
|
+
if (!Number.isInteger(index) || index < 0 || index >= menu.options.length) throw new RangeError("Tip option is not in the current menu");
|
|
913
|
+
this.client.send({
|
|
914
|
+
...this.#target.command,
|
|
915
|
+
...command,
|
|
916
|
+
focus_ent: menu.entity,
|
|
917
|
+
act1: false,
|
|
918
|
+
act2: false,
|
|
919
|
+
tip_select: index
|
|
920
|
+
});
|
|
921
|
+
this.#current = null;
|
|
922
|
+
this.#target = null;
|
|
923
|
+
return this.client;
|
|
924
|
+
}
|
|
925
|
+
choose(entity, option, options = {}) {
|
|
926
|
+
return this.#exclusive(async () => {
|
|
927
|
+
await this.#open(entity, options);
|
|
928
|
+
return this.#select(option);
|
|
929
|
+
});
|
|
930
|
+
}
|
|
931
|
+
setRank(handle, rank, options = {}) {
|
|
932
|
+
return this.#exclusive(async () => {
|
|
933
|
+
const value = normalizePlayerRank(rank);
|
|
934
|
+
const timeout = timeoutValue(options);
|
|
935
|
+
if (!handle.exists()) throw new Error("Rank target no longer exists");
|
|
936
|
+
if (handle.rank === value) return this.client;
|
|
937
|
+
const text = { 0: "Restrict to Guests", 1: "Restrict to Crew", 3: "Restrict to Captains" }[value];
|
|
938
|
+
await this.#open(handle, options);
|
|
939
|
+
await waitFor(
|
|
940
|
+
this.client,
|
|
941
|
+
"model",
|
|
942
|
+
() => handle.rank === value,
|
|
943
|
+
timeout,
|
|
944
|
+
"access rank update",
|
|
945
|
+
() => this.#select(text)
|
|
946
|
+
);
|
|
947
|
+
return this.client;
|
|
948
|
+
});
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
|
|
591
952
|
// src/client/core.js
|
|
592
953
|
init_cookies();
|
|
593
954
|
init_servers();
|
|
@@ -8433,184 +8794,6 @@
|
|
|
8433
8794
|
return number === 0 && count === 0 ? null : number;
|
|
8434
8795
|
}
|
|
8435
8796
|
|
|
8436
|
-
// src/protocol/outbound/ship-management.js
|
|
8437
|
-
var SHIP_MANAGEMENT_TYPE = 4;
|
|
8438
|
-
var SET_PRIVACY_ACTION = "set_privacy";
|
|
8439
|
-
var STARTER_RECOVERY_ACTION = "starter_recovery";
|
|
8440
|
-
var PLAYER_LIST_ACTION = "player_list";
|
|
8441
|
-
var INVITE_RESET_ACTION = "invite_reset";
|
|
8442
|
-
var SET_RANK_ACTION = "set_rank";
|
|
8443
|
-
var KICK_ACTION = "kick";
|
|
8444
|
-
var BAN_ACTION = "ban";
|
|
8445
|
-
var DEMOTE_SELF_ACTION = "demote_self";
|
|
8446
|
-
function requireNonNegativeInteger(value, name) {
|
|
8447
|
-
const number = Number(value);
|
|
8448
|
-
if (!Number.isInteger(number) || number < 0) throw new RangeError(`${name} must be a non-negative integer`);
|
|
8449
|
-
return number;
|
|
8450
|
-
}
|
|
8451
|
-
function normalizePrivacy(value) {
|
|
8452
|
-
if (value === true || value === 1 || value === "private") return 1;
|
|
8453
|
-
if (value === false || value === 0 || value === "public") return 0;
|
|
8454
|
-
throw new RangeError(`privacy must be 0, 1, true, false, "public", or "private"`);
|
|
8455
|
-
}
|
|
8456
|
-
function normalizePlayerRef(value) {
|
|
8457
|
-
return requireNonNegativeInteger(value, "refId");
|
|
8458
|
-
}
|
|
8459
|
-
function normalizePlayerRank(value) {
|
|
8460
|
-
if (value === 0 || value === "guest") return 0;
|
|
8461
|
-
if (value === 1 || value === "crew") return 1;
|
|
8462
|
-
if (value === 3 || value === "captain") return 3;
|
|
8463
|
-
throw new RangeError(`rank must be 0, 1, 3, "guest", "crew", or "captain"`);
|
|
8464
|
-
}
|
|
8465
|
-
function buildShipManagementMessage(act, arg = null, extra = null) {
|
|
8466
|
-
if (typeof act !== "string" || !act) throw new TypeError(`act must be a non-empty string`);
|
|
8467
|
-
const message = { type: SHIP_MANAGEMENT_TYPE, act, arg };
|
|
8468
|
-
if (extra && typeof extra === "object") Object.assign(message, extra);
|
|
8469
|
-
return message;
|
|
8470
|
-
}
|
|
8471
|
-
function buildShipPrivacyMessage(privacy) {
|
|
8472
|
-
return buildShipManagementMessage(SET_PRIVACY_ACTION, normalizePrivacy(privacy));
|
|
8473
|
-
}
|
|
8474
|
-
function buildStarterRecoveryMessage(itemId) {
|
|
8475
|
-
return buildShipManagementMessage(STARTER_RECOVERY_ACTION, requireNonNegativeInteger(itemId, "itemId"));
|
|
8476
|
-
}
|
|
8477
|
-
function buildPlayerListMessage() {
|
|
8478
|
-
return buildShipManagementMessage(PLAYER_LIST_ACTION);
|
|
8479
|
-
}
|
|
8480
|
-
function buildInviteResetMessage() {
|
|
8481
|
-
return buildShipManagementMessage(INVITE_RESET_ACTION);
|
|
8482
|
-
}
|
|
8483
|
-
function buildSetPlayerRankMessage(refId, rank) {
|
|
8484
|
-
return buildShipManagementMessage(SET_RANK_ACTION, normalizePlayerRef(refId), { rank: normalizePlayerRank(rank) });
|
|
8485
|
-
}
|
|
8486
|
-
function buildKickPlayerMessage(refId) {
|
|
8487
|
-
return buildShipManagementMessage(KICK_ACTION, normalizePlayerRef(refId));
|
|
8488
|
-
}
|
|
8489
|
-
function buildBanPlayerMessage(refId) {
|
|
8490
|
-
return buildShipManagementMessage(BAN_ACTION, normalizePlayerRef(refId));
|
|
8491
|
-
}
|
|
8492
|
-
function buildDemoteSelfMessage() {
|
|
8493
|
-
return buildShipManagementMessage(DEMOTE_SELF_ACTION);
|
|
8494
|
-
}
|
|
8495
|
-
function normalizeShipConfig(event = {}) {
|
|
8496
|
-
const source = event && typeof event === "object" ? event : {};
|
|
8497
|
-
const config = source.config && typeof source.config === "object" ? source.config : {};
|
|
8498
|
-
return {
|
|
8499
|
-
privacy: config.privacy ?? null,
|
|
8500
|
-
privacyName: config.privacy === 0 ? "public" : config.privacy === 1 ? "private" : null,
|
|
8501
|
-
inviteKey: config.invite_key ?? null,
|
|
8502
|
-
teamId: source.team_id ?? null,
|
|
8503
|
-
patronPerks: Array.isArray(source.patron_perks) ? source.patron_perks.slice() : []
|
|
8504
|
-
};
|
|
8505
|
-
}
|
|
8506
|
-
function normalizeCaptainSubrank(event = {}) {
|
|
8507
|
-
const source = event && typeof event === "object" ? event : {};
|
|
8508
|
-
return {
|
|
8509
|
-
subrank: source.subrank ?? null,
|
|
8510
|
-
enableCheats: Boolean(source.enable_cheats)
|
|
8511
|
-
};
|
|
8512
|
-
}
|
|
8513
|
-
function normalizeShipPlayerList(event = {}, previous = null, currentCaptainSubrank = null) {
|
|
8514
|
-
const source = event && typeof event === "object" ? event : {};
|
|
8515
|
-
const changes = Array.isArray(source.player_list) ? source.player_list.map(normalizePlayerListChange) : [];
|
|
8516
|
-
const removedPlayers = changes.filter((change) => change.remove && Number.isFinite(Number(change.refId))).map((change) => Number(change.refId));
|
|
8517
|
-
const changedPlayers = changes.filter((change) => !change.remove).map(({ remove: _remove, ...change }) => change);
|
|
8518
|
-
const players = mergePlayerListChanges(previous?.players, changes);
|
|
8519
|
-
const ownerCaptainRank = ownerRankForPlayers(players);
|
|
8520
|
-
applyPlayerControlState(players, currentCaptainSubrank);
|
|
8521
|
-
applyPlayerControlState(changedPlayers, currentCaptainSubrank);
|
|
8522
|
-
return {
|
|
8523
|
-
ownerCaptainRank,
|
|
8524
|
-
shipOwners: players.filter((player) => player.isShipOwner),
|
|
8525
|
-
players,
|
|
8526
|
-
changes: changedPlayers,
|
|
8527
|
-
removedPlayers
|
|
8528
|
-
};
|
|
8529
|
-
}
|
|
8530
|
-
function mergePlayerListChanges(previousPlayers, changes) {
|
|
8531
|
-
if (!Array.isArray(previousPlayers) || !previousPlayers.length) return changes.filter((change) => !change.remove).map(({
|
|
8532
|
-
remove: _remove,
|
|
8533
|
-
...player
|
|
8534
|
-
}) => ({ ...player }));
|
|
8535
|
-
const existing = /* @__PURE__ */ new Map();
|
|
8536
|
-
for (const player of previousPlayers) {
|
|
8537
|
-
if (player?.refId == null) continue;
|
|
8538
|
-
existing.set(player.refId, { ...player, isShipOwner: false });
|
|
8539
|
-
}
|
|
8540
|
-
const changedRefs = /* @__PURE__ */ new Set();
|
|
8541
|
-
const merged = [];
|
|
8542
|
-
for (const change of changes) {
|
|
8543
|
-
if (change.refId == null) {
|
|
8544
|
-
if (!change.remove) {
|
|
8545
|
-
const { remove: _remove2, ...player2 } = change;
|
|
8546
|
-
merged.push({ ...player2, isShipOwner: false });
|
|
8547
|
-
}
|
|
8548
|
-
continue;
|
|
8549
|
-
}
|
|
8550
|
-
changedRefs.add(change.refId);
|
|
8551
|
-
if (change.remove) {
|
|
8552
|
-
existing.delete(change.refId);
|
|
8553
|
-
continue;
|
|
8554
|
-
}
|
|
8555
|
-
const { remove: _remove, ...changePlayer } = change;
|
|
8556
|
-
const player = { ...existing.get(change.refId) || {}, ...changePlayer, isShipOwner: false };
|
|
8557
|
-
existing.set(change.refId, player);
|
|
8558
|
-
merged.push(player);
|
|
8559
|
-
}
|
|
8560
|
-
for (const player of previousPlayers) {
|
|
8561
|
-
if (player?.refId == null || changedRefs.has(player.refId)) continue;
|
|
8562
|
-
const retained = existing.get(player.refId);
|
|
8563
|
-
if (retained) merged.push(retained);
|
|
8564
|
-
}
|
|
8565
|
-
return merged;
|
|
8566
|
-
}
|
|
8567
|
-
function normalizePlayerListChange(entry = {}) {
|
|
8568
|
-
const source = entry && typeof entry === "object" ? entry : {};
|
|
8569
|
-
if (source._removed) return { refId: source.ref_id ?? null, remove: true };
|
|
8570
|
-
return {
|
|
8571
|
-
refId: source.ref_id ?? null,
|
|
8572
|
-
remove: false,
|
|
8573
|
-
discrim: source.discrim ?? null,
|
|
8574
|
-
discrimColor: source.discrim_color ?? null,
|
|
8575
|
-
teamRank: source.team_rank ?? null,
|
|
8576
|
-
captainRank: source.captain_rank ?? null,
|
|
8577
|
-
isCaptain: Number(source.captain_rank) > 0,
|
|
8578
|
-
isShipOwner: false,
|
|
8579
|
-
canBeManaged: false,
|
|
8580
|
-
time: source.time ?? null,
|
|
8581
|
-
items: Array.isArray(source.items) ? source.items.slice() : [],
|
|
8582
|
-
aliasDiscrims: Array.isArray(source.alias_discrims) ? source.alias_discrims.slice() : [],
|
|
8583
|
-
extraAliasCount: normalizeNonNegativeNumber(source.extra_aliases),
|
|
8584
|
-
onlineCount: source.online_count ?? null
|
|
8585
|
-
};
|
|
8586
|
-
}
|
|
8587
|
-
function normalizeNonNegativeNumber(value) {
|
|
8588
|
-
const number = Number(value);
|
|
8589
|
-
return Number.isFinite(number) && number > 0 ? number : 0;
|
|
8590
|
-
}
|
|
8591
|
-
function ownerRankForPlayers(players) {
|
|
8592
|
-
const ranks = players.filter((player) => Number(player.captainRank) > 0).map((player) => Number(player.captainRank)).filter(Number.isFinite);
|
|
8593
|
-
if (!ranks.length) return null;
|
|
8594
|
-
const ownerRank = Math.min(...ranks);
|
|
8595
|
-
for (const player of players) player.isShipOwner = Number(player.captainRank) === ownerRank;
|
|
8596
|
-
return ownerRank;
|
|
8597
|
-
}
|
|
8598
|
-
function applyPlayerControlState(players, currentCaptainSubrank) {
|
|
8599
|
-
const currentRank = currentCaptainRank(currentCaptainSubrank);
|
|
8600
|
-
for (const player of players) player.canBeManaged = playerCanBeManagedBy(player, currentRank);
|
|
8601
|
-
}
|
|
8602
|
-
function currentCaptainRank(currentCaptainSubrank) {
|
|
8603
|
-
const rank = Number(currentCaptainSubrank?.subrank);
|
|
8604
|
-
return Number.isFinite(rank) && rank > 0 ? rank : null;
|
|
8605
|
-
}
|
|
8606
|
-
function playerCanBeManagedBy(player, currentRank) {
|
|
8607
|
-
if (!Number.isFinite(currentRank) || !player) return false;
|
|
8608
|
-
const targetCaptainRank = Number(player.captainRank);
|
|
8609
|
-
if (Number.isFinite(targetCaptainRank) && targetCaptainRank > 0) return currentRank < targetCaptainRank;
|
|
8610
|
-
const targetTeamRank = Number(player.teamRank);
|
|
8611
|
-
return targetTeamRank === 0 || targetTeamRank === 1;
|
|
8612
|
-
}
|
|
8613
|
-
|
|
8614
8797
|
// src/protocol/outbound/sign.js
|
|
8615
8798
|
var SIGN_TEXT_COMMAND = "sign_text";
|
|
8616
8799
|
var SignDisplayMode = Object.freeze({
|
|
@@ -9800,6 +9983,9 @@
|
|
|
9800
9983
|
use(options = {}, command = {}) {
|
|
9801
9984
|
return this.client.player.useEntity(this.id, options, command);
|
|
9802
9985
|
}
|
|
9986
|
+
openTips(options = {}) {
|
|
9987
|
+
return this.client.tips.open(this, options);
|
|
9988
|
+
}
|
|
9803
9989
|
};
|
|
9804
9990
|
var MachineHandle = class extends EntityHandle {
|
|
9805
9991
|
open(options = {}, command = {}) {
|
|
@@ -10102,6 +10288,9 @@
|
|
|
10102
10288
|
}
|
|
10103
10289
|
};
|
|
10104
10290
|
var DoorHandle = class extends MachineHandle {
|
|
10291
|
+
setRank(rank, options = {}) {
|
|
10292
|
+
return this.client.tips.setRank(this, rank, options);
|
|
10293
|
+
}
|
|
10105
10294
|
get state() {
|
|
10106
10295
|
return summaryForEntity(this.client, this.scope, this.id)?.contents?.door || null;
|
|
10107
10296
|
}
|
|
@@ -10113,6 +10302,9 @@
|
|
|
10113
10302
|
}
|
|
10114
10303
|
};
|
|
10115
10304
|
var SpawnPointHandle = class extends MachineHandle {
|
|
10305
|
+
setRank(rank, options = {}) {
|
|
10306
|
+
return this.client.tips.setRank(this, rank, options);
|
|
10307
|
+
}
|
|
10116
10308
|
get state() {
|
|
10117
10309
|
return summaryForEntity(this.client, this.scope, this.id)?.contents?.spawnPoint || null;
|
|
10118
10310
|
}
|
|
@@ -10657,6 +10849,7 @@
|
|
|
10657
10849
|
this.debug = new ClientDebugDomain(this);
|
|
10658
10850
|
this.player = new PlayerDomain(this);
|
|
10659
10851
|
this.inventory = new InventoryDomain(this);
|
|
10852
|
+
this.tips = new TipDomain(this);
|
|
10660
10853
|
this.management = new ShipManagementDomain(this);
|
|
10661
10854
|
this.cpuLoad = null;
|
|
10662
10855
|
this.inventoryState = normalizeInventoryEvent({ type: "inventory", general_slots: 5 });
|
|
@@ -11410,6 +11603,7 @@
|
|
|
11410
11603
|
}
|
|
11411
11604
|
#handleSideEvent(event, world) {
|
|
11412
11605
|
if (!event || typeof event !== "object") return;
|
|
11606
|
+
if (event.type === "tip_list") this.tips.receive(event, world);
|
|
11413
11607
|
if (event.type === "inventory") {
|
|
11414
11608
|
this.inventoryState = normalizeInventoryEvent(event);
|
|
11415
11609
|
this.emit("inventory", this.inventoryState, world);
|