valheim-oz-dsm 1.5.4 → 1.8.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/CHANGELOG.md +38 -0
- package/dist/main.js +2355 -497
- package/dist/main.js.map +1 -1
- package/package.json +2 -5
- package/patches/@caleb-collar+steamcmd+1.1.0.patch +0 -13
package/dist/main.js
CHANGED
|
@@ -10,10 +10,20 @@ var __export = (target, all) => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
// src/rcon/types.ts
|
|
13
|
-
var RconError;
|
|
13
|
+
var PacketType, RconError, ValheimCommands, ValheimEvents, ValheimGlobalKeys;
|
|
14
14
|
var init_types = __esm({
|
|
15
15
|
"src/rcon/types.ts"() {
|
|
16
16
|
"use strict";
|
|
17
|
+
PacketType = {
|
|
18
|
+
/** Authentication request */
|
|
19
|
+
SERVERDATA_AUTH: 3,
|
|
20
|
+
/** Authentication response */
|
|
21
|
+
SERVERDATA_AUTH_RESPONSE: 2,
|
|
22
|
+
/** Execute command */
|
|
23
|
+
SERVERDATA_EXECCOMMAND: 2,
|
|
24
|
+
/** Command response */
|
|
25
|
+
SERVERDATA_RESPONSE_VALUE: 0
|
|
26
|
+
};
|
|
17
27
|
RconError = class extends Error {
|
|
18
28
|
constructor(code, message) {
|
|
19
29
|
super(message);
|
|
@@ -21,6 +31,81 @@ var init_types = __esm({
|
|
|
21
31
|
this.name = "RconError";
|
|
22
32
|
}
|
|
23
33
|
};
|
|
34
|
+
ValheimCommands = {
|
|
35
|
+
// Server Management
|
|
36
|
+
/** Force save world */
|
|
37
|
+
SAVE: "save",
|
|
38
|
+
/** Get server info */
|
|
39
|
+
INFO: "info",
|
|
40
|
+
/** Ping server */
|
|
41
|
+
PING: "ping",
|
|
42
|
+
// Player Management
|
|
43
|
+
/** Kick player */
|
|
44
|
+
kick: (player) => `kick ${player}`,
|
|
45
|
+
/** Ban player */
|
|
46
|
+
ban: (player) => `ban ${player}`,
|
|
47
|
+
/** Unban player */
|
|
48
|
+
unban: (player) => `unban ${player}`,
|
|
49
|
+
/** List banned players */
|
|
50
|
+
BANNED: "banned",
|
|
51
|
+
/** List permitted players */
|
|
52
|
+
PERMITTED: "permitted",
|
|
53
|
+
// Time Control
|
|
54
|
+
/** Sleep through night (skip to morning) */
|
|
55
|
+
SLEEP: "sleep",
|
|
56
|
+
/** Skip time by seconds */
|
|
57
|
+
skiptime: (seconds) => `skiptime ${seconds}`,
|
|
58
|
+
// Events
|
|
59
|
+
/** Trigger specific event */
|
|
60
|
+
event: (eventName) => `event ${eventName}`,
|
|
61
|
+
/** Trigger random event */
|
|
62
|
+
RANDOMEVENT: "randomevent",
|
|
63
|
+
/** Stop current event */
|
|
64
|
+
STOPEVENT: "stopevent",
|
|
65
|
+
// World Management
|
|
66
|
+
/** Remove all dropped items */
|
|
67
|
+
REMOVEDROPS: "removedrops",
|
|
68
|
+
/** Set a global key */
|
|
69
|
+
setkey: (key) => `setkey ${key}`,
|
|
70
|
+
/** Remove a global key */
|
|
71
|
+
removekey: (key) => `removekey ${key}`,
|
|
72
|
+
/** Reset all global keys */
|
|
73
|
+
RESETKEYS: "resetkeys",
|
|
74
|
+
/** List all global keys */
|
|
75
|
+
LISTKEYS: "listkeys",
|
|
76
|
+
// Performance
|
|
77
|
+
/** Set LOD bias (0-5, lower = better performance) */
|
|
78
|
+
lodbias: (value) => `lodbias ${value}`,
|
|
79
|
+
/** Set LOD distance (100-6000) */
|
|
80
|
+
loddist: (value) => `loddist ${value}`
|
|
81
|
+
};
|
|
82
|
+
ValheimEvents = {
|
|
83
|
+
ARMY_EIKTHYR: "army_eikthyr",
|
|
84
|
+
ARMY_THEELDER: "army_theelder",
|
|
85
|
+
ARMY_BONEMASS: "army_bonemass",
|
|
86
|
+
ARMY_MODER: "army_moder",
|
|
87
|
+
ARMY_GOBLIN: "army_goblin",
|
|
88
|
+
FORESTTROLLS: "foresttrolls",
|
|
89
|
+
SKELETONS: "skeletons",
|
|
90
|
+
BLOBS: "blobs",
|
|
91
|
+
WOLVES: "wolves",
|
|
92
|
+
BATS: "bats",
|
|
93
|
+
SERPENTS: "serpents"
|
|
94
|
+
};
|
|
95
|
+
ValheimGlobalKeys = {
|
|
96
|
+
DEFEATED_EIKTHYR: "defeated_eikthyr",
|
|
97
|
+
DEFEATED_GDKING: "defeated_gdking",
|
|
98
|
+
// The Elder
|
|
99
|
+
DEFEATED_BONEMASS: "defeated_bonemass",
|
|
100
|
+
DEFEATED_DRAGON: "defeated_dragon",
|
|
101
|
+
// Moder
|
|
102
|
+
DEFEATED_GOBLINKING: "defeated_goblinking",
|
|
103
|
+
// Yagluth
|
|
104
|
+
DEFEATED_QUEEN: "defeated_queen",
|
|
105
|
+
KILLED_TROLL: "KilledTroll",
|
|
106
|
+
KILLED_BAT: "KilledBat",
|
|
107
|
+
KILLED_SURTLING: "KilledSurtling"
|
|
108
|
+
};
|
|
24
109
|
}
|
|
25
110
|
});
|
|
26
111
|
|
|
@@ -373,6 +458,415 @@ var init_client = __esm({
|
|
|
373
458
|
}
|
|
374
459
|
});
|
|
375
460
|
|
|
461
|
+
// src/rcon/manager.ts
|
|
462
|
+
var RconManager, rconManager;
|
|
463
|
+
var init_manager = __esm({
|
|
464
|
+
"src/rcon/manager.ts"() {
|
|
465
|
+
"use strict";
|
|
466
|
+
init_client();
|
|
467
|
+
RconManager = class {
|
|
468
|
+
client = null;
|
|
469
|
+
config = null;
|
|
470
|
+
state = "disconnected";
|
|
471
|
+
reconnectTimer = null;
|
|
472
|
+
pollTimer = null;
|
|
473
|
+
reconnectAttempts = 0;
|
|
474
|
+
maxReconnectAttempts = 10;
|
|
475
|
+
reconnectDelay = 5e3;
|
|
476
|
+
// 5 seconds
|
|
477
|
+
pollInterval = 1e4;
|
|
478
|
+
// 10 seconds
|
|
479
|
+
onConnectionStateChange = null;
|
|
480
|
+
onPlayerListUpdate = null;
|
|
481
|
+
lastKnownPlayers = [];
|
|
482
|
+
enabled = false;
|
|
483
|
+
/**
|
|
484
|
+
* Initialize the RCON manager
|
|
485
|
+
* @param config RCON configuration
|
|
486
|
+
* @param options Optional callbacks and settings
|
|
487
|
+
*/
|
|
488
|
+
initialize(config, options = {}) {
|
|
489
|
+
this.config = config;
|
|
490
|
+
this.enabled = config.enabled;
|
|
491
|
+
this.onConnectionStateChange = options.onConnectionStateChange ?? null;
|
|
492
|
+
this.onPlayerListUpdate = options.onPlayerListUpdate ?? null;
|
|
493
|
+
if (options.pollInterval) {
|
|
494
|
+
this.pollInterval = options.pollInterval;
|
|
495
|
+
}
|
|
496
|
+
if (this.enabled) {
|
|
497
|
+
this.connect();
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Connect to RCON server
|
|
502
|
+
*/
|
|
503
|
+
async connect() {
|
|
504
|
+
if (!this.config || !this.enabled) {
|
|
505
|
+
return false;
|
|
506
|
+
}
|
|
507
|
+
if (this.state === "connected" || this.state === "connecting") {
|
|
508
|
+
return true;
|
|
509
|
+
}
|
|
510
|
+
this.setState("connecting");
|
|
511
|
+
this.reconnectAttempts = 0;
|
|
512
|
+
try {
|
|
513
|
+
this.client = new RconClient({
|
|
514
|
+
host: "localhost",
|
|
515
|
+
port: this.config.port,
|
|
516
|
+
password: this.config.password,
|
|
517
|
+
timeout: this.config.timeout
|
|
518
|
+
});
|
|
519
|
+
await this.client.connect();
|
|
520
|
+
this.setState("connected");
|
|
521
|
+
this.reconnectAttempts = 0;
|
|
522
|
+
this.startPlayerListPolling();
|
|
523
|
+
return true;
|
|
524
|
+
} catch (_error) {
|
|
525
|
+
this.setState("error");
|
|
526
|
+
this.client = null;
|
|
527
|
+
if (this.config.autoReconnect && this.enabled) {
|
|
528
|
+
this.scheduleReconnect();
|
|
529
|
+
}
|
|
530
|
+
return false;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Disconnect from RCON server
|
|
535
|
+
*/
|
|
536
|
+
disconnect() {
|
|
537
|
+
this.enabled = false;
|
|
538
|
+
this.clearTimers();
|
|
539
|
+
if (this.client) {
|
|
540
|
+
this.client.disconnect();
|
|
541
|
+
this.client = null;
|
|
542
|
+
}
|
|
543
|
+
this.setState("disconnected");
|
|
544
|
+
this.lastKnownPlayers = [];
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Send a command to the RCON server
|
|
548
|
+
* @param command Command to send
|
|
549
|
+
* @returns Response from server, or null if not connected
|
|
550
|
+
*/
|
|
551
|
+
async send(command) {
|
|
552
|
+
if (!this.client || this.state !== "connected") {
|
|
553
|
+
return null;
|
|
554
|
+
}
|
|
555
|
+
try {
|
|
556
|
+
const response = await this.client.send(command);
|
|
557
|
+
return response;
|
|
558
|
+
} catch (_error) {
|
|
559
|
+
this.setState("error");
|
|
560
|
+
if (this.config?.autoReconnect && this.enabled) {
|
|
561
|
+
this.scheduleReconnect();
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Get the current player list from the server
|
|
568
|
+
* @returns Array of player names, or empty array if unavailable
|
|
569
|
+
*/
|
|
570
|
+
async getPlayerList() {
|
|
571
|
+
const response = await this.send("status");
|
|
572
|
+
if (!response) {
|
|
573
|
+
return [];
|
|
574
|
+
}
|
|
575
|
+
const players = this.parsePlayerList(response);
|
|
576
|
+
return players;
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Parse player names from status command response
|
|
580
|
+
* @param statusText Raw status response from server
|
|
581
|
+
* @returns Array of player names
|
|
582
|
+
*/
|
|
583
|
+
parsePlayerList(statusText) {
|
|
584
|
+
const players = [];
|
|
585
|
+
const lines = statusText.split("\n");
|
|
586
|
+
for (const line of lines) {
|
|
587
|
+
if (line.includes("players:")) {
|
|
588
|
+
const match = line.match(/players:\s*(.+)/);
|
|
589
|
+
if (match?.[1]) {
|
|
590
|
+
const names = match[1].split(",").map((n) => n.trim()).filter((n) => n.length > 0);
|
|
591
|
+
players.push(...names);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
return players;
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Start polling for player list updates
|
|
599
|
+
*/
|
|
600
|
+
startPlayerListPolling() {
|
|
601
|
+
this.stopPlayerListPolling();
|
|
602
|
+
this.pollTimer = setInterval(async () => {
|
|
603
|
+
if (this.state !== "connected") {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
const players = await this.getPlayerList();
|
|
607
|
+
if (this.hasPlayerListChanged(players)) {
|
|
608
|
+
this.lastKnownPlayers = players;
|
|
609
|
+
if (this.onPlayerListUpdate) {
|
|
610
|
+
this.onPlayerListUpdate(players);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
}, this.pollInterval);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Stop polling for player list
|
|
617
|
+
*/
|
|
618
|
+
stopPlayerListPolling() {
|
|
619
|
+
if (this.pollTimer) {
|
|
620
|
+
clearInterval(this.pollTimer);
|
|
621
|
+
this.pollTimer = null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Check if player list has changed
|
|
626
|
+
*/
|
|
627
|
+
hasPlayerListChanged(newPlayers) {
|
|
628
|
+
if (newPlayers.length !== this.lastKnownPlayers.length) {
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
const sorted1 = [...newPlayers].sort();
|
|
632
|
+
const sorted2 = [...this.lastKnownPlayers].sort();
|
|
633
|
+
return !sorted1.every((name, i) => name === sorted2[i]);
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Schedule a reconnect attempt
|
|
637
|
+
*/
|
|
638
|
+
scheduleReconnect() {
|
|
639
|
+
if (this.reconnectTimer) {
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
642
|
+
if (this.reconnectAttempts >= this.maxReconnectAttempts) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
this.reconnectAttempts++;
|
|
646
|
+
const delay = this.reconnectDelay * this.reconnectAttempts;
|
|
647
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
648
|
+
this.reconnectTimer = null;
|
|
649
|
+
await this.connect();
|
|
650
|
+
}, delay);
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Update the connection state and notify listeners
|
|
654
|
+
*/
|
|
655
|
+
setState(newState) {
|
|
656
|
+
if (this.state === newState) {
|
|
657
|
+
return;
|
|
658
|
+
}
|
|
659
|
+
this.state = newState;
|
|
660
|
+
if (this.onConnectionStateChange) {
|
|
661
|
+
this.onConnectionStateChange(newState);
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Clear all timers
|
|
666
|
+
*/
|
|
667
|
+
clearTimers() {
|
|
668
|
+
if (this.reconnectTimer) {
|
|
669
|
+
clearTimeout(this.reconnectTimer);
|
|
670
|
+
this.reconnectTimer = null;
|
|
671
|
+
}
|
|
672
|
+
this.stopPlayerListPolling();
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Get the current connection state
|
|
676
|
+
*/
|
|
677
|
+
getState() {
|
|
678
|
+
return this.state;
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Check if RCON is connected
|
|
682
|
+
*/
|
|
683
|
+
isConnected() {
|
|
684
|
+
return this.state === "connected";
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Get the last known player list
|
|
688
|
+
*/
|
|
689
|
+
getLastKnownPlayers() {
|
|
690
|
+
return [...this.lastKnownPlayers];
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Kick a player from the server
|
|
694
|
+
* @param playerName Player name to kick
|
|
695
|
+
* @returns Response message
|
|
696
|
+
*/
|
|
697
|
+
async kickPlayer(playerName) {
|
|
698
|
+
return this.send(`kick ${playerName}`);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Ban a player from the server
|
|
702
|
+
* @param playerName Player name to ban
|
|
703
|
+
* @returns Response message
|
|
704
|
+
*/
|
|
705
|
+
async banPlayer(playerName) {
|
|
706
|
+
return this.send(`ban ${playerName}`);
|
|
707
|
+
}
|
|
708
|
+
/**
|
|
709
|
+
* Unban a player
|
|
710
|
+
* @param playerName Player name to unban
|
|
711
|
+
* @returns Response message
|
|
712
|
+
*/
|
|
713
|
+
async unbanPlayer(playerName) {
|
|
714
|
+
return this.send(`unban ${playerName}`);
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* Get list of banned players
|
|
718
|
+
* @returns Array of banned player names/IDs
|
|
719
|
+
*/
|
|
720
|
+
async getBannedPlayers() {
|
|
721
|
+
const response = await this.send("banned");
|
|
722
|
+
if (!response) return [];
|
|
723
|
+
return response.split("\n").filter((line) => line.trim().length > 0);
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Get server information
|
|
727
|
+
* @returns Server info response
|
|
728
|
+
*/
|
|
729
|
+
async getServerInfo() {
|
|
730
|
+
return this.send("info");
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* Ping the server
|
|
734
|
+
* @returns Ping response
|
|
735
|
+
*/
|
|
736
|
+
async pingServer() {
|
|
737
|
+
return this.send("ping");
|
|
738
|
+
}
|
|
739
|
+
/**
|
|
740
|
+
* Trigger a random event
|
|
741
|
+
* @param eventName Event name (e.g., "army_eikthyr")
|
|
742
|
+
* @returns Response message
|
|
743
|
+
*/
|
|
744
|
+
async triggerEvent(eventName) {
|
|
745
|
+
return this.send(`event ${eventName}`);
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Trigger a random event
|
|
749
|
+
* @returns Response message
|
|
750
|
+
*/
|
|
751
|
+
async triggerRandomEvent() {
|
|
752
|
+
return this.send("randomevent");
|
|
753
|
+
}
|
|
754
|
+
/**
|
|
755
|
+
* Stop the current event
|
|
756
|
+
* @returns Response message
|
|
757
|
+
*/
|
|
758
|
+
async stopEvent() {
|
|
759
|
+
return this.send("stopevent");
|
|
760
|
+
}
|
|
761
|
+
/**
|
|
762
|
+
* Skip time by specified seconds
|
|
763
|
+
* @param seconds Number of seconds to skip
|
|
764
|
+
* @returns Response message
|
|
765
|
+
*/
|
|
766
|
+
async skipTime(seconds) {
|
|
767
|
+
return this.send(`skiptime ${seconds}`);
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Sleep through the night
|
|
771
|
+
* @returns Response message
|
|
772
|
+
*/
|
|
773
|
+
async sleep() {
|
|
774
|
+
return this.send("sleep");
|
|
775
|
+
}
|
|
776
|
+
/**
|
|
777
|
+
* Remove all dropped items from the world
|
|
778
|
+
* @returns Response message
|
|
779
|
+
*/
|
|
780
|
+
async removeDrops() {
|
|
781
|
+
return this.send("removedrops");
|
|
782
|
+
}
|
|
783
|
+
/**
|
|
784
|
+
* Set a global key
|
|
785
|
+
* @param key Global key name
|
|
786
|
+
* @returns Response message
|
|
787
|
+
*/
|
|
788
|
+
async setGlobalKey(key) {
|
|
789
|
+
return this.send(`setkey ${key}`);
|
|
790
|
+
}
|
|
791
|
+
/**
|
|
792
|
+
* Remove a global key
|
|
793
|
+
* @param key Global key name
|
|
794
|
+
* @returns Response message
|
|
795
|
+
*/
|
|
796
|
+
async removeGlobalKey(key) {
|
|
797
|
+
return this.send(`removekey ${key}`);
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* Reset all global keys
|
|
801
|
+
* @returns Response message
|
|
802
|
+
*/
|
|
803
|
+
async resetGlobalKeys() {
|
|
804
|
+
return this.send("resetkeys");
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* List all global keys
|
|
808
|
+
* @returns Array of global key names
|
|
809
|
+
*/
|
|
810
|
+
async listGlobalKeys() {
|
|
811
|
+
const response = await this.send("listkeys");
|
|
812
|
+
if (!response) return [];
|
|
813
|
+
return response.split("\n").filter((line) => line.trim().length > 0);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Set LOD bias (0-5, lower = better performance)
|
|
817
|
+
* @param value LOD bias value
|
|
818
|
+
* @returns Response message
|
|
819
|
+
*/
|
|
820
|
+
async setLodBias(value) {
|
|
821
|
+
return this.send(`lodbias ${value}`);
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Set LOD distance (100-6000)
|
|
825
|
+
* @param value LOD distance value
|
|
826
|
+
* @returns Response message
|
|
827
|
+
*/
|
|
828
|
+
async setLodDistance(value) {
|
|
829
|
+
return this.send(`loddist ${value}`);
|
|
830
|
+
}
|
|
831
|
+
/**
|
|
832
|
+
* Clean up resources
|
|
833
|
+
*/
|
|
834
|
+
cleanup() {
|
|
835
|
+
this.disconnect();
|
|
836
|
+
}
|
|
837
|
+
};
|
|
838
|
+
rconManager = new RconManager();
|
|
839
|
+
}
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
// src/rcon/mod.ts
|
|
843
|
+
var mod_exports = {};
|
|
844
|
+
__export(mod_exports, {
|
|
845
|
+
PacketType: () => PacketType,
|
|
846
|
+
RconClient: () => RconClient,
|
|
847
|
+
RconError: () => RconError,
|
|
848
|
+
ValheimCommands: () => ValheimCommands,
|
|
849
|
+
ValheimEvents: () => ValheimEvents,
|
|
850
|
+
ValheimGlobalKeys: () => ValheimGlobalKeys,
|
|
851
|
+
createAuthPacket: () => createAuthPacket,
|
|
852
|
+
createCommandPacket: () => createCommandPacket,
|
|
853
|
+
decodePacket: () => decodePacket,
|
|
854
|
+
encodePacket: () => encodePacket,
|
|
855
|
+
isAuthFailure: () => isAuthFailure,
|
|
856
|
+
isAuthResponse: () => isAuthResponse,
|
|
857
|
+
isAuthSuccess: () => isAuthSuccess,
|
|
858
|
+
rconManager: () => rconManager
|
|
859
|
+
});
|
|
860
|
+
var init_mod = __esm({
|
|
861
|
+
"src/rcon/mod.ts"() {
|
|
862
|
+
"use strict";
|
|
863
|
+
init_client();
|
|
864
|
+
init_manager();
|
|
865
|
+
init_protocol();
|
|
866
|
+
init_types();
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
|
|
376
870
|
// src/cli/args.ts
|
|
377
871
|
import { z } from "zod";
|
|
378
872
|
var PortSchema = z.number().int().min(1024).max(65535);
|
|
@@ -919,11 +1413,11 @@ var defaultConfig = {
|
|
|
919
1413
|
refreshRate: 1e3
|
|
920
1414
|
},
|
|
921
1415
|
rcon: {
|
|
922
|
-
enabled:
|
|
1416
|
+
enabled: true,
|
|
923
1417
|
port: 25575,
|
|
924
|
-
password: "",
|
|
1418
|
+
password: "valheim-rcon",
|
|
925
1419
|
timeout: 5e3,
|
|
926
|
-
autoReconnect:
|
|
1420
|
+
autoReconnect: true
|
|
927
1421
|
},
|
|
928
1422
|
worlds: [],
|
|
929
1423
|
activeWorld: null,
|
|
@@ -1026,11 +1520,11 @@ var TuiConfigSchema = z2.object({
|
|
|
1026
1520
|
refreshRate: z2.number().int().min(100).max(5e3).default(1e3)
|
|
1027
1521
|
});
|
|
1028
1522
|
var RconConfigSchema = z2.object({
|
|
1029
|
-
enabled: z2.boolean().default(
|
|
1523
|
+
enabled: z2.boolean().default(true),
|
|
1030
1524
|
port: z2.number().int().min(1024, "Port must be >= 1024").max(65535, "Port must be <= 65535").default(25575),
|
|
1031
|
-
password: z2.string().default(""),
|
|
1525
|
+
password: z2.string().default("valheim-rcon"),
|
|
1032
1526
|
timeout: z2.number().int().min(1e3).max(6e4).default(5e3),
|
|
1033
|
-
autoReconnect: z2.boolean().default(
|
|
1527
|
+
autoReconnect: z2.boolean().default(true)
|
|
1034
1528
|
});
|
|
1035
1529
|
var AppConfigSchema = z2.object({
|
|
1036
1530
|
version: z2.number().int().default(1),
|
|
@@ -2496,13 +2990,7 @@ function createProgressBar(percent) {
|
|
|
2496
2990
|
|
|
2497
2991
|
// src/cli/commands/rcon.ts
|
|
2498
2992
|
import * as readline from "readline";
|
|
2499
|
-
|
|
2500
|
-
// src/rcon/mod.ts
|
|
2501
|
-
init_client();
|
|
2502
|
-
init_protocol();
|
|
2503
|
-
init_types();
|
|
2504
|
-
|
|
2505
|
-
// src/cli/commands/rcon.ts
|
|
2993
|
+
init_mod();
|
|
2506
2994
|
async function rconCommand(args) {
|
|
2507
2995
|
const config = await loadConfig();
|
|
2508
2996
|
const host = args.host ?? "localhost";
|
|
@@ -2640,10 +3128,30 @@ async function interactiveRcon(args) {
|
|
|
2640
3128
|
}
|
|
2641
3129
|
|
|
2642
3130
|
// src/server/commands.ts
|
|
3131
|
+
init_mod();
|
|
2643
3132
|
import * as fs5 from "fs/promises";
|
|
2644
3133
|
import { dirname, join } from "path";
|
|
2645
3134
|
|
|
2646
3135
|
// src/server/logs.ts
|
|
3136
|
+
function parseLogLine(line) {
|
|
3137
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
3138
|
+
let level = "info";
|
|
3139
|
+
let message = line.trim();
|
|
3140
|
+
if (line.includes("Error") || line.includes("Exception")) {
|
|
3141
|
+
level = "error";
|
|
3142
|
+
} else if (line.includes("Warning") || line.includes("WARN")) {
|
|
3143
|
+
level = "warn";
|
|
3144
|
+
} else if (line.includes("DEBUG") || line.includes("[Debug]")) {
|
|
3145
|
+
level = "debug";
|
|
3146
|
+
}
|
|
3147
|
+
const timestampMatch = line.match(
|
|
3148
|
+
/^(\d{2}\/\d{2}\/\d{4} \d{2}:\d{2}:\d{2}): (.+)$/
|
|
3149
|
+
);
|
|
3150
|
+
if (timestampMatch) {
|
|
3151
|
+
message = timestampMatch[2];
|
|
3152
|
+
}
|
|
3153
|
+
return { timestamp, level, message, raw: line };
|
|
3154
|
+
}
|
|
2647
3155
|
function parseEvent(line) {
|
|
2648
3156
|
if (line.includes("Got character ZDOID from")) {
|
|
2649
3157
|
const match = line.match(/Got character ZDOID from (\S+)/);
|
|
@@ -2651,8 +3159,11 @@ function parseEvent(line) {
|
|
|
2651
3159
|
return { type: "player_join", name: match[1] };
|
|
2652
3160
|
}
|
|
2653
3161
|
}
|
|
2654
|
-
if (line.includes("
|
|
2655
|
-
|
|
3162
|
+
if (line.includes("Destroying abandoned non persistent zdo")) {
|
|
3163
|
+
const playerMatch = line.match(/owner (\S+)/);
|
|
3164
|
+
if (playerMatch?.[1]) {
|
|
3165
|
+
return { type: "player_leave", name: playerMatch[1] };
|
|
3166
|
+
}
|
|
2656
3167
|
}
|
|
2657
3168
|
if (line.includes("World saved")) {
|
|
2658
3169
|
return { type: "world_saved" };
|
|
@@ -2690,9 +3201,179 @@ function parseEvent(line) {
|
|
|
2690
3201
|
return null;
|
|
2691
3202
|
}
|
|
2692
3203
|
|
|
3204
|
+
// src/server/logTail.ts
|
|
3205
|
+
import { open } from "fs/promises";
|
|
3206
|
+
var LogTailer = class {
|
|
3207
|
+
filePath;
|
|
3208
|
+
handle = null;
|
|
3209
|
+
position = 0;
|
|
3210
|
+
running = false;
|
|
3211
|
+
pollInterval = null;
|
|
3212
|
+
buffer = "";
|
|
3213
|
+
onLine;
|
|
3214
|
+
onEvent;
|
|
3215
|
+
pollMs;
|
|
3216
|
+
/**
|
|
3217
|
+
* Creates a new log tailer
|
|
3218
|
+
* @param filePath Path to the log file to tail
|
|
3219
|
+
* @param onLine Callback for each new log line
|
|
3220
|
+
* @param options Optional settings
|
|
3221
|
+
*/
|
|
3222
|
+
constructor(filePath, onLine, options) {
|
|
3223
|
+
this.filePath = filePath;
|
|
3224
|
+
this.onLine = onLine;
|
|
3225
|
+
this.onEvent = options?.onEvent;
|
|
3226
|
+
this.pollMs = options?.pollMs ?? 500;
|
|
3227
|
+
}
|
|
3228
|
+
/**
|
|
3229
|
+
* Starts tailing the log file
|
|
3230
|
+
* @param fromEnd If true, start from end of file (skip existing content)
|
|
3231
|
+
*/
|
|
3232
|
+
async start(fromEnd = true) {
|
|
3233
|
+
if (this.running) return;
|
|
3234
|
+
try {
|
|
3235
|
+
this.handle = await open(this.filePath, "r");
|
|
3236
|
+
if (fromEnd) {
|
|
3237
|
+
const stats = await this.handle.stat();
|
|
3238
|
+
this.position = stats.size;
|
|
3239
|
+
} else {
|
|
3240
|
+
this.position = 0;
|
|
3241
|
+
}
|
|
3242
|
+
this.running = true;
|
|
3243
|
+
this.pollInterval = setInterval(() => this.poll(), this.pollMs);
|
|
3244
|
+
await this.poll();
|
|
3245
|
+
} catch (_error) {
|
|
3246
|
+
this.running = true;
|
|
3247
|
+
this.pollInterval = setInterval(() => this.poll(), this.pollMs);
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Stops tailing the log file
|
|
3252
|
+
*/
|
|
3253
|
+
async stop() {
|
|
3254
|
+
this.running = false;
|
|
3255
|
+
if (this.pollInterval) {
|
|
3256
|
+
clearInterval(this.pollInterval);
|
|
3257
|
+
this.pollInterval = null;
|
|
3258
|
+
}
|
|
3259
|
+
if (this.handle) {
|
|
3260
|
+
await this.handle.close();
|
|
3261
|
+
this.handle = null;
|
|
3262
|
+
}
|
|
3263
|
+
}
|
|
3264
|
+
/**
|
|
3265
|
+
* Polls the log file for new content
|
|
3266
|
+
*/
|
|
3267
|
+
async poll() {
|
|
3268
|
+
if (!this.running) return;
|
|
3269
|
+
try {
|
|
3270
|
+
if (!this.handle) {
|
|
3271
|
+
try {
|
|
3272
|
+
this.handle = await open(this.filePath, "r");
|
|
3273
|
+
const stats2 = await this.handle.stat();
|
|
3274
|
+
if (this.position > stats2.size) {
|
|
3275
|
+
this.position = 0;
|
|
3276
|
+
}
|
|
3277
|
+
} catch {
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3280
|
+
}
|
|
3281
|
+
const stats = await this.handle.stat();
|
|
3282
|
+
if (stats.size <= this.position) {
|
|
3283
|
+
if (stats.size < this.position) {
|
|
3284
|
+
this.position = 0;
|
|
3285
|
+
}
|
|
3286
|
+
return;
|
|
3287
|
+
}
|
|
3288
|
+
const bytesToRead = stats.size - this.position;
|
|
3289
|
+
const buffer = Buffer.alloc(bytesToRead);
|
|
3290
|
+
const { bytesRead } = await this.handle.read(
|
|
3291
|
+
buffer,
|
|
3292
|
+
0,
|
|
3293
|
+
bytesToRead,
|
|
3294
|
+
this.position
|
|
3295
|
+
);
|
|
3296
|
+
if (bytesRead > 0) {
|
|
3297
|
+
this.position += bytesRead;
|
|
3298
|
+
this.processChunk(buffer.toString("utf-8", 0, bytesRead));
|
|
3299
|
+
}
|
|
3300
|
+
} catch (_error) {
|
|
3301
|
+
if (this.handle) {
|
|
3302
|
+
try {
|
|
3303
|
+
await this.handle.close();
|
|
3304
|
+
} catch {
|
|
3305
|
+
}
|
|
3306
|
+
this.handle = null;
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
/**
|
|
3311
|
+
* Processes a chunk of log data, splitting into lines
|
|
3312
|
+
*/
|
|
3313
|
+
processChunk(chunk) {
|
|
3314
|
+
this.buffer += chunk;
|
|
3315
|
+
const lines = this.buffer.split("\n");
|
|
3316
|
+
this.buffer = lines.pop() ?? "";
|
|
3317
|
+
for (const line of lines) {
|
|
3318
|
+
const trimmed = line.trim();
|
|
3319
|
+
if (!trimmed) continue;
|
|
3320
|
+
const entry = parseLogLine(trimmed);
|
|
3321
|
+
this.onLine(trimmed, entry);
|
|
3322
|
+
const event = parseEvent(trimmed);
|
|
3323
|
+
if (event && this.onEvent) {
|
|
3324
|
+
this.onEvent(event);
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
/**
|
|
3329
|
+
* Reads the last N lines from the log file (for initial display)
|
|
3330
|
+
* @param lineCount Number of lines to read
|
|
3331
|
+
* @returns Array of log entries
|
|
3332
|
+
*/
|
|
3333
|
+
async readLastLines(lineCount = 100) {
|
|
3334
|
+
const entries = [];
|
|
3335
|
+
try {
|
|
3336
|
+
const handle = await open(this.filePath, "r");
|
|
3337
|
+
const stats = await handle.stat();
|
|
3338
|
+
const fileSize = stats.size;
|
|
3339
|
+
const chunkSize = Math.min(16384, fileSize);
|
|
3340
|
+
let position = Math.max(0, fileSize - chunkSize);
|
|
3341
|
+
let content = "";
|
|
3342
|
+
let lines = [];
|
|
3343
|
+
while (lines.length < lineCount && position >= 0) {
|
|
3344
|
+
const buffer = Buffer.alloc(Math.min(chunkSize, fileSize - position));
|
|
3345
|
+
await handle.read(buffer, 0, buffer.length, position);
|
|
3346
|
+
content = buffer.toString("utf-8") + content;
|
|
3347
|
+
lines = content.split("\n").filter((l) => l.trim());
|
|
3348
|
+
if (position === 0) break;
|
|
3349
|
+
position = Math.max(0, position - chunkSize);
|
|
3350
|
+
}
|
|
3351
|
+
await handle.close();
|
|
3352
|
+
const lastLines = lines.slice(-lineCount);
|
|
3353
|
+
for (const line of lastLines) {
|
|
3354
|
+
entries.push(parseLogLine(line.trim()));
|
|
3355
|
+
}
|
|
3356
|
+
} catch {
|
|
3357
|
+
}
|
|
3358
|
+
return entries;
|
|
3359
|
+
}
|
|
3360
|
+
/** Whether the tailer is currently running */
|
|
3361
|
+
get isRunning() {
|
|
3362
|
+
return this.running;
|
|
3363
|
+
}
|
|
3364
|
+
};
|
|
3365
|
+
|
|
2693
3366
|
// src/server/pidfile.ts
|
|
2694
3367
|
import fs6 from "fs/promises";
|
|
2695
3368
|
import path5 from "path";
|
|
3369
|
+
function getServerLogsDir() {
|
|
3370
|
+
return path5.join(getAppConfigDir(), "logs");
|
|
3371
|
+
}
|
|
3372
|
+
function getServerLogFile(timestamp) {
|
|
3373
|
+
const ts = timestamp ?? /* @__PURE__ */ new Date();
|
|
3374
|
+
const dateStr = ts.toISOString().split("T")[0];
|
|
3375
|
+
return path5.join(getServerLogsDir(), `valheim-server-${dateStr}.log`);
|
|
3376
|
+
}
|
|
2696
3377
|
function getPidFilePath() {
|
|
2697
3378
|
return path5.join(getConfigDir(), "oz-valheim", "server.pid");
|
|
2698
3379
|
}
|
|
@@ -2746,9 +3427,28 @@ async function getRunningServer() {
|
|
|
2746
3427
|
}
|
|
2747
3428
|
return data;
|
|
2748
3429
|
}
|
|
3430
|
+
async function ensureLogsDir() {
|
|
3431
|
+
const logsDir = getServerLogsDir();
|
|
3432
|
+
await fs6.mkdir(logsDir, { recursive: true });
|
|
3433
|
+
}
|
|
3434
|
+
async function cleanupOldLogs(keepCount = 7) {
|
|
3435
|
+
const logsDir = getServerLogsDir();
|
|
3436
|
+
try {
|
|
3437
|
+
const files = await fs6.readdir(logsDir);
|
|
3438
|
+
const logFiles = files.filter((f) => f.startsWith("valheim-server-") && f.endsWith(".log")).sort().reverse();
|
|
3439
|
+
for (const file of logFiles.slice(keepCount)) {
|
|
3440
|
+
try {
|
|
3441
|
+
await fs6.unlink(path5.join(logsDir, file));
|
|
3442
|
+
} catch {
|
|
3443
|
+
}
|
|
3444
|
+
}
|
|
3445
|
+
} catch {
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
2749
3448
|
|
|
2750
3449
|
// src/server/process.ts
|
|
2751
3450
|
import { spawn } from "child_process";
|
|
3451
|
+
import { createWriteStream } from "fs";
|
|
2752
3452
|
var defaultEvents = {
|
|
2753
3453
|
onStateChange: () => {
|
|
2754
3454
|
},
|
|
@@ -2767,6 +3467,12 @@ var ValheimProcess = class {
|
|
|
2767
3467
|
events;
|
|
2768
3468
|
config;
|
|
2769
3469
|
startTime = null;
|
|
3470
|
+
logFileStream = null;
|
|
3471
|
+
logTailer = null;
|
|
3472
|
+
_logFilePath = null;
|
|
3473
|
+
_isDetached = false;
|
|
3474
|
+
/** PID for detached processes (when we don't have a direct handle) */
|
|
3475
|
+
_detachedPid = null;
|
|
2770
3476
|
/**
|
|
2771
3477
|
* Creates a new Valheim process wrapper
|
|
2772
3478
|
* @param config Server launch configuration
|
|
@@ -2778,7 +3484,7 @@ var ValheimProcess = class {
|
|
|
2778
3484
|
}
|
|
2779
3485
|
/** Gets the process ID if running, null otherwise */
|
|
2780
3486
|
get pid() {
|
|
2781
|
-
return this.process?.pid ?? null;
|
|
3487
|
+
return this.process?.pid ?? this._detachedPid ?? null;
|
|
2782
3488
|
}
|
|
2783
3489
|
/** Gets the current process state */
|
|
2784
3490
|
get currentState() {
|
|
@@ -2788,6 +3494,14 @@ var ValheimProcess = class {
|
|
|
2788
3494
|
get uptime() {
|
|
2789
3495
|
return this.startTime;
|
|
2790
3496
|
}
|
|
3497
|
+
/** Gets the log file path (for detached mode) */
|
|
3498
|
+
get logFilePath() {
|
|
3499
|
+
return this._logFilePath;
|
|
3500
|
+
}
|
|
3501
|
+
/** Whether the server is running in detached mode */
|
|
3502
|
+
get isDetached() {
|
|
3503
|
+
return this._isDetached;
|
|
3504
|
+
}
|
|
2791
3505
|
/**
|
|
2792
3506
|
* Updates the process state and notifies listeners
|
|
2793
3507
|
* @param newState New process state
|
|
@@ -2806,21 +3520,16 @@ var ValheimProcess = class {
|
|
|
2806
3520
|
}
|
|
2807
3521
|
this.setState("starting");
|
|
2808
3522
|
this.startTime = /* @__PURE__ */ new Date();
|
|
3523
|
+
this._isDetached = this.config.detached ?? false;
|
|
2809
3524
|
const execPath = getValheimExecutablePath();
|
|
2810
3525
|
const args = this.buildArgs();
|
|
2811
3526
|
const env = this.getEnvironment();
|
|
2812
3527
|
try {
|
|
2813
|
-
this.
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
this.process.on("error", (error2) => {
|
|
2819
|
-
this.setState("crashed");
|
|
2820
|
-
this.startTime = null;
|
|
2821
|
-
this.events.onError(error2);
|
|
2822
|
-
});
|
|
2823
|
-
await Promise.resolve();
|
|
3528
|
+
if (this._isDetached) {
|
|
3529
|
+
await this.startDetached(execPath, args, env);
|
|
3530
|
+
} else {
|
|
3531
|
+
await this.startAttached(execPath, args, env);
|
|
3532
|
+
}
|
|
2824
3533
|
} catch (error2) {
|
|
2825
3534
|
this.setState("crashed");
|
|
2826
3535
|
this.startTime = null;
|
|
@@ -2828,6 +3537,169 @@ var ValheimProcess = class {
|
|
|
2828
3537
|
throw error2;
|
|
2829
3538
|
}
|
|
2830
3539
|
}
|
|
3540
|
+
/**
|
|
3541
|
+
* Starts the server in attached mode (piped stdout/stderr)
|
|
3542
|
+
*/
|
|
3543
|
+
async startAttached(execPath, args, env) {
|
|
3544
|
+
this.process = spawn(execPath, args, {
|
|
3545
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3546
|
+
env
|
|
3547
|
+
});
|
|
3548
|
+
this.streamOutput();
|
|
3549
|
+
this.process.on("error", (error2) => {
|
|
3550
|
+
this.setState("crashed");
|
|
3551
|
+
this.startTime = null;
|
|
3552
|
+
this.events.onError(error2);
|
|
3553
|
+
});
|
|
3554
|
+
await Promise.resolve();
|
|
3555
|
+
}
|
|
3556
|
+
/**
|
|
3557
|
+
* Starts the server in detached mode (log file output, independent process)
|
|
3558
|
+
*/
|
|
3559
|
+
async startDetached(execPath, args, env) {
|
|
3560
|
+
await ensureLogsDir();
|
|
3561
|
+
this._logFilePath = getServerLogFile(this.startTime);
|
|
3562
|
+
this.logFileStream = createWriteStream(this._logFilePath, { flags: "a" });
|
|
3563
|
+
const header = `
|
|
3564
|
+
${"=".repeat(60)}
|
|
3565
|
+
Server starting at ${this.startTime.toISOString()}
|
|
3566
|
+
World: ${this.config.world} | Port: ${this.config.port}
|
|
3567
|
+
${"=".repeat(60)}
|
|
3568
|
+
`;
|
|
3569
|
+
this.logFileStream.write(header);
|
|
3570
|
+
const platform = getPlatform();
|
|
3571
|
+
this.process = spawn(execPath, args, {
|
|
3572
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3573
|
+
env,
|
|
3574
|
+
detached: true
|
|
3575
|
+
});
|
|
3576
|
+
if (this.process.stdout) {
|
|
3577
|
+
this.process.stdout.pipe(this.logFileStream, { end: false });
|
|
3578
|
+
}
|
|
3579
|
+
if (this.process.stderr) {
|
|
3580
|
+
this.process.stderr.pipe(this.logFileStream, { end: false });
|
|
3581
|
+
}
|
|
3582
|
+
this.logTailer = new LogTailer(
|
|
3583
|
+
this._logFilePath,
|
|
3584
|
+
(line, _entry) => {
|
|
3585
|
+
this.events.onLog(line);
|
|
3586
|
+
},
|
|
3587
|
+
{
|
|
3588
|
+
onEvent: (event) => this.handleEvent(event),
|
|
3589
|
+
fromEnd: false
|
|
3590
|
+
// Read from where we started
|
|
3591
|
+
}
|
|
3592
|
+
);
|
|
3593
|
+
await this.logTailer.start(false);
|
|
3594
|
+
this.process.on("error", (error2) => {
|
|
3595
|
+
this.setState("crashed");
|
|
3596
|
+
this.startTime = null;
|
|
3597
|
+
this.events.onError(error2);
|
|
3598
|
+
});
|
|
3599
|
+
const pid = this.process.pid;
|
|
3600
|
+
if (pid) {
|
|
3601
|
+
await writePidFile({
|
|
3602
|
+
pid,
|
|
3603
|
+
startedAt: this.startTime.toISOString(),
|
|
3604
|
+
world: this.config.world,
|
|
3605
|
+
port: this.config.port,
|
|
3606
|
+
logFile: this._logFilePath,
|
|
3607
|
+
detached: true,
|
|
3608
|
+
serverName: this.config.name
|
|
3609
|
+
});
|
|
3610
|
+
}
|
|
3611
|
+
this.process.unref();
|
|
3612
|
+
if (platform === "windows") {
|
|
3613
|
+
if (this.process.stdout && typeof this.process.stdout.unref === "function") {
|
|
3614
|
+
this.process.stdout.unref();
|
|
3615
|
+
}
|
|
3616
|
+
if (this.process.stderr && typeof this.process.stderr.unref === "function") {
|
|
3617
|
+
this.process.stderr.unref();
|
|
3618
|
+
}
|
|
3619
|
+
}
|
|
3620
|
+
this.process.on("exit", (code, signal) => {
|
|
3621
|
+
const exitCode = code ?? (signal ? 1 : 0);
|
|
3622
|
+
if (exitCode !== 0 && (this.state === "online" || this.state === "starting")) {
|
|
3623
|
+
this.startTime = null;
|
|
3624
|
+
this.setState("crashed");
|
|
3625
|
+
this.events.onError(new Error(`Server exited with code ${exitCode}`));
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
await Promise.resolve();
|
|
3629
|
+
}
|
|
3630
|
+
/**
|
|
3631
|
+
* Handles a parsed event from log output
|
|
3632
|
+
*/
|
|
3633
|
+
handleEvent(event) {
|
|
3634
|
+
this.events.onEvent?.(event);
|
|
3635
|
+
switch (event.type) {
|
|
3636
|
+
case "player_join":
|
|
3637
|
+
this.events.onPlayerJoin(event.name);
|
|
3638
|
+
break;
|
|
3639
|
+
case "player_leave":
|
|
3640
|
+
this.events.onPlayerLeave(event.name);
|
|
3641
|
+
break;
|
|
3642
|
+
case "server_ready":
|
|
3643
|
+
if (this.state === "starting") {
|
|
3644
|
+
this.setState("online");
|
|
3645
|
+
}
|
|
3646
|
+
break;
|
|
3647
|
+
case "error":
|
|
3648
|
+
this.events.onError(new Error(event.message));
|
|
3649
|
+
break;
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
/**
|
|
3653
|
+
* Attaches to an already-running detached server
|
|
3654
|
+
* @param pidData PID file data for the running server
|
|
3655
|
+
*/
|
|
3656
|
+
async attach(pidData) {
|
|
3657
|
+
if (this.state !== "offline" && this.state !== "crashed") {
|
|
3658
|
+
throw new Error(`Cannot attach in state: ${this.state}`);
|
|
3659
|
+
}
|
|
3660
|
+
if (!isProcessRunning(pidData.pid)) {
|
|
3661
|
+
throw new Error(`Server process ${pidData.pid} is not running`);
|
|
3662
|
+
}
|
|
3663
|
+
this._isDetached = true;
|
|
3664
|
+
this._detachedPid = pidData.pid;
|
|
3665
|
+
this._logFilePath = pidData.logFile ?? null;
|
|
3666
|
+
this.startTime = new Date(pidData.startedAt);
|
|
3667
|
+
if (this._logFilePath) {
|
|
3668
|
+
this.logTailer = new LogTailer(
|
|
3669
|
+
this._logFilePath,
|
|
3670
|
+
(line, _entry) => {
|
|
3671
|
+
this.events.onLog(line);
|
|
3672
|
+
},
|
|
3673
|
+
{
|
|
3674
|
+
onEvent: (event) => this.handleEvent(event),
|
|
3675
|
+
pollMs: 500
|
|
3676
|
+
}
|
|
3677
|
+
);
|
|
3678
|
+
const history = await this.logTailer.readLastLines(50);
|
|
3679
|
+
for (const entry of history) {
|
|
3680
|
+
this.events.onLog(entry.raw);
|
|
3681
|
+
}
|
|
3682
|
+
await this.logTailer.start(true);
|
|
3683
|
+
}
|
|
3684
|
+
this.startProcessMonitor();
|
|
3685
|
+
this.setState("online");
|
|
3686
|
+
}
|
|
3687
|
+
/**
|
|
3688
|
+
* Monitors a detached process for exit
|
|
3689
|
+
*/
|
|
3690
|
+
startProcessMonitor() {
|
|
3691
|
+
const checkInterval = setInterval(() => {
|
|
3692
|
+
const pid = this._detachedPid;
|
|
3693
|
+
if (pid && !isProcessRunning(pid)) {
|
|
3694
|
+
clearInterval(checkInterval);
|
|
3695
|
+
this._detachedPid = null;
|
|
3696
|
+
this.startTime = null;
|
|
3697
|
+
this.setState("crashed");
|
|
3698
|
+
this.events.onError(new Error("Server process exited unexpectedly"));
|
|
3699
|
+
}
|
|
3700
|
+
}, 2e3);
|
|
3701
|
+
this._monitorInterval = checkInterval;
|
|
3702
|
+
}
|
|
2831
3703
|
/**
|
|
2832
3704
|
* Gracefully stops the server with optional timeout
|
|
2833
3705
|
* @param timeout Maximum time to wait for graceful shutdown (ms)
|
|
@@ -2837,6 +3709,31 @@ var ValheimProcess = class {
|
|
|
2837
3709
|
return;
|
|
2838
3710
|
}
|
|
2839
3711
|
this.setState("stopping");
|
|
3712
|
+
await this.cleanup();
|
|
3713
|
+
if (this._isDetached && this._detachedPid) {
|
|
3714
|
+
const platform = getPlatform();
|
|
3715
|
+
try {
|
|
3716
|
+
process.kill(this._detachedPid, "SIGTERM");
|
|
3717
|
+
} catch {
|
|
3718
|
+
}
|
|
3719
|
+
const startTime = Date.now();
|
|
3720
|
+
while (isProcessRunning(this._detachedPid) && Date.now() - startTime < timeout) {
|
|
3721
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3722
|
+
}
|
|
3723
|
+
if (isProcessRunning(this._detachedPid)) {
|
|
3724
|
+
try {
|
|
3725
|
+
process.kill(
|
|
3726
|
+
this._detachedPid,
|
|
3727
|
+
platform === "windows" ? "SIGTERM" : "SIGKILL"
|
|
3728
|
+
);
|
|
3729
|
+
} catch {
|
|
3730
|
+
}
|
|
3731
|
+
}
|
|
3732
|
+
this._detachedPid = null;
|
|
3733
|
+
this.startTime = null;
|
|
3734
|
+
this.setState("offline");
|
|
3735
|
+
return;
|
|
3736
|
+
}
|
|
2840
3737
|
if (this.process) {
|
|
2841
3738
|
try {
|
|
2842
3739
|
this.process.kill("SIGTERM");
|
|
@@ -2864,6 +3761,18 @@ var ValheimProcess = class {
|
|
|
2864
3761
|
* Immediately kills the server process
|
|
2865
3762
|
*/
|
|
2866
3763
|
async kill() {
|
|
3764
|
+
await this.cleanup();
|
|
3765
|
+
if (this._isDetached && this._detachedPid) {
|
|
3766
|
+
const platform = getPlatform();
|
|
3767
|
+
try {
|
|
3768
|
+
process.kill(
|
|
3769
|
+
this._detachedPid,
|
|
3770
|
+
platform === "windows" ? "SIGTERM" : "SIGKILL"
|
|
3771
|
+
);
|
|
3772
|
+
} catch {
|
|
3773
|
+
}
|
|
3774
|
+
this._detachedPid = null;
|
|
3775
|
+
}
|
|
2867
3776
|
if (this.process) {
|
|
2868
3777
|
try {
|
|
2869
3778
|
this.process.kill("SIGKILL");
|
|
@@ -2875,6 +3784,36 @@ var ValheimProcess = class {
|
|
|
2875
3784
|
this.setState("offline");
|
|
2876
3785
|
await Promise.resolve();
|
|
2877
3786
|
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Detaches from a running server without stopping it
|
|
3789
|
+
* Only valid for servers started in detached mode
|
|
3790
|
+
*/
|
|
3791
|
+
async detach() {
|
|
3792
|
+
if (!this._isDetached) {
|
|
3793
|
+
throw new Error("Cannot detach from non-detached server");
|
|
3794
|
+
}
|
|
3795
|
+
await this.cleanup();
|
|
3796
|
+
this.process = null;
|
|
3797
|
+
this._detachedPid = null;
|
|
3798
|
+
}
|
|
3799
|
+
/**
|
|
3800
|
+
* Cleans up resources (log tailer, monitor interval, log file stream)
|
|
3801
|
+
*/
|
|
3802
|
+
async cleanup() {
|
|
3803
|
+
const self = this;
|
|
3804
|
+
if (self._monitorInterval) {
|
|
3805
|
+
clearInterval(self._monitorInterval);
|
|
3806
|
+
self._monitorInterval = void 0;
|
|
3807
|
+
}
|
|
3808
|
+
if (this.logTailer) {
|
|
3809
|
+
await this.logTailer.stop();
|
|
3810
|
+
this.logTailer = null;
|
|
3811
|
+
}
|
|
3812
|
+
if (this.logFileStream) {
|
|
3813
|
+
this.logFileStream.end();
|
|
3814
|
+
this.logFileStream = null;
|
|
3815
|
+
}
|
|
3816
|
+
}
|
|
2878
3817
|
/**
|
|
2879
3818
|
* Builds command line arguments for Valheim server
|
|
2880
3819
|
* @returns Array of command line arguments
|
|
@@ -3190,6 +4129,18 @@ async function startCommand(args, config) {
|
|
|
3190
4129
|
console.log("Run 'valheim-dsm install' first to install the server.");
|
|
3191
4130
|
process.exit(1);
|
|
3192
4131
|
}
|
|
4132
|
+
const running = await getRunningServer();
|
|
4133
|
+
if (running) {
|
|
4134
|
+
console.error(`
|
|
4135
|
+
Error: A server is already running.`);
|
|
4136
|
+
console.log(` PID: ${running.pid}`);
|
|
4137
|
+
console.log(` World: ${running.world}`);
|
|
4138
|
+
console.log(` Port: ${running.port}`);
|
|
4139
|
+
console.log(` Started: ${new Date(running.startedAt).toLocaleString()}`);
|
|
4140
|
+
console.log("\nRun 'valheim-dsm stop' to stop it first.");
|
|
4141
|
+
process.exit(1);
|
|
4142
|
+
}
|
|
4143
|
+
await cleanupOldLogs();
|
|
3193
4144
|
const serverConfig = {
|
|
3194
4145
|
name: args.name ?? config.server.name,
|
|
3195
4146
|
port: args.port ?? config.server.port,
|
|
@@ -3199,7 +4150,9 @@ async function startCommand(args, config) {
|
|
|
3199
4150
|
crossplay: args.crossplay ?? config.server.crossplay,
|
|
3200
4151
|
savedir: args.savedir ?? config.server.savedir,
|
|
3201
4152
|
saveinterval: config.server.saveinterval,
|
|
3202
|
-
backups: config.server.backups
|
|
4153
|
+
backups: config.server.backups,
|
|
4154
|
+
// Always use detached mode for stability
|
|
4155
|
+
detached: true
|
|
3203
4156
|
};
|
|
3204
4157
|
console.log(`
|
|
3205
4158
|
Starting ${serverConfig.name}...`);
|
|
@@ -3207,6 +4160,7 @@ Starting ${serverConfig.name}...`);
|
|
|
3207
4160
|
console.log(` Port: ${serverConfig.port}`);
|
|
3208
4161
|
console.log(` Public: ${serverConfig.public}`);
|
|
3209
4162
|
console.log(` Crossplay: ${serverConfig.crossplay}`);
|
|
4163
|
+
console.log(` Mode: Detached (server continues after terminal exits)`);
|
|
3210
4164
|
console.log("");
|
|
3211
4165
|
activeWatchdog = new Watchdog(
|
|
3212
4166
|
serverConfig,
|
|
@@ -3220,6 +4174,12 @@ Starting ${serverConfig.name}...`);
|
|
|
3220
4174
|
{
|
|
3221
4175
|
onStateChange: (state) => {
|
|
3222
4176
|
console.log(`[Server] State: ${state}`);
|
|
4177
|
+
if (state === "online") {
|
|
4178
|
+
console.log("\n\u2713 Server is now online!");
|
|
4179
|
+
console.log(" The server will continue running in the background.");
|
|
4180
|
+
console.log(" Use 'valheim-dsm stop' to stop it.");
|
|
4181
|
+
console.log(" Use 'valheim-dsm' (TUI) to manage it.\n");
|
|
4182
|
+
}
|
|
3223
4183
|
},
|
|
3224
4184
|
onLog: (line) => {
|
|
3225
4185
|
console.log(`[Server] ${line}`);
|
|
@@ -3248,18 +4208,34 @@ Starting ${serverConfig.name}...`);
|
|
|
3248
4208
|
setupShutdownHandlers();
|
|
3249
4209
|
try {
|
|
3250
4210
|
await activeWatchdog.start();
|
|
3251
|
-
const
|
|
3252
|
-
if (
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3256
|
-
world: serverConfig.world,
|
|
3257
|
-
port: serverConfig.port
|
|
3258
|
-
});
|
|
4211
|
+
const logPath = activeWatchdog.serverProcess.logFilePath;
|
|
4212
|
+
if (logPath) {
|
|
4213
|
+
console.log(`
|
|
4214
|
+
Server log: ${logPath}`);
|
|
3259
4215
|
}
|
|
3260
|
-
console.log("\nServer
|
|
3261
|
-
|
|
3262
|
-
|
|
4216
|
+
console.log("\nServer is starting in detached mode.");
|
|
4217
|
+
console.log(
|
|
4218
|
+
"Press Ctrl+C to stop monitoring (server will keep running).\n"
|
|
4219
|
+
);
|
|
4220
|
+
const timeout = 12e4;
|
|
4221
|
+
const startTime = Date.now();
|
|
4222
|
+
while (Date.now() - startTime < timeout) {
|
|
4223
|
+
const state = activeWatchdog.serverProcess.currentState;
|
|
4224
|
+
if (state === "online") {
|
|
4225
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
4226
|
+
break;
|
|
4227
|
+
}
|
|
4228
|
+
if (state === "crashed" || state === "offline") {
|
|
4229
|
+
console.error("\nServer failed to start.");
|
|
4230
|
+
await cleanupAndExit(1);
|
|
4231
|
+
return;
|
|
4232
|
+
}
|
|
4233
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
4234
|
+
}
|
|
4235
|
+
console.log("\nDetaching from server...");
|
|
4236
|
+
await activeWatchdog.serverProcess.detach();
|
|
4237
|
+
activeWatchdog = null;
|
|
4238
|
+
process.exit(0);
|
|
3263
4239
|
} catch (error2) {
|
|
3264
4240
|
console.error(`
|
|
3265
4241
|
Failed to start server: ${error2.message}`);
|
|
@@ -3272,14 +4248,26 @@ function getActiveWatchdog() {
|
|
|
3272
4248
|
function clearActiveWatchdog() {
|
|
3273
4249
|
activeWatchdog = null;
|
|
3274
4250
|
}
|
|
4251
|
+
async function cleanupAndExit(code) {
|
|
4252
|
+
if (activeWatchdog) {
|
|
4253
|
+
try {
|
|
4254
|
+
await activeWatchdog.serverProcess.detach();
|
|
4255
|
+
} catch {
|
|
4256
|
+
}
|
|
4257
|
+
activeWatchdog = null;
|
|
4258
|
+
}
|
|
4259
|
+
process.exit(code);
|
|
4260
|
+
}
|
|
3275
4261
|
function setupShutdownHandlers() {
|
|
3276
4262
|
const shutdown = async () => {
|
|
3277
|
-
console.log("\n\
|
|
4263
|
+
console.log("\n\nDetaching from server (it will keep running)...");
|
|
3278
4264
|
if (activeWatchdog) {
|
|
3279
|
-
|
|
4265
|
+
try {
|
|
4266
|
+
await activeWatchdog.serverProcess.detach();
|
|
4267
|
+
} catch {
|
|
4268
|
+
}
|
|
3280
4269
|
activeWatchdog = null;
|
|
3281
4270
|
}
|
|
3282
|
-
await removePidFile();
|
|
3283
4271
|
process.exit(0);
|
|
3284
4272
|
};
|
|
3285
4273
|
process.on("SIGINT", shutdown);
|
|
@@ -3290,9 +4278,9 @@ function setupShutdownHandlers() {
|
|
|
3290
4278
|
|
|
3291
4279
|
// src/cli/commands/stop.ts
|
|
3292
4280
|
async function stopCommand(args) {
|
|
4281
|
+
const timeout = args.timeout ?? 3e4;
|
|
3293
4282
|
const watchdog2 = getActiveWatchdog();
|
|
3294
4283
|
if (watchdog2) {
|
|
3295
|
-
const timeout = args.timeout ?? 3e4;
|
|
3296
4284
|
if (args.force) {
|
|
3297
4285
|
console.log("\nForce stopping server...");
|
|
3298
4286
|
await watchdog2.kill();
|
|
@@ -3312,34 +4300,53 @@ Stopping server (timeout: ${timeout}ms)...`);
|
|
|
3312
4300
|
console.log("\nNote: Run 'valheim-dsm start' to start a server.");
|
|
3313
4301
|
return;
|
|
3314
4302
|
}
|
|
3315
|
-
const { pid, world, port, startedAt } = runningServer;
|
|
4303
|
+
const { pid, world, port, startedAt, detached, logFile } = runningServer;
|
|
3316
4304
|
console.log(`
|
|
3317
4305
|
Found running server:`);
|
|
3318
4306
|
console.log(` PID: ${pid}`);
|
|
3319
4307
|
console.log(` World: ${world}`);
|
|
3320
4308
|
console.log(` Port: ${port}`);
|
|
3321
4309
|
console.log(` Started: ${new Date(startedAt).toLocaleString()}`);
|
|
4310
|
+
console.log(` Mode: ${detached ? "Detached" : "Attached"}`);
|
|
4311
|
+
if (logFile) {
|
|
4312
|
+
console.log(` Log: ${logFile}`);
|
|
4313
|
+
}
|
|
3322
4314
|
if (!isProcessRunning(pid)) {
|
|
3323
4315
|
console.log("\nServer process is no longer running. Cleaning up...");
|
|
3324
4316
|
await removePidFile();
|
|
3325
4317
|
return;
|
|
3326
4318
|
}
|
|
4319
|
+
const platform = getPlatform();
|
|
3327
4320
|
if (args.force) {
|
|
3328
4321
|
console.log("\nForce killing server...");
|
|
3329
|
-
killProcess(pid,
|
|
4322
|
+
killProcess(pid, platform !== "windows");
|
|
3330
4323
|
} else {
|
|
3331
4324
|
console.log("\nSending stop signal...");
|
|
3332
4325
|
killProcess(pid, false);
|
|
3333
|
-
const timeout = args.timeout ?? 3e4;
|
|
3334
4326
|
const startTime = Date.now();
|
|
4327
|
+
let dots = 0;
|
|
3335
4328
|
while (isProcessRunning(pid) && Date.now() - startTime < timeout) {
|
|
3336
4329
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
3337
4330
|
process.stdout.write(".");
|
|
4331
|
+
dots++;
|
|
3338
4332
|
}
|
|
3339
|
-
console.log();
|
|
4333
|
+
if (dots > 0) console.log();
|
|
3340
4334
|
if (isProcessRunning(pid)) {
|
|
3341
4335
|
console.log("Server did not stop gracefully, force killing...");
|
|
3342
|
-
killProcess(pid,
|
|
4336
|
+
killProcess(pid, platform !== "windows");
|
|
4337
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
4338
|
+
if (isProcessRunning(pid)) {
|
|
4339
|
+
console.error(
|
|
4340
|
+
"Failed to stop server. Process may require manual termination."
|
|
4341
|
+
);
|
|
4342
|
+
console.log(` PID: ${pid}`);
|
|
4343
|
+
if (platform === "windows") {
|
|
4344
|
+
console.log(` Try: taskkill /F /PID ${pid}`);
|
|
4345
|
+
} else {
|
|
4346
|
+
console.log(` Try: kill -9 ${pid}`);
|
|
4347
|
+
}
|
|
4348
|
+
return;
|
|
4349
|
+
}
|
|
3343
4350
|
}
|
|
3344
4351
|
}
|
|
3345
4352
|
await removePidFile();
|
|
@@ -3853,13 +4860,16 @@ Error deleting world: ${error2.message}`);
|
|
|
3853
4860
|
}
|
|
3854
4861
|
}
|
|
3855
4862
|
|
|
4863
|
+
// src/mod.ts
|
|
4864
|
+
init_mod();
|
|
4865
|
+
|
|
3856
4866
|
// src/tui/mod.ts
|
|
3857
4867
|
import { withFullScreen } from "fullscreen-ink";
|
|
3858
4868
|
import React2 from "react";
|
|
3859
4869
|
|
|
3860
4870
|
// src/tui/App.tsx
|
|
3861
|
-
import { Box as
|
|
3862
|
-
import { useEffect as
|
|
4871
|
+
import { Box as Box22, useApp, useInput as useInput15 } from "ink";
|
|
4872
|
+
import { useEffect as useEffect14 } from "react";
|
|
3863
4873
|
|
|
3864
4874
|
// src/tui/components/Header.tsx
|
|
3865
4875
|
import { Box as Box2, Text as Text2 } from "ink";
|
|
@@ -3868,7 +4878,7 @@ import { useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useSta
|
|
|
3868
4878
|
// package.json
|
|
3869
4879
|
var package_default = {
|
|
3870
4880
|
name: "valheim-oz-dsm",
|
|
3871
|
-
version: "1.
|
|
4881
|
+
version: "1.8.0",
|
|
3872
4882
|
description: "Land of OZ - Valheim Dedicated Server Manager",
|
|
3873
4883
|
type: "module",
|
|
3874
4884
|
bin: {
|
|
@@ -3877,7 +4887,6 @@ var package_default = {
|
|
|
3877
4887
|
main: "./dist/main.js",
|
|
3878
4888
|
files: [
|
|
3879
4889
|
"dist",
|
|
3880
|
-
"patches",
|
|
3881
4890
|
"README.md",
|
|
3882
4891
|
"LICENSE",
|
|
3883
4892
|
"CHANGELOG.md"
|
|
@@ -3898,12 +4907,11 @@ var package_default = {
|
|
|
3898
4907
|
"lint:fix": "biome check --write .",
|
|
3899
4908
|
format: "biome format --write .",
|
|
3900
4909
|
typecheck: "tsc --noEmit",
|
|
3901
|
-
postinstall: "patch-package",
|
|
3902
4910
|
prepare: "tsx scripts/install-hooks.ts",
|
|
3903
4911
|
prepublishOnly: "npm run typecheck && npm run lint && npm test && npm run build"
|
|
3904
4912
|
},
|
|
3905
4913
|
dependencies: {
|
|
3906
|
-
"@caleb-collar/steamcmd": "^1.1.
|
|
4914
|
+
"@caleb-collar/steamcmd": "^1.1.1",
|
|
3907
4915
|
conf: "^13.0.1",
|
|
3908
4916
|
"fullscreen-ink": "^0.1.0",
|
|
3909
4917
|
ink: "^6.6.0",
|
|
@@ -3916,7 +4924,6 @@ var package_default = {
|
|
|
3916
4924
|
"@types/node": "^22.13.1",
|
|
3917
4925
|
"@types/react": "^19.2.10",
|
|
3918
4926
|
"@vitest/coverage-v8": "^3.2.4",
|
|
3919
|
-
"patch-package": "^8.0.1",
|
|
3920
4927
|
tsup: "^8.3.6",
|
|
3921
4928
|
tsx: "^4.19.2",
|
|
3922
4929
|
typescript: "^5.7.3",
|
|
@@ -4038,13 +5045,13 @@ var useStore = create((set) => ({
|
|
|
4038
5045
|
},
|
|
4039
5046
|
// Initial RCON state
|
|
4040
5047
|
rcon: {
|
|
4041
|
-
enabled:
|
|
5048
|
+
enabled: true,
|
|
4042
5049
|
connected: false,
|
|
4043
5050
|
port: 25575,
|
|
4044
|
-
password: "",
|
|
5051
|
+
password: "valheim-rcon",
|
|
4045
5052
|
host: "localhost",
|
|
4046
5053
|
timeout: 5e3,
|
|
4047
|
-
autoReconnect:
|
|
5054
|
+
autoReconnect: true
|
|
4048
5055
|
},
|
|
4049
5056
|
// Initial worlds state
|
|
4050
5057
|
worlds: {
|
|
@@ -4093,6 +5100,9 @@ var useStore = create((set) => ({
|
|
|
4093
5100
|
players: state.server.players.filter((p) => p !== name)
|
|
4094
5101
|
}
|
|
4095
5102
|
})),
|
|
5103
|
+
setPlayers: (players) => set((state) => ({
|
|
5104
|
+
server: { ...state.server, players }
|
|
5105
|
+
})),
|
|
4096
5106
|
incrementUptime: () => set((state) => ({
|
|
4097
5107
|
server: { ...state.server, uptime: state.server.uptime + 1 }
|
|
4098
5108
|
})),
|
|
@@ -42985,22 +43995,328 @@ var Console = () => {
|
|
|
42985
43995
|
};
|
|
42986
43996
|
|
|
42987
43997
|
// src/tui/screens/Dashboard.tsx
|
|
42988
|
-
import { Box as
|
|
42989
|
-
import { useEffect as
|
|
43998
|
+
import { Box as Box16, Text as Text16, useInput as useInput9 } from "ink";
|
|
43999
|
+
import { useEffect as useEffect9, useState as useState11 } from "react";
|
|
42990
44000
|
|
|
42991
|
-
// src/tui/components/
|
|
44001
|
+
// src/tui/components/EventManager.tsx
|
|
44002
|
+
init_mod();
|
|
42992
44003
|
import { Box as Box9, Text as Text9, useInput as useInput3 } from "ink";
|
|
44004
|
+
import { useState as useState5 } from "react";
|
|
42993
44005
|
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
44006
|
+
var EVENT_LIST = [
|
|
44007
|
+
{
|
|
44008
|
+
key: ValheimEvents.ARMY_EIKTHYR,
|
|
44009
|
+
name: "Eikthyr Army",
|
|
44010
|
+
description: "Eikthyr rallies the creatures"
|
|
44011
|
+
},
|
|
44012
|
+
{
|
|
44013
|
+
key: ValheimEvents.ARMY_THEELDER,
|
|
44014
|
+
name: "The Elder's Hunt",
|
|
44015
|
+
description: "The Elder is hunting you"
|
|
44016
|
+
},
|
|
44017
|
+
{
|
|
44018
|
+
key: ValheimEvents.ARMY_BONEMASS,
|
|
44019
|
+
name: "Bonemass Army",
|
|
44020
|
+
description: "A foul smell from the swamp"
|
|
44021
|
+
},
|
|
44022
|
+
{
|
|
44023
|
+
key: ValheimEvents.ARMY_MODER,
|
|
44024
|
+
name: "Moder's Hunt",
|
|
44025
|
+
description: "You are being hunted"
|
|
44026
|
+
},
|
|
44027
|
+
{
|
|
44028
|
+
key: ValheimEvents.ARMY_GOBLIN,
|
|
44029
|
+
name: "Goblin Horde",
|
|
44030
|
+
description: "The horde is attacking"
|
|
44031
|
+
},
|
|
44032
|
+
{
|
|
44033
|
+
key: ValheimEvents.FORESTTROLLS,
|
|
44034
|
+
name: "Forest Trolls",
|
|
44035
|
+
description: "The forest is moving"
|
|
44036
|
+
},
|
|
44037
|
+
{
|
|
44038
|
+
key: ValheimEvents.SKELETONS,
|
|
44039
|
+
name: "Skeletons",
|
|
44040
|
+
description: "Skeleton surprise"
|
|
44041
|
+
},
|
|
44042
|
+
{
|
|
44043
|
+
key: ValheimEvents.BLOBS,
|
|
44044
|
+
name: "Blobs",
|
|
44045
|
+
description: "Blob attack"
|
|
44046
|
+
},
|
|
44047
|
+
{
|
|
44048
|
+
key: ValheimEvents.WOLVES,
|
|
44049
|
+
name: "Wolves",
|
|
44050
|
+
description: "You are being hunted"
|
|
44051
|
+
},
|
|
44052
|
+
{
|
|
44053
|
+
key: ValheimEvents.BATS,
|
|
44054
|
+
name: "Bats",
|
|
44055
|
+
description: "Bat swarm"
|
|
44056
|
+
},
|
|
44057
|
+
{
|
|
44058
|
+
key: ValheimEvents.SERPENTS,
|
|
44059
|
+
name: "Serpents",
|
|
44060
|
+
description: "Sea serpents"
|
|
44061
|
+
}
|
|
44062
|
+
];
|
|
44063
|
+
var EventManager = ({ onClose }) => {
|
|
44064
|
+
const rconConnected = useStore((s) => s.rcon.connected);
|
|
44065
|
+
const addLog = useStore((s) => s.actions.addLog);
|
|
44066
|
+
const [selectedIndex, setSelectedIndex] = useState5(0);
|
|
44067
|
+
const handleTriggerEvent = async (eventKey, eventName) => {
|
|
44068
|
+
const response = await rconManager.triggerEvent(eventKey);
|
|
44069
|
+
if (response) {
|
|
44070
|
+
addLog("info", `Triggered event: ${eventName}`);
|
|
44071
|
+
} else {
|
|
44072
|
+
addLog("error", `Failed to trigger event: ${eventName}`);
|
|
44073
|
+
}
|
|
44074
|
+
};
|
|
44075
|
+
const handleRandomEvent = async () => {
|
|
44076
|
+
const response = await rconManager.triggerRandomEvent();
|
|
44077
|
+
if (response) {
|
|
44078
|
+
addLog("info", `Triggered random event: ${response}`);
|
|
44079
|
+
} else {
|
|
44080
|
+
addLog("error", "Failed to trigger random event");
|
|
44081
|
+
}
|
|
44082
|
+
};
|
|
44083
|
+
const handleStopEvent = async () => {
|
|
44084
|
+
const response = await rconManager.stopEvent();
|
|
44085
|
+
if (response) {
|
|
44086
|
+
addLog("info", `Stopped event: ${response}`);
|
|
44087
|
+
} else {
|
|
44088
|
+
addLog("error", "Failed to stop event");
|
|
44089
|
+
}
|
|
44090
|
+
};
|
|
44091
|
+
useInput3((input, key) => {
|
|
44092
|
+
if (key.escape || input === "q" || input === "Q") {
|
|
44093
|
+
onClose();
|
|
44094
|
+
return;
|
|
44095
|
+
}
|
|
44096
|
+
if (key.upArrow) {
|
|
44097
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
44098
|
+
} else if (key.downArrow) {
|
|
44099
|
+
setSelectedIndex(Math.min(EVENT_LIST.length - 1, selectedIndex + 1));
|
|
44100
|
+
}
|
|
44101
|
+
if (key.return) {
|
|
44102
|
+
const selectedEvent = EVENT_LIST[selectedIndex];
|
|
44103
|
+
handleTriggerEvent(selectedEvent.key, selectedEvent.name);
|
|
44104
|
+
} else if (input === "r" || input === "R") {
|
|
44105
|
+
handleRandomEvent();
|
|
44106
|
+
} else if (input === "x" || input === "X") {
|
|
44107
|
+
handleStopEvent();
|
|
44108
|
+
}
|
|
44109
|
+
});
|
|
44110
|
+
if (!rconConnected) {
|
|
44111
|
+
return /* @__PURE__ */ jsxs8(
|
|
44112
|
+
Box9,
|
|
44113
|
+
{
|
|
44114
|
+
flexDirection: "column",
|
|
44115
|
+
borderStyle: "round",
|
|
44116
|
+
borderColor: theme.error,
|
|
44117
|
+
padding: 1,
|
|
44118
|
+
width: 60,
|
|
44119
|
+
children: [
|
|
44120
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.error, children: "\u26A0 Event Manager" }) }),
|
|
44121
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "RCON not connected" }),
|
|
44122
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Event management requires RCON connection" }),
|
|
44123
|
+
/* @__PURE__ */ jsx9(Box9, { marginTop: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "[Esc/Q] Close" }) })
|
|
44124
|
+
]
|
|
44125
|
+
}
|
|
44126
|
+
);
|
|
44127
|
+
}
|
|
44128
|
+
return /* @__PURE__ */ jsxs8(
|
|
44129
|
+
Box9,
|
|
44130
|
+
{
|
|
44131
|
+
flexDirection: "column",
|
|
44132
|
+
borderStyle: "round",
|
|
44133
|
+
borderColor: theme.primary,
|
|
44134
|
+
padding: 1,
|
|
44135
|
+
width: 70,
|
|
44136
|
+
height: 25,
|
|
44137
|
+
children: [
|
|
44138
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { bold: true, color: theme.primary, children: "Event Manager" }) }),
|
|
44139
|
+
/* @__PURE__ */ jsx9(Box9, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] Trigger, [R] Random, [X] Stop" }) }),
|
|
44140
|
+
/* @__PURE__ */ jsx9(Box9, { flexDirection: "column", flexGrow: 1, children: EVENT_LIST.map((event, index) => /* @__PURE__ */ jsxs8(Box9, { marginLeft: 1, children: [
|
|
44141
|
+
/* @__PURE__ */ jsxs8(
|
|
44142
|
+
Text9,
|
|
44143
|
+
{
|
|
44144
|
+
color: index === selectedIndex ? theme.primary : theme.secondary,
|
|
44145
|
+
bold: index === selectedIndex,
|
|
44146
|
+
children: [
|
|
44147
|
+
index === selectedIndex ? "\u2192 " : " ",
|
|
44148
|
+
event.name
|
|
44149
|
+
]
|
|
44150
|
+
}
|
|
44151
|
+
),
|
|
44152
|
+
/* @__PURE__ */ jsxs8(Text9, { dimColor: true, children: [
|
|
44153
|
+
" - ",
|
|
44154
|
+
event.description
|
|
44155
|
+
] })
|
|
44156
|
+
] }, event.key)) }),
|
|
44157
|
+
/* @__PURE__ */ jsxs8(Box9, { marginTop: 1, flexDirection: "column", children: [
|
|
44158
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "[Enter] Trigger Selected" }),
|
|
44159
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "[R] Random Event [X] Stop Event" }),
|
|
44160
|
+
/* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "[Esc/Q] Close" })
|
|
44161
|
+
] })
|
|
44162
|
+
]
|
|
44163
|
+
}
|
|
44164
|
+
);
|
|
44165
|
+
};
|
|
44166
|
+
|
|
44167
|
+
// src/tui/components/GlobalKeysManager.tsx
|
|
44168
|
+
init_mod();
|
|
44169
|
+
import { Box as Box10, Text as Text10, useInput as useInput4 } from "ink";
|
|
44170
|
+
import { useEffect as useEffect5, useState as useState6 } from "react";
|
|
44171
|
+
import { Fragment, jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
44172
|
+
var BOSS_KEYS = [
|
|
44173
|
+
{ key: ValheimGlobalKeys.DEFEATED_EIKTHYR, name: "Eikthyr" },
|
|
44174
|
+
{ key: ValheimGlobalKeys.DEFEATED_GDKING, name: "The Elder" },
|
|
44175
|
+
{ key: ValheimGlobalKeys.DEFEATED_BONEMASS, name: "Bonemass" },
|
|
44176
|
+
{ key: ValheimGlobalKeys.DEFEATED_DRAGON, name: "Moder" },
|
|
44177
|
+
{ key: ValheimGlobalKeys.DEFEATED_GOBLINKING, name: "Yagluth" },
|
|
44178
|
+
{ key: ValheimGlobalKeys.DEFEATED_QUEEN, name: "The Queen" }
|
|
44179
|
+
];
|
|
44180
|
+
var GlobalKeysManager = ({ onClose }) => {
|
|
44181
|
+
const rconConnected = useStore((s) => s.rcon.connected);
|
|
44182
|
+
const addLog = useStore((s) => s.actions.addLog);
|
|
44183
|
+
const [selectedIndex, setSelectedIndex] = useState6(0);
|
|
44184
|
+
const [activeKeys, setActiveKeys] = useState6([]);
|
|
44185
|
+
const [loading, setLoading] = useState6(true);
|
|
44186
|
+
useEffect5(() => {
|
|
44187
|
+
const loadKeys = async () => {
|
|
44188
|
+
setLoading(true);
|
|
44189
|
+
const keys = await rconManager.listGlobalKeys();
|
|
44190
|
+
setActiveKeys(keys);
|
|
44191
|
+
setLoading(false);
|
|
44192
|
+
};
|
|
44193
|
+
if (rconConnected) {
|
|
44194
|
+
loadKeys();
|
|
44195
|
+
}
|
|
44196
|
+
}, [rconConnected]);
|
|
44197
|
+
const isBossDefeated = (bossKey) => {
|
|
44198
|
+
return activeKeys.some(
|
|
44199
|
+
(key) => key.toLowerCase().includes(bossKey.toLowerCase())
|
|
44200
|
+
);
|
|
44201
|
+
};
|
|
44202
|
+
const handleToggleKey = async (keyName, isActive) => {
|
|
44203
|
+
if (isActive) {
|
|
44204
|
+
const response = await rconManager.removeGlobalKey(keyName);
|
|
44205
|
+
if (response) {
|
|
44206
|
+
addLog("info", `Removed key: ${keyName}`);
|
|
44207
|
+
const keys = await rconManager.listGlobalKeys();
|
|
44208
|
+
setActiveKeys(keys);
|
|
44209
|
+
} else {
|
|
44210
|
+
addLog("error", `Failed to remove key: ${keyName}`);
|
|
44211
|
+
}
|
|
44212
|
+
} else {
|
|
44213
|
+
const response = await rconManager.setGlobalKey(keyName);
|
|
44214
|
+
if (response) {
|
|
44215
|
+
addLog("info", `Set key: ${keyName}`);
|
|
44216
|
+
const keys = await rconManager.listGlobalKeys();
|
|
44217
|
+
setActiveKeys(keys);
|
|
44218
|
+
} else {
|
|
44219
|
+
addLog("error", `Failed to set key: ${keyName}`);
|
|
44220
|
+
}
|
|
44221
|
+
}
|
|
44222
|
+
};
|
|
44223
|
+
const handleResetAll = async () => {
|
|
44224
|
+
const response = await rconManager.resetGlobalKeys();
|
|
44225
|
+
if (response) {
|
|
44226
|
+
addLog("warn", "Reset all global keys");
|
|
44227
|
+
setActiveKeys([]);
|
|
44228
|
+
} else {
|
|
44229
|
+
addLog("error", "Failed to reset keys");
|
|
44230
|
+
}
|
|
44231
|
+
};
|
|
44232
|
+
useInput4((input, key) => {
|
|
44233
|
+
if (key.escape || input === "q" || input === "Q") {
|
|
44234
|
+
onClose();
|
|
44235
|
+
return;
|
|
44236
|
+
}
|
|
44237
|
+
if (key.upArrow) {
|
|
44238
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
44239
|
+
} else if (key.downArrow) {
|
|
44240
|
+
setSelectedIndex(Math.min(BOSS_KEYS.length - 1, selectedIndex + 1));
|
|
44241
|
+
}
|
|
44242
|
+
if (input === " " || key.return) {
|
|
44243
|
+
const selected = BOSS_KEYS[selectedIndex];
|
|
44244
|
+
const isActive = isBossDefeated(selected.key);
|
|
44245
|
+
handleToggleKey(selected.key, isActive);
|
|
44246
|
+
} else if (input === "r" || input === "R") {
|
|
44247
|
+
handleResetAll();
|
|
44248
|
+
}
|
|
44249
|
+
});
|
|
44250
|
+
if (!rconConnected) {
|
|
44251
|
+
return /* @__PURE__ */ jsxs9(
|
|
44252
|
+
Box10,
|
|
44253
|
+
{
|
|
44254
|
+
flexDirection: "column",
|
|
44255
|
+
borderStyle: "round",
|
|
44256
|
+
borderColor: theme.error,
|
|
44257
|
+
padding: 1,
|
|
44258
|
+
width: 60,
|
|
44259
|
+
children: [
|
|
44260
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.error, children: "\u26A0 Global Keys Manager" }) }),
|
|
44261
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "RCON not connected" }),
|
|
44262
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Key management requires RCON connection" }),
|
|
44263
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "[Esc/Q] Close" }) })
|
|
44264
|
+
]
|
|
44265
|
+
}
|
|
44266
|
+
);
|
|
44267
|
+
}
|
|
44268
|
+
return /* @__PURE__ */ jsxs9(
|
|
44269
|
+
Box10,
|
|
44270
|
+
{
|
|
44271
|
+
flexDirection: "column",
|
|
44272
|
+
borderStyle: "round",
|
|
44273
|
+
borderColor: theme.primary,
|
|
44274
|
+
padding: 1,
|
|
44275
|
+
width: 60,
|
|
44276
|
+
children: [
|
|
44277
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { bold: true, color: theme.primary, children: "Boss Progression" }) }),
|
|
44278
|
+
loading ? /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Loading keys..." }) : /* @__PURE__ */ jsxs9(Fragment, { children: [
|
|
44279
|
+
/* @__PURE__ */ jsx10(Box10, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "Use \u2191\u2193 to select, [Space/Enter] to toggle" }) }),
|
|
44280
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "column", children: BOSS_KEYS.map((boss, index) => {
|
|
44281
|
+
const defeated = isBossDefeated(boss.key);
|
|
44282
|
+
return /* @__PURE__ */ jsx10(Box10, { marginLeft: 1, children: /* @__PURE__ */ jsxs9(
|
|
44283
|
+
Text10,
|
|
44284
|
+
{
|
|
44285
|
+
color: index === selectedIndex ? theme.primary : theme.secondary,
|
|
44286
|
+
bold: index === selectedIndex,
|
|
44287
|
+
children: [
|
|
44288
|
+
index === selectedIndex ? "\u2192 " : " ",
|
|
44289
|
+
defeated ? "\u2611" : "\u2610",
|
|
44290
|
+
" ",
|
|
44291
|
+
boss.name
|
|
44292
|
+
]
|
|
44293
|
+
}
|
|
44294
|
+
) }, boss.key);
|
|
44295
|
+
}) }),
|
|
44296
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 2, flexDirection: "column", children: /* @__PURE__ */ jsx10(Text10, { color: theme.warning, children: "\u26A0\uFE0F Warning: Affects world progression" }) })
|
|
44297
|
+
] }),
|
|
44298
|
+
/* @__PURE__ */ jsxs9(Box10, { marginTop: 1, flexDirection: "column", children: [
|
|
44299
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "[Space/Enter] Toggle [R] Reset All" }),
|
|
44300
|
+
/* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "[Esc/Q] Close" })
|
|
44301
|
+
] })
|
|
44302
|
+
]
|
|
44303
|
+
}
|
|
44304
|
+
);
|
|
44305
|
+
};
|
|
44306
|
+
|
|
44307
|
+
// src/tui/components/Modal.tsx
|
|
44308
|
+
import { Box as Box11, Text as Text11, useInput as useInput5 } from "ink";
|
|
44309
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
42994
44310
|
var Modal = (props) => {
|
|
42995
44311
|
const { title, children, width = 50 } = props;
|
|
42996
44312
|
const closeModal = useStore((s) => s.actions.closeModal);
|
|
42997
|
-
|
|
44313
|
+
useInput5((_input, key) => {
|
|
42998
44314
|
if (key.escape) {
|
|
42999
44315
|
closeModal();
|
|
43000
44316
|
}
|
|
43001
44317
|
});
|
|
43002
|
-
return /* @__PURE__ */
|
|
43003
|
-
|
|
44318
|
+
return /* @__PURE__ */ jsxs10(
|
|
44319
|
+
Box11,
|
|
43004
44320
|
{
|
|
43005
44321
|
flexDirection: "column",
|
|
43006
44322
|
width,
|
|
@@ -43010,36 +44326,36 @@ var Modal = (props) => {
|
|
|
43010
44326
|
paddingX: 2,
|
|
43011
44327
|
paddingY: 1,
|
|
43012
44328
|
children: [
|
|
43013
|
-
/* @__PURE__ */
|
|
43014
|
-
/* @__PURE__ */
|
|
43015
|
-
/* @__PURE__ */
|
|
44329
|
+
/* @__PURE__ */ jsxs10(Box11, { justifyContent: "space-between", marginBottom: 1, children: [
|
|
44330
|
+
/* @__PURE__ */ jsx11(Text11, { bold: true, color: theme.primary, children: title }),
|
|
44331
|
+
/* @__PURE__ */ jsx11(Text11, { dimColor: true, children: "[ESC to close]" })
|
|
43016
44332
|
] }),
|
|
43017
|
-
/* @__PURE__ */
|
|
44333
|
+
/* @__PURE__ */ jsx11(Box11, { flexDirection: "column", children })
|
|
43018
44334
|
]
|
|
43019
44335
|
}
|
|
43020
44336
|
);
|
|
43021
44337
|
};
|
|
43022
44338
|
var ConfirmModal = (props) => {
|
|
43023
44339
|
const { message, onConfirm, onCancel } = props;
|
|
43024
|
-
|
|
44340
|
+
useInput5((input, key) => {
|
|
43025
44341
|
if (input === "y" || input === "Y") {
|
|
43026
44342
|
onConfirm();
|
|
43027
44343
|
} else if (input === "n" || input === "N" || key.escape) {
|
|
43028
44344
|
onCancel();
|
|
43029
44345
|
}
|
|
43030
44346
|
});
|
|
43031
|
-
return /* @__PURE__ */
|
|
43032
|
-
/* @__PURE__ */
|
|
43033
|
-
/* @__PURE__ */
|
|
43034
|
-
/* @__PURE__ */
|
|
43035
|
-
/* @__PURE__ */
|
|
43036
|
-
/* @__PURE__ */
|
|
44347
|
+
return /* @__PURE__ */ jsxs10(Modal, { title: "Confirm", width: 40, children: [
|
|
44348
|
+
/* @__PURE__ */ jsx11(Text11, { children: message }),
|
|
44349
|
+
/* @__PURE__ */ jsxs10(Box11, { marginTop: 1, children: [
|
|
44350
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.success, children: "[Y] Yes" }),
|
|
44351
|
+
/* @__PURE__ */ jsx11(Text11, {}),
|
|
44352
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.error, children: "[N] No" })
|
|
43037
44353
|
] })
|
|
43038
44354
|
] });
|
|
43039
44355
|
};
|
|
43040
44356
|
var DeleteWorldModal = (props) => {
|
|
43041
44357
|
const { worldName, backupCount, onConfirm, onCancel } = props;
|
|
43042
|
-
|
|
44358
|
+
useInput5((input, key) => {
|
|
43043
44359
|
if (backupCount > 0) {
|
|
43044
44360
|
if (input === "y" || input === "Y") {
|
|
43045
44361
|
onConfirm(true);
|
|
@@ -43057,105 +44373,438 @@ var DeleteWorldModal = (props) => {
|
|
|
43057
44373
|
}
|
|
43058
44374
|
});
|
|
43059
44375
|
if (backupCount > 0) {
|
|
43060
|
-
return /* @__PURE__ */
|
|
43061
|
-
/* @__PURE__ */
|
|
44376
|
+
return /* @__PURE__ */ jsxs10(Modal, { title: "Delete World", width: 50, children: [
|
|
44377
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
43062
44378
|
'Delete world "',
|
|
43063
44379
|
worldName,
|
|
43064
44380
|
'"?'
|
|
43065
44381
|
] }),
|
|
43066
|
-
/* @__PURE__ */
|
|
44382
|
+
/* @__PURE__ */ jsx11(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs10(Text11, { dimColor: true, children: [
|
|
43067
44383
|
"This world has ",
|
|
43068
44384
|
backupCount,
|
|
43069
44385
|
" backup",
|
|
43070
44386
|
backupCount === 1 ? "" : "s",
|
|
43071
44387
|
"."
|
|
43072
44388
|
] }) }),
|
|
43073
|
-
/* @__PURE__ */
|
|
43074
|
-
/* @__PURE__ */
|
|
43075
|
-
/* @__PURE__ */
|
|
43076
|
-
/* @__PURE__ */
|
|
44389
|
+
/* @__PURE__ */ jsxs10(Box11, { marginTop: 1, flexDirection: "column", children: [
|
|
44390
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.success, children: "[Y] Delete world and backups" }),
|
|
44391
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.warning, children: "[N] Delete world only" }),
|
|
44392
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.error, children: "[Esc] Cancel" })
|
|
43077
44393
|
] })
|
|
43078
44394
|
] });
|
|
43079
44395
|
}
|
|
43080
|
-
return /* @__PURE__ */
|
|
43081
|
-
/* @__PURE__ */
|
|
44396
|
+
return /* @__PURE__ */ jsxs10(Modal, { title: "Confirm", width: 40, children: [
|
|
44397
|
+
/* @__PURE__ */ jsxs10(Text11, { children: [
|
|
43082
44398
|
'Delete world "',
|
|
43083
44399
|
worldName,
|
|
43084
44400
|
'"? This cannot be undone!'
|
|
43085
44401
|
] }),
|
|
43086
|
-
/* @__PURE__ */
|
|
43087
|
-
/* @__PURE__ */
|
|
43088
|
-
/* @__PURE__ */
|
|
43089
|
-
/* @__PURE__ */
|
|
44402
|
+
/* @__PURE__ */ jsxs10(Box11, { marginTop: 1, children: [
|
|
44403
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.success, children: "[Y] Yes" }),
|
|
44404
|
+
/* @__PURE__ */ jsx11(Text11, {}),
|
|
44405
|
+
/* @__PURE__ */ jsx11(Text11, { color: theme.error, children: "[N] No" })
|
|
43090
44406
|
] })
|
|
43091
44407
|
] });
|
|
43092
44408
|
};
|
|
43093
44409
|
|
|
44410
|
+
// src/tui/components/PlayerManager.tsx
|
|
44411
|
+
init_mod();
|
|
44412
|
+
import { Box as Box12, Text as Text12, useInput as useInput6 } from "ink";
|
|
44413
|
+
import { useState as useState7 } from "react";
|
|
44414
|
+
import { Fragment as Fragment2, jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
44415
|
+
var PlayerManager = ({ onClose }) => {
|
|
44416
|
+
const players = useStore((s) => s.server.players);
|
|
44417
|
+
const rconConnected = useStore((s) => s.rcon.connected);
|
|
44418
|
+
const addLog = useStore((s) => s.actions.addLog);
|
|
44419
|
+
const [selectedIndex, setSelectedIndex] = useState7(0);
|
|
44420
|
+
const [view, setView] = useState7("players");
|
|
44421
|
+
const [bannedPlayers, setBannedPlayers] = useState7([]);
|
|
44422
|
+
const [loading, setLoading] = useState7(false);
|
|
44423
|
+
const loadBannedPlayers = async () => {
|
|
44424
|
+
setLoading(true);
|
|
44425
|
+
const banned = await rconManager.getBannedPlayers();
|
|
44426
|
+
setBannedPlayers(banned);
|
|
44427
|
+
setLoading(false);
|
|
44428
|
+
};
|
|
44429
|
+
const handleKick = async (playerName) => {
|
|
44430
|
+
const response = await rconManager.kickPlayer(playerName);
|
|
44431
|
+
if (response) {
|
|
44432
|
+
addLog("info", `Kicked ${playerName}: ${response}`);
|
|
44433
|
+
} else {
|
|
44434
|
+
addLog("error", `Failed to kick ${playerName}`);
|
|
44435
|
+
}
|
|
44436
|
+
};
|
|
44437
|
+
const handleBan = async (playerName) => {
|
|
44438
|
+
const response = await rconManager.banPlayer(playerName);
|
|
44439
|
+
if (response) {
|
|
44440
|
+
addLog("warn", `Banned ${playerName}: ${response}`);
|
|
44441
|
+
} else {
|
|
44442
|
+
addLog("error", `Failed to ban ${playerName}`);
|
|
44443
|
+
}
|
|
44444
|
+
};
|
|
44445
|
+
const handleUnban = async (playerIdentifier) => {
|
|
44446
|
+
const response = await rconManager.unbanPlayer(playerIdentifier);
|
|
44447
|
+
if (response) {
|
|
44448
|
+
addLog("info", `Unbanned ${playerIdentifier}: ${response}`);
|
|
44449
|
+
await loadBannedPlayers();
|
|
44450
|
+
} else {
|
|
44451
|
+
addLog("error", `Failed to unban ${playerIdentifier}`);
|
|
44452
|
+
}
|
|
44453
|
+
};
|
|
44454
|
+
useInput6((input, key) => {
|
|
44455
|
+
if (key.escape || input === "q" || input === "Q") {
|
|
44456
|
+
onClose();
|
|
44457
|
+
return;
|
|
44458
|
+
}
|
|
44459
|
+
if (input === "t" || input === "T") {
|
|
44460
|
+
if (view === "players") {
|
|
44461
|
+
setView("banned");
|
|
44462
|
+
loadBannedPlayers();
|
|
44463
|
+
} else {
|
|
44464
|
+
setView("players");
|
|
44465
|
+
}
|
|
44466
|
+
setSelectedIndex(0);
|
|
44467
|
+
return;
|
|
44468
|
+
}
|
|
44469
|
+
const currentList = view === "players" ? players : bannedPlayers;
|
|
44470
|
+
const maxIndex = Math.max(0, currentList.length - 1);
|
|
44471
|
+
if (key.upArrow) {
|
|
44472
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
44473
|
+
} else if (key.downArrow) {
|
|
44474
|
+
setSelectedIndex(Math.min(maxIndex, selectedIndex + 1));
|
|
44475
|
+
}
|
|
44476
|
+
if (view === "players" && players.length > 0) {
|
|
44477
|
+
const selectedPlayer = players[selectedIndex];
|
|
44478
|
+
if (input === "k" || input === "K") {
|
|
44479
|
+
handleKick(selectedPlayer);
|
|
44480
|
+
} else if (input === "b" || input === "B") {
|
|
44481
|
+
handleBan(selectedPlayer);
|
|
44482
|
+
}
|
|
44483
|
+
} else if (view === "banned" && bannedPlayers.length > 0) {
|
|
44484
|
+
const selectedBanned = bannedPlayers[selectedIndex];
|
|
44485
|
+
if (input === "u" || input === "U") {
|
|
44486
|
+
handleUnban(selectedBanned);
|
|
44487
|
+
}
|
|
44488
|
+
}
|
|
44489
|
+
});
|
|
44490
|
+
if (!rconConnected) {
|
|
44491
|
+
return /* @__PURE__ */ jsxs11(
|
|
44492
|
+
Box12,
|
|
44493
|
+
{
|
|
44494
|
+
flexDirection: "column",
|
|
44495
|
+
borderStyle: "round",
|
|
44496
|
+
borderColor: theme.error,
|
|
44497
|
+
padding: 1,
|
|
44498
|
+
width: 60,
|
|
44499
|
+
children: [
|
|
44500
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { bold: true, color: theme.error, children: "\u26A0 Player Management" }) }),
|
|
44501
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "RCON not connected" }),
|
|
44502
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Player management requires RCON connection" }),
|
|
44503
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "[Esc/Q] Close" }) })
|
|
44504
|
+
]
|
|
44505
|
+
}
|
|
44506
|
+
);
|
|
44507
|
+
}
|
|
44508
|
+
return /* @__PURE__ */ jsxs11(
|
|
44509
|
+
Box12,
|
|
44510
|
+
{
|
|
44511
|
+
flexDirection: "column",
|
|
44512
|
+
borderStyle: "round",
|
|
44513
|
+
borderColor: theme.primary,
|
|
44514
|
+
padding: 1,
|
|
44515
|
+
width: 70,
|
|
44516
|
+
children: [
|
|
44517
|
+
/* @__PURE__ */ jsxs11(Box12, { marginBottom: 1, children: [
|
|
44518
|
+
/* @__PURE__ */ jsx12(Text12, { bold: true, color: theme.primary, children: "Player Management" }),
|
|
44519
|
+
/* @__PURE__ */ jsx12(Text12, { dimColor: true, children: " - " }),
|
|
44520
|
+
/* @__PURE__ */ jsxs11(Text12, { color: view === "players" ? theme.success : theme.muted, children: [
|
|
44521
|
+
"[T] ",
|
|
44522
|
+
view === "players" ? "Online Players" : "Banned Players"
|
|
44523
|
+
] })
|
|
44524
|
+
] }),
|
|
44525
|
+
view === "players" ? /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: players.length === 0 ? /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "No players currently online" }) : /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
44526
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Use \u2191\u2193 to select, [K] Kick, [B] Ban" }) }),
|
|
44527
|
+
players.map((player, index) => /* @__PURE__ */ jsx12(Box12, { marginLeft: 1, children: /* @__PURE__ */ jsxs11(
|
|
44528
|
+
Text12,
|
|
44529
|
+
{
|
|
44530
|
+
color: index === selectedIndex ? theme.primary : theme.secondary,
|
|
44531
|
+
bold: index === selectedIndex,
|
|
44532
|
+
children: [
|
|
44533
|
+
index === selectedIndex ? "\u2192 " : " ",
|
|
44534
|
+
player
|
|
44535
|
+
]
|
|
44536
|
+
}
|
|
44537
|
+
) }, player))
|
|
44538
|
+
] }) }) : /* @__PURE__ */ jsx12(Box12, { flexDirection: "column", children: loading ? /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Loading banned players..." }) : bannedPlayers.length === 0 ? /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "No banned players" }) : /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
44539
|
+
/* @__PURE__ */ jsx12(Box12, { marginBottom: 1, children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "Use \u2191\u2193 to select, [U] Unban" }) }),
|
|
44540
|
+
bannedPlayers.map((banned, index) => /* @__PURE__ */ jsx12(Box12, { marginLeft: 1, children: /* @__PURE__ */ jsxs11(
|
|
44541
|
+
Text12,
|
|
44542
|
+
{
|
|
44543
|
+
color: index === selectedIndex ? theme.warning : theme.secondary,
|
|
44544
|
+
bold: index === selectedIndex,
|
|
44545
|
+
children: [
|
|
44546
|
+
index === selectedIndex ? "\u2192 " : " ",
|
|
44547
|
+
banned
|
|
44548
|
+
]
|
|
44549
|
+
}
|
|
44550
|
+
) }, banned))
|
|
44551
|
+
] }) }),
|
|
44552
|
+
/* @__PURE__ */ jsx12(Box12, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx12(Text12, { dimColor: true, children: "[Esc/Q] Close [T] Toggle View" }) })
|
|
44553
|
+
]
|
|
44554
|
+
}
|
|
44555
|
+
);
|
|
44556
|
+
};
|
|
44557
|
+
|
|
44558
|
+
// src/tui/components/ServerInfoModal.tsx
|
|
44559
|
+
init_mod();
|
|
44560
|
+
import { Box as Box14, Text as Text14, useInput as useInput7 } from "ink";
|
|
44561
|
+
import { useEffect as useEffect7, useState as useState9 } from "react";
|
|
44562
|
+
|
|
43094
44563
|
// src/tui/components/Spinner.tsx
|
|
43095
|
-
import { Box as
|
|
43096
|
-
import { useEffect as
|
|
43097
|
-
import { jsx as
|
|
44564
|
+
import { Box as Box13, Text as Text13 } from "ink";
|
|
44565
|
+
import { useEffect as useEffect6, useState as useState8 } from "react";
|
|
44566
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
43098
44567
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
43099
44568
|
var SPINNER_INTERVAL = 80;
|
|
43100
44569
|
var Spinner = (props) => {
|
|
43101
44570
|
const { label, color = valheimPalette.mandarin } = props;
|
|
43102
|
-
const [frame, setFrame] =
|
|
43103
|
-
|
|
44571
|
+
const [frame, setFrame] = useState8(0);
|
|
44572
|
+
useEffect6(() => {
|
|
43104
44573
|
const timer = setInterval(() => {
|
|
43105
44574
|
setFrame((f) => (f + 1) % SPINNER_FRAMES.length);
|
|
43106
44575
|
}, SPINNER_INTERVAL);
|
|
43107
44576
|
return () => clearInterval(timer);
|
|
43108
44577
|
}, []);
|
|
43109
|
-
return /* @__PURE__ */
|
|
43110
|
-
/* @__PURE__ */
|
|
43111
|
-
label && /* @__PURE__ */
|
|
44578
|
+
return /* @__PURE__ */ jsxs12(Box13, { children: [
|
|
44579
|
+
/* @__PURE__ */ jsx13(Text13, { color, children: SPINNER_FRAMES[frame] }),
|
|
44580
|
+
label && /* @__PURE__ */ jsxs12(Text13, { children: [
|
|
43112
44581
|
" ",
|
|
43113
44582
|
label
|
|
43114
44583
|
] })
|
|
43115
44584
|
] });
|
|
43116
44585
|
};
|
|
43117
44586
|
|
|
44587
|
+
// src/tui/components/ServerInfoModal.tsx
|
|
44588
|
+
import { Fragment as Fragment3, jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
44589
|
+
var ServerInfoModal = ({ onClose }) => {
|
|
44590
|
+
const [info2, setInfo] = useState9("");
|
|
44591
|
+
const [ping, setPing] = useState9("");
|
|
44592
|
+
const [loading, setLoading] = useState9(true);
|
|
44593
|
+
useEffect7(() => {
|
|
44594
|
+
const fetchInfo = async () => {
|
|
44595
|
+
setLoading(true);
|
|
44596
|
+
const [infoResponse, pingResponse] = await Promise.all([
|
|
44597
|
+
rconManager.getServerInfo(),
|
|
44598
|
+
rconManager.pingServer()
|
|
44599
|
+
]);
|
|
44600
|
+
setInfo(infoResponse || "No info available");
|
|
44601
|
+
setPing(pingResponse || "");
|
|
44602
|
+
setLoading(false);
|
|
44603
|
+
};
|
|
44604
|
+
fetchInfo();
|
|
44605
|
+
}, []);
|
|
44606
|
+
useInput7((input, key) => {
|
|
44607
|
+
if (key.escape || input === "q" || input === "Q") {
|
|
44608
|
+
onClose();
|
|
44609
|
+
}
|
|
44610
|
+
});
|
|
44611
|
+
return /* @__PURE__ */ jsxs13(
|
|
44612
|
+
Box14,
|
|
44613
|
+
{
|
|
44614
|
+
flexDirection: "column",
|
|
44615
|
+
borderStyle: "round",
|
|
44616
|
+
borderColor: theme.primary,
|
|
44617
|
+
padding: 1,
|
|
44618
|
+
width: 70,
|
|
44619
|
+
children: [
|
|
44620
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, color: theme.primary, children: "Server Information" }) }),
|
|
44621
|
+
loading ? /* @__PURE__ */ jsx14(Box14, { marginY: 2, children: /* @__PURE__ */ jsx14(Spinner, { label: "Fetching server info..." }) }) : /* @__PURE__ */ jsxs13(Box14, { flexDirection: "column", children: [
|
|
44622
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, children: "Info:" }) }),
|
|
44623
|
+
/* @__PURE__ */ jsx14(Box14, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { children: info2 }) }),
|
|
44624
|
+
ping && /* @__PURE__ */ jsxs13(Fragment3, { children: [
|
|
44625
|
+
/* @__PURE__ */ jsx14(Box14, { marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { bold: true, children: "Ping:" }) }),
|
|
44626
|
+
/* @__PURE__ */ jsx14(Box14, { marginLeft: 2, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { color: theme.success, children: ping }) })
|
|
44627
|
+
] })
|
|
44628
|
+
] }),
|
|
44629
|
+
/* @__PURE__ */ jsx14(Box14, { marginTop: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "[Esc/Q] Close" }) })
|
|
44630
|
+
]
|
|
44631
|
+
}
|
|
44632
|
+
);
|
|
44633
|
+
};
|
|
44634
|
+
|
|
44635
|
+
// src/tui/components/TimeControl.tsx
|
|
44636
|
+
init_mod();
|
|
44637
|
+
import { Box as Box15, Text as Text15, useInput as useInput8 } from "ink";
|
|
44638
|
+
import { useState as useState10 } from "react";
|
|
44639
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
44640
|
+
var TIME_OPTIONS = [
|
|
44641
|
+
{ index: 0, label: "Sleep (Skip to morning)", seconds: 0 },
|
|
44642
|
+
{ index: 1, label: "Skip 1 hour", seconds: 3600 },
|
|
44643
|
+
{ index: 2, label: "Skip 3 hours", seconds: 10800 },
|
|
44644
|
+
{ index: 3, label: "Skip 6 hours", seconds: 21600 },
|
|
44645
|
+
{ index: 4, label: "Skip 12 hours", seconds: 43200 },
|
|
44646
|
+
{ index: 5, label: "Skip 1 day", seconds: 86400 }
|
|
44647
|
+
];
|
|
44648
|
+
var TimeControl = ({ onClose }) => {
|
|
44649
|
+
const rconConnected = useStore((s) => s.rcon.connected);
|
|
44650
|
+
const addLog = useStore((s) => s.actions.addLog);
|
|
44651
|
+
const [selectedIndex, setSelectedIndex] = useState10(0);
|
|
44652
|
+
const handleTimeSkip = async (seconds, label) => {
|
|
44653
|
+
if (seconds === 0) {
|
|
44654
|
+
const response = await rconManager.sleep();
|
|
44655
|
+
if (response) {
|
|
44656
|
+
addLog("info", `Sleeping through night: ${response}`);
|
|
44657
|
+
} else {
|
|
44658
|
+
addLog("error", "Failed to sleep");
|
|
44659
|
+
}
|
|
44660
|
+
} else {
|
|
44661
|
+
const response = await rconManager.skipTime(seconds);
|
|
44662
|
+
if (response) {
|
|
44663
|
+
addLog("info", `${label}: ${response}`);
|
|
44664
|
+
} else {
|
|
44665
|
+
addLog("error", `Failed to skip time: ${label}`);
|
|
44666
|
+
}
|
|
44667
|
+
}
|
|
44668
|
+
};
|
|
44669
|
+
useInput8((input, key) => {
|
|
44670
|
+
if (key.escape || input === "q" || input === "Q") {
|
|
44671
|
+
onClose();
|
|
44672
|
+
return;
|
|
44673
|
+
}
|
|
44674
|
+
if (key.upArrow) {
|
|
44675
|
+
setSelectedIndex(Math.max(0, selectedIndex - 1));
|
|
44676
|
+
} else if (key.downArrow) {
|
|
44677
|
+
setSelectedIndex(Math.min(TIME_OPTIONS.length - 1, selectedIndex + 1));
|
|
44678
|
+
}
|
|
44679
|
+
if (key.return) {
|
|
44680
|
+
const selected = TIME_OPTIONS[selectedIndex];
|
|
44681
|
+
handleTimeSkip(selected.seconds, selected.label);
|
|
44682
|
+
}
|
|
44683
|
+
});
|
|
44684
|
+
if (!rconConnected) {
|
|
44685
|
+
return /* @__PURE__ */ jsxs14(
|
|
44686
|
+
Box15,
|
|
44687
|
+
{
|
|
44688
|
+
flexDirection: "column",
|
|
44689
|
+
borderStyle: "round",
|
|
44690
|
+
borderColor: theme.error,
|
|
44691
|
+
padding: 1,
|
|
44692
|
+
width: 60,
|
|
44693
|
+
children: [
|
|
44694
|
+
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { bold: true, color: theme.error, children: "\u26A0 Time Control" }) }),
|
|
44695
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "RCON not connected" }),
|
|
44696
|
+
/* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Time control requires RCON connection" }),
|
|
44697
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "[Esc/Q] Close" }) })
|
|
44698
|
+
]
|
|
44699
|
+
}
|
|
44700
|
+
);
|
|
44701
|
+
}
|
|
44702
|
+
return /* @__PURE__ */ jsxs14(
|
|
44703
|
+
Box15,
|
|
44704
|
+
{
|
|
44705
|
+
flexDirection: "column",
|
|
44706
|
+
borderStyle: "round",
|
|
44707
|
+
borderColor: theme.primary,
|
|
44708
|
+
padding: 1,
|
|
44709
|
+
width: 50,
|
|
44710
|
+
children: [
|
|
44711
|
+
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { bold: true, color: theme.primary, children: "Time Control" }) }),
|
|
44712
|
+
/* @__PURE__ */ jsx15(Box15, { marginBottom: 1, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "Use \u2191\u2193 to select, [Enter] to execute" }) }),
|
|
44713
|
+
/* @__PURE__ */ jsx15(Box15, { flexDirection: "column", children: TIME_OPTIONS.map((option) => /* @__PURE__ */ jsx15(Box15, { marginLeft: 1, children: /* @__PURE__ */ jsxs14(
|
|
44714
|
+
Text15,
|
|
44715
|
+
{
|
|
44716
|
+
color: option.index === selectedIndex ? theme.primary : theme.secondary,
|
|
44717
|
+
bold: option.index === selectedIndex,
|
|
44718
|
+
children: [
|
|
44719
|
+
option.index === selectedIndex ? "\u2192 " : " ",
|
|
44720
|
+
option.label
|
|
44721
|
+
]
|
|
44722
|
+
}
|
|
44723
|
+
) }, option.index)) }),
|
|
44724
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 2, children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "\u26A0\uFE0F Warning: Time skip affects all players" }) }),
|
|
44725
|
+
/* @__PURE__ */ jsx15(Box15, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx15(Text15, { dimColor: true, children: "[Enter] Execute [Esc/Q] Close" }) })
|
|
44726
|
+
]
|
|
44727
|
+
}
|
|
44728
|
+
);
|
|
44729
|
+
};
|
|
44730
|
+
|
|
43118
44731
|
// src/tui/hooks/useServer.ts
|
|
43119
|
-
|
|
44732
|
+
init_mod();
|
|
44733
|
+
import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef3 } from "react";
|
|
43120
44734
|
|
|
43121
44735
|
// src/tui/serverManager.ts
|
|
43122
44736
|
var watchdog = null;
|
|
43123
44737
|
var updating = false;
|
|
44738
|
+
var isAttached = false;
|
|
43124
44739
|
function getWatchdog() {
|
|
43125
44740
|
return watchdog;
|
|
43126
44741
|
}
|
|
43127
44742
|
function hasActiveServer() {
|
|
43128
44743
|
return watchdog !== null;
|
|
43129
44744
|
}
|
|
44745
|
+
function isAttachedToServer() {
|
|
44746
|
+
return isAttached;
|
|
44747
|
+
}
|
|
43130
44748
|
function isUpdating() {
|
|
43131
44749
|
return updating;
|
|
43132
44750
|
}
|
|
43133
44751
|
function setUpdating(value) {
|
|
43134
44752
|
updating = value;
|
|
43135
44753
|
}
|
|
44754
|
+
async function checkRunningServer() {
|
|
44755
|
+
return getRunningServer();
|
|
44756
|
+
}
|
|
44757
|
+
async function attachToServer(pidData, events) {
|
|
44758
|
+
if (watchdog) {
|
|
44759
|
+
throw new Error("Already managing a server - stop it first");
|
|
44760
|
+
}
|
|
44761
|
+
const config = {
|
|
44762
|
+
name: pidData.serverName ?? "Valheim Server",
|
|
44763
|
+
port: pidData.port,
|
|
44764
|
+
world: pidData.world,
|
|
44765
|
+
password: "",
|
|
44766
|
+
// Not needed for attach
|
|
44767
|
+
public: false,
|
|
44768
|
+
crossplay: false,
|
|
44769
|
+
detached: true
|
|
44770
|
+
};
|
|
44771
|
+
watchdog = new Watchdog(config, { enabled: false }, events);
|
|
44772
|
+
await watchdog.serverProcess.attach(pidData);
|
|
44773
|
+
isAttached = true;
|
|
44774
|
+
return watchdog;
|
|
44775
|
+
}
|
|
43136
44776
|
async function startServer(config, watchdogConfig, events) {
|
|
43137
44777
|
if (watchdog) {
|
|
43138
44778
|
throw new Error("Server is already running");
|
|
43139
44779
|
}
|
|
43140
|
-
|
|
43141
|
-
|
|
43142
|
-
|
|
43143
|
-
|
|
43144
|
-
|
|
43145
|
-
pid,
|
|
43146
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
43147
|
-
world: config.world,
|
|
43148
|
-
port: config.port
|
|
43149
|
-
});
|
|
44780
|
+
const running = await getRunningServer();
|
|
44781
|
+
if (running) {
|
|
44782
|
+
throw new Error(
|
|
44783
|
+
`A server is already running (PID: ${running.pid}, World: ${running.world}). Stop it first or attach to it.`
|
|
44784
|
+
);
|
|
43150
44785
|
}
|
|
44786
|
+
const detachedConfig = {
|
|
44787
|
+
...config,
|
|
44788
|
+
detached: true
|
|
44789
|
+
};
|
|
44790
|
+
watchdog = new Watchdog(detachedConfig, watchdogConfig, events);
|
|
44791
|
+
await watchdog.start();
|
|
44792
|
+
isAttached = false;
|
|
43151
44793
|
return watchdog;
|
|
43152
44794
|
}
|
|
43153
|
-
async function stopServer() {
|
|
44795
|
+
async function stopServer(keepRunning = false) {
|
|
43154
44796
|
if (!watchdog) {
|
|
43155
44797
|
return;
|
|
43156
44798
|
}
|
|
44799
|
+
if (keepRunning && isAttached) {
|
|
44800
|
+
await watchdog.serverProcess.detach();
|
|
44801
|
+
watchdog = null;
|
|
44802
|
+
isAttached = false;
|
|
44803
|
+
return;
|
|
44804
|
+
}
|
|
43157
44805
|
await watchdog.stop();
|
|
43158
44806
|
watchdog = null;
|
|
44807
|
+
isAttached = false;
|
|
43159
44808
|
await removePidFile();
|
|
43160
44809
|
}
|
|
43161
44810
|
async function killServer() {
|
|
@@ -43164,21 +44813,46 @@ async function killServer() {
|
|
|
43164
44813
|
}
|
|
43165
44814
|
await watchdog.kill();
|
|
43166
44815
|
watchdog = null;
|
|
44816
|
+
isAttached = false;
|
|
43167
44817
|
await removePidFile();
|
|
43168
44818
|
}
|
|
44819
|
+
async function detachFromServer() {
|
|
44820
|
+
if (!watchdog) {
|
|
44821
|
+
return;
|
|
44822
|
+
}
|
|
44823
|
+
if (!isAttached && !watchdog.serverProcess.isDetached) {
|
|
44824
|
+
throw new Error("Cannot detach from a non-detached server");
|
|
44825
|
+
}
|
|
44826
|
+
await watchdog.serverProcess.detach();
|
|
44827
|
+
watchdog = null;
|
|
44828
|
+
isAttached = false;
|
|
44829
|
+
}
|
|
44830
|
+
function getLogFilePath() {
|
|
44831
|
+
return watchdog?.serverProcess.logFilePath ?? null;
|
|
44832
|
+
}
|
|
43169
44833
|
async function cleanupOnExit() {
|
|
43170
|
-
if (watchdog) {
|
|
44834
|
+
if (!watchdog) {
|
|
44835
|
+
return;
|
|
44836
|
+
}
|
|
44837
|
+
if (isAttached || watchdog.serverProcess.isDetached) {
|
|
43171
44838
|
try {
|
|
43172
|
-
await watchdog.
|
|
44839
|
+
await watchdog.serverProcess.detach();
|
|
43173
44840
|
} catch {
|
|
43174
|
-
try {
|
|
43175
|
-
await watchdog.kill();
|
|
43176
|
-
} catch {
|
|
43177
|
-
}
|
|
43178
44841
|
}
|
|
43179
44842
|
watchdog = null;
|
|
43180
|
-
|
|
44843
|
+
isAttached = false;
|
|
44844
|
+
return;
|
|
44845
|
+
}
|
|
44846
|
+
try {
|
|
44847
|
+
await watchdog.stop();
|
|
44848
|
+
} catch {
|
|
44849
|
+
try {
|
|
44850
|
+
await watchdog.kill();
|
|
44851
|
+
} catch {
|
|
44852
|
+
}
|
|
43181
44853
|
}
|
|
44854
|
+
watchdog = null;
|
|
44855
|
+
await removePidFile();
|
|
43182
44856
|
}
|
|
43183
44857
|
|
|
43184
44858
|
// src/tui/hooks/useServer.ts
|
|
@@ -43214,6 +44888,7 @@ function useServer() {
|
|
|
43214
44888
|
const config = useStore((s) => s.config);
|
|
43215
44889
|
const rcon = useStore((s) => s.rcon);
|
|
43216
44890
|
const actions = useStore((s) => s.actions);
|
|
44891
|
+
const hasCheckedForRunning = useRef3(false);
|
|
43217
44892
|
const createWatchdogEvents = useCallback3(
|
|
43218
44893
|
() => ({
|
|
43219
44894
|
onStateChange: (state) => {
|
|
@@ -43285,6 +44960,38 @@ function useServer() {
|
|
|
43285
44960
|
}),
|
|
43286
44961
|
[actions]
|
|
43287
44962
|
);
|
|
44963
|
+
const checkAndAttach = useCallback3(async () => {
|
|
44964
|
+
if (hasActiveServer()) {
|
|
44965
|
+
return;
|
|
44966
|
+
}
|
|
44967
|
+
const running = await checkRunningServer();
|
|
44968
|
+
if (!running) {
|
|
44969
|
+
return;
|
|
44970
|
+
}
|
|
44971
|
+
actions.addLog(
|
|
44972
|
+
"info",
|
|
44973
|
+
`Found running server (PID: ${running.pid}, World: ${running.world})`
|
|
44974
|
+
);
|
|
44975
|
+
actions.addLog("info", "Attaching to running server...");
|
|
44976
|
+
try {
|
|
44977
|
+
const events = createWatchdogEvents();
|
|
44978
|
+
await attachToServer(running, events);
|
|
44979
|
+
actions.setServerPid(running.pid);
|
|
44980
|
+
actions.setWorld(running.world);
|
|
44981
|
+
actions.setServerStatus("online");
|
|
44982
|
+
actions.setStartupPhase("ready");
|
|
44983
|
+
actions.addLog("info", "Successfully attached to running server");
|
|
44984
|
+
} catch (error2) {
|
|
44985
|
+
actions.addLog("error", `Failed to attach to running server: ${error2}`);
|
|
44986
|
+
}
|
|
44987
|
+
}, [actions, createWatchdogEvents]);
|
|
44988
|
+
useEffect8(() => {
|
|
44989
|
+
if (hasCheckedForRunning.current) return;
|
|
44990
|
+
hasCheckedForRunning.current = true;
|
|
44991
|
+
checkAndAttach().catch((error2) => {
|
|
44992
|
+
actions.addLog("error", `Error checking for running server: ${error2}`);
|
|
44993
|
+
});
|
|
44994
|
+
}, [checkAndAttach, actions]);
|
|
43288
44995
|
const start = useCallback3(async () => {
|
|
43289
44996
|
if (status !== "offline") {
|
|
43290
44997
|
actions.addLog("warn", "Server is not offline, cannot start");
|
|
@@ -43405,13 +45112,78 @@ function useServer() {
|
|
|
43405
45112
|
actions.addLog("error", `Failed to send save command: ${error2}`);
|
|
43406
45113
|
}
|
|
43407
45114
|
}, [rcon, actions]);
|
|
43408
|
-
|
|
45115
|
+
const detach = useCallback3(async () => {
|
|
45116
|
+
if (!hasActiveServer()) {
|
|
45117
|
+
actions.addLog("warn", "No server to detach from");
|
|
45118
|
+
return;
|
|
45119
|
+
}
|
|
45120
|
+
try {
|
|
45121
|
+
await detachFromServer();
|
|
45122
|
+
actions.addLog("info", "Detached from server - it will continue running");
|
|
45123
|
+
} catch (error2) {
|
|
45124
|
+
actions.addLog("error", `Failed to detach: ${error2}`);
|
|
45125
|
+
}
|
|
45126
|
+
}, [actions]);
|
|
45127
|
+
useEffect8(() => {
|
|
43409
45128
|
if (status !== "online") return;
|
|
43410
45129
|
const interval = setInterval(() => {
|
|
43411
45130
|
actions.incrementUptime();
|
|
43412
45131
|
}, 1e3);
|
|
43413
45132
|
return () => clearInterval(interval);
|
|
43414
45133
|
}, [status, actions]);
|
|
45134
|
+
useEffect8(() => {
|
|
45135
|
+
if (!rcon.enabled) {
|
|
45136
|
+
if (rconManager.isConnected()) {
|
|
45137
|
+
rconManager.disconnect();
|
|
45138
|
+
actions.setRconConnected(false);
|
|
45139
|
+
}
|
|
45140
|
+
return;
|
|
45141
|
+
}
|
|
45142
|
+
rconManager.initialize(
|
|
45143
|
+
{
|
|
45144
|
+
host: "localhost",
|
|
45145
|
+
port: rcon.port,
|
|
45146
|
+
password: rcon.password,
|
|
45147
|
+
timeout: rcon.timeout,
|
|
45148
|
+
enabled: rcon.enabled,
|
|
45149
|
+
autoReconnect: rcon.autoReconnect
|
|
45150
|
+
},
|
|
45151
|
+
{
|
|
45152
|
+
onConnectionStateChange: (state) => {
|
|
45153
|
+
const connected = state === "connected";
|
|
45154
|
+
actions.setRconConnected(connected);
|
|
45155
|
+
if (connected) {
|
|
45156
|
+
actions.addLog("info", "RCON connected");
|
|
45157
|
+
} else if (state === "error") {
|
|
45158
|
+
actions.addLog("warn", "RCON connection error");
|
|
45159
|
+
} else if (state === "disconnected") {
|
|
45160
|
+
actions.addLog("info", "RCON disconnected");
|
|
45161
|
+
}
|
|
45162
|
+
},
|
|
45163
|
+
onPlayerListUpdate: (players) => {
|
|
45164
|
+
actions.setPlayers(players);
|
|
45165
|
+
},
|
|
45166
|
+
pollInterval: 1e4
|
|
45167
|
+
// Poll every 10 seconds
|
|
45168
|
+
}
|
|
45169
|
+
);
|
|
45170
|
+
return () => {
|
|
45171
|
+
rconManager.disconnect();
|
|
45172
|
+
};
|
|
45173
|
+
}, [rcon, actions]);
|
|
45174
|
+
useEffect8(() => {
|
|
45175
|
+
if (status === "online" && rcon.enabled && !rconManager.isConnected()) {
|
|
45176
|
+
const timer = setTimeout(() => {
|
|
45177
|
+
rconManager.connect().catch((error2) => {
|
|
45178
|
+
actions.addLog("warn", `RCON connection failed: ${error2}`);
|
|
45179
|
+
});
|
|
45180
|
+
}, 3e3);
|
|
45181
|
+
return () => clearTimeout(timer);
|
|
45182
|
+
}
|
|
45183
|
+
if (status === "offline" && rconManager.isConnected()) {
|
|
45184
|
+
rconManager.disconnect();
|
|
45185
|
+
}
|
|
45186
|
+
}, [status, rcon.enabled, actions]);
|
|
43415
45187
|
return {
|
|
43416
45188
|
status,
|
|
43417
45189
|
start,
|
|
@@ -43420,16 +45192,20 @@ function useServer() {
|
|
|
43420
45192
|
restart,
|
|
43421
45193
|
update,
|
|
43422
45194
|
forceSave,
|
|
45195
|
+
detach,
|
|
45196
|
+
checkAndAttach,
|
|
43423
45197
|
isOnline: status === "online",
|
|
43424
45198
|
isOffline: status === "offline",
|
|
43425
45199
|
isTransitioning: status === "starting" || status === "stopping",
|
|
43426
45200
|
isUpdating: isUpdating(),
|
|
45201
|
+
isAttached: isAttachedToServer(),
|
|
45202
|
+
logFilePath: getLogFilePath(),
|
|
43427
45203
|
watchdog: getWatchdog()
|
|
43428
45204
|
};
|
|
43429
45205
|
}
|
|
43430
45206
|
|
|
43431
45207
|
// src/tui/screens/Dashboard.tsx
|
|
43432
|
-
import { jsx as
|
|
45208
|
+
import { Fragment as Fragment4, jsx as jsx16, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
43433
45209
|
function formatUptime2(seconds) {
|
|
43434
45210
|
if (seconds === 0) return "N/A";
|
|
43435
45211
|
const hours = Math.floor(seconds / 3600);
|
|
@@ -43491,6 +45267,8 @@ var Dashboard = () => {
|
|
|
43491
45267
|
const memoryUsage = useStore((s) => s.server.memoryUsage);
|
|
43492
45268
|
const startupPhase = useStore((s) => s.server.startupPhase);
|
|
43493
45269
|
const config = useStore((s) => s.config);
|
|
45270
|
+
const rconConfig = useStore((s) => s.rcon);
|
|
45271
|
+
const rconConnected = useStore((s) => s.rcon.connected);
|
|
43494
45272
|
const modalOpen = useStore((s) => s.ui.modalOpen);
|
|
43495
45273
|
const openModal = useStore((s) => s.actions.openModal);
|
|
43496
45274
|
const closeModal = useStore((s) => s.actions.closeModal);
|
|
@@ -43526,12 +45304,12 @@ var Dashboard = () => {
|
|
|
43526
45304
|
const setValheimBuildId = useStore((s) => s.actions.setValheimBuildId);
|
|
43527
45305
|
const resetValheimInstall = useStore((s) => s.actions.resetValheimInstall);
|
|
43528
45306
|
const { start, stop, restart, update, forceSave } = useServer();
|
|
43529
|
-
const [isUpdating2, setIsUpdating] =
|
|
43530
|
-
const [updateProgress, setUpdateProgress] =
|
|
43531
|
-
const [startupTaskRegistered, setStartupTaskRegistered] =
|
|
43532
|
-
const [startupTaskProcessing, setStartupTaskProcessing] =
|
|
45307
|
+
const [isUpdating2, setIsUpdating] = useState11(false);
|
|
45308
|
+
const [updateProgress, setUpdateProgress] = useState11("");
|
|
45309
|
+
const [startupTaskRegistered, setStartupTaskRegistered] = useState11(null);
|
|
45310
|
+
const [startupTaskProcessing, setStartupTaskProcessing] = useState11(false);
|
|
43533
45311
|
const statusColor = getStatusColor(status);
|
|
43534
|
-
|
|
45312
|
+
useEffect9(() => {
|
|
43535
45313
|
const checkSteamCmd2 = async () => {
|
|
43536
45314
|
try {
|
|
43537
45315
|
const installed = await isSteamCmdInstalled();
|
|
@@ -43546,7 +45324,7 @@ var Dashboard = () => {
|
|
|
43546
45324
|
};
|
|
43547
45325
|
checkSteamCmd2();
|
|
43548
45326
|
}, [setSteamCmdInstalled, setSteamCmdPath]);
|
|
43549
|
-
|
|
45327
|
+
useEffect9(() => {
|
|
43550
45328
|
if (steamCmdInstalled !== true) return;
|
|
43551
45329
|
const checkValheim = async () => {
|
|
43552
45330
|
try {
|
|
@@ -43577,7 +45355,7 @@ var Dashboard = () => {
|
|
|
43577
45355
|
setValheimBuildId,
|
|
43578
45356
|
addLog
|
|
43579
45357
|
]);
|
|
43580
|
-
|
|
45358
|
+
useEffect9(() => {
|
|
43581
45359
|
if (steamCmdInstalled === true && valheimInstalled === false && !valheimInstalling) {
|
|
43582
45360
|
const autoInstallValheim = async () => {
|
|
43583
45361
|
setValheimInstalling(true);
|
|
@@ -43636,7 +45414,7 @@ var Dashboard = () => {
|
|
|
43636
45414
|
resetValheimInstall,
|
|
43637
45415
|
addLog
|
|
43638
45416
|
]);
|
|
43639
|
-
|
|
45417
|
+
useEffect9(() => {
|
|
43640
45418
|
const checkStartupTask = async () => {
|
|
43641
45419
|
try {
|
|
43642
45420
|
const registered = await isStartupTaskRegistered();
|
|
@@ -43778,7 +45556,7 @@ var Dashboard = () => {
|
|
|
43778
45556
|
setStartupTaskProcessing(false);
|
|
43779
45557
|
}
|
|
43780
45558
|
};
|
|
43781
|
-
|
|
45559
|
+
useInput9((input) => {
|
|
43782
45560
|
if (modalOpen || isUpdating2 || steamCmdInstalling || valheimInstalling || startupTaskProcessing)
|
|
43783
45561
|
return;
|
|
43784
45562
|
if (input === "s" || input === "S") {
|
|
@@ -43790,7 +45568,7 @@ var Dashboard = () => {
|
|
|
43790
45568
|
if (input === "x" || input === "X") {
|
|
43791
45569
|
if (status === "online") {
|
|
43792
45570
|
openModal(
|
|
43793
|
-
/* @__PURE__ */
|
|
45571
|
+
/* @__PURE__ */ jsx16(
|
|
43794
45572
|
ConfirmModal,
|
|
43795
45573
|
{
|
|
43796
45574
|
message: "Stop the server? Players will be disconnected.",
|
|
@@ -43804,7 +45582,7 @@ var Dashboard = () => {
|
|
|
43804
45582
|
if (input === "r" || input === "R") {
|
|
43805
45583
|
if (status === "online") {
|
|
43806
45584
|
openModal(
|
|
43807
|
-
/* @__PURE__ */
|
|
45585
|
+
/* @__PURE__ */ jsx16(
|
|
43808
45586
|
ConfirmModal,
|
|
43809
45587
|
{
|
|
43810
45588
|
message: "Restart the server? Players will be briefly disconnected.",
|
|
@@ -43818,7 +45596,7 @@ var Dashboard = () => {
|
|
|
43818
45596
|
if (input === "u" || input === "U") {
|
|
43819
45597
|
if (status === "offline" && steamCmdInstalled && valheimInstalled) {
|
|
43820
45598
|
openModal(
|
|
43821
|
-
/* @__PURE__ */
|
|
45599
|
+
/* @__PURE__ */ jsx16(
|
|
43822
45600
|
ConfirmModal,
|
|
43823
45601
|
{
|
|
43824
45602
|
message: "Update server via SteamCMD? This may take a few minutes.",
|
|
@@ -43832,7 +45610,7 @@ var Dashboard = () => {
|
|
|
43832
45610
|
if (input === "i" || input === "I") {
|
|
43833
45611
|
if (!steamCmdInstalled && !steamCmdInstalling) {
|
|
43834
45612
|
openModal(
|
|
43835
|
-
/* @__PURE__ */
|
|
45613
|
+
/* @__PURE__ */ jsx16(
|
|
43836
45614
|
ConfirmModal,
|
|
43837
45615
|
{
|
|
43838
45616
|
message: "Install SteamCMD? This is required to download and update Valheim.",
|
|
@@ -43847,7 +45625,7 @@ var Dashboard = () => {
|
|
|
43847
45625
|
if (steamCmdInstalled && !valheimInstalling) {
|
|
43848
45626
|
const action = valheimInstalled === false ? "Install" : "Reinstall/Verify";
|
|
43849
45627
|
openModal(
|
|
43850
|
-
/* @__PURE__ */
|
|
45628
|
+
/* @__PURE__ */ jsx16(
|
|
43851
45629
|
ConfirmModal,
|
|
43852
45630
|
{
|
|
43853
45631
|
message: `${action} Valheim Dedicated Server? This may take several minutes.`,
|
|
@@ -43866,7 +45644,7 @@ var Dashboard = () => {
|
|
|
43866
45644
|
if (input === "k" || input === "K") {
|
|
43867
45645
|
if (status === "online" || status === "starting" || status === "stopping") {
|
|
43868
45646
|
openModal(
|
|
43869
|
-
/* @__PURE__ */
|
|
45647
|
+
/* @__PURE__ */ jsx16(
|
|
43870
45648
|
ConfirmModal,
|
|
43871
45649
|
{
|
|
43872
45650
|
message: "Force kill the server? Data may be lost!",
|
|
@@ -43881,7 +45659,7 @@ var Dashboard = () => {
|
|
|
43881
45659
|
if (startupTaskRegistered === null) return;
|
|
43882
45660
|
const action = startupTaskRegistered ? "Remove" : "Enable";
|
|
43883
45661
|
openModal(
|
|
43884
|
-
/* @__PURE__ */
|
|
45662
|
+
/* @__PURE__ */ jsx16(
|
|
43885
45663
|
ConfirmModal,
|
|
43886
45664
|
{
|
|
43887
45665
|
message: `${action} auto-start at login? The server manager will ${startupTaskRegistered ? "no longer" : ""} start automatically when you log in.`,
|
|
@@ -43891,256 +45669,336 @@ var Dashboard = () => {
|
|
|
43891
45669
|
)
|
|
43892
45670
|
);
|
|
43893
45671
|
}
|
|
45672
|
+
if (rconConnected && status === "online") {
|
|
45673
|
+
if (input === "p" || input === "P") {
|
|
45674
|
+
openModal(/* @__PURE__ */ jsx16(PlayerManager, { onClose: closeModal }));
|
|
45675
|
+
}
|
|
45676
|
+
if (input === "n" || input === "N") {
|
|
45677
|
+
openModal(/* @__PURE__ */ jsx16(ServerInfoModal, { onClose: closeModal }));
|
|
45678
|
+
}
|
|
45679
|
+
if (input === "e" || input === "E") {
|
|
45680
|
+
openModal(/* @__PURE__ */ jsx16(EventManager, { onClose: closeModal }));
|
|
45681
|
+
}
|
|
45682
|
+
if (input === "t" || input === "T") {
|
|
45683
|
+
openModal(/* @__PURE__ */ jsx16(TimeControl, { onClose: closeModal }));
|
|
45684
|
+
}
|
|
45685
|
+
if (input === "g" || input === "G") {
|
|
45686
|
+
openModal(/* @__PURE__ */ jsx16(GlobalKeysManager, { onClose: closeModal }));
|
|
45687
|
+
}
|
|
45688
|
+
if (input === "d" || input === "D") {
|
|
45689
|
+
openModal(
|
|
45690
|
+
/* @__PURE__ */ jsx16(
|
|
45691
|
+
ConfirmModal,
|
|
45692
|
+
{
|
|
45693
|
+
message: "Remove all dropped items from the world? This cannot be undone.",
|
|
45694
|
+
onConfirm: async () => {
|
|
45695
|
+
closeModal();
|
|
45696
|
+
const { rconManager: rconManager2 } = await Promise.resolve().then(() => (init_mod(), mod_exports));
|
|
45697
|
+
const response = await rconManager2.removeDrops();
|
|
45698
|
+
addLog("info", `Remove drops: ${response || "Done"}`);
|
|
45699
|
+
},
|
|
45700
|
+
onCancel: closeModal
|
|
45701
|
+
}
|
|
45702
|
+
)
|
|
45703
|
+
);
|
|
45704
|
+
}
|
|
45705
|
+
}
|
|
43894
45706
|
});
|
|
43895
45707
|
const renderStatusWithLoading = () => {
|
|
43896
45708
|
if (status === "starting") {
|
|
43897
45709
|
const phaseDesc = getPhaseDescription(startupPhase);
|
|
43898
|
-
return /* @__PURE__ */
|
|
43899
|
-
/* @__PURE__ */
|
|
43900
|
-
/* @__PURE__ */
|
|
43901
|
-
/* @__PURE__ */
|
|
45710
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
45711
|
+
/* @__PURE__ */ jsxs15(Box16, { children: [
|
|
45712
|
+
/* @__PURE__ */ jsx16(Spinner, {}),
|
|
45713
|
+
/* @__PURE__ */ jsxs15(Text16, { color: statusColor, bold: true, children: [
|
|
43902
45714
|
" ",
|
|
43903
45715
|
"STARTING"
|
|
43904
45716
|
] })
|
|
43905
45717
|
] }),
|
|
43906
|
-
phaseDesc && /* @__PURE__ */
|
|
45718
|
+
phaseDesc && /* @__PURE__ */ jsx16(Box16, { marginLeft: 2, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: phaseDesc }) })
|
|
43907
45719
|
] });
|
|
43908
45720
|
}
|
|
43909
45721
|
if (status === "stopping") {
|
|
43910
|
-
return /* @__PURE__ */
|
|
43911
|
-
/* @__PURE__ */
|
|
43912
|
-
/* @__PURE__ */
|
|
45722
|
+
return /* @__PURE__ */ jsxs15(Box16, { children: [
|
|
45723
|
+
/* @__PURE__ */ jsx16(Spinner, {}),
|
|
45724
|
+
/* @__PURE__ */ jsxs15(Text16, { color: statusColor, bold: true, children: [
|
|
43913
45725
|
" ",
|
|
43914
45726
|
"STOPPING"
|
|
43915
45727
|
] })
|
|
43916
45728
|
] });
|
|
43917
45729
|
}
|
|
43918
45730
|
if (status === "online" && startupPhase === "ready") {
|
|
43919
|
-
return /* @__PURE__ */
|
|
45731
|
+
return /* @__PURE__ */ jsx16(Text16, { color: statusColor, bold: true, children: "\u25CF ONLINE (Ready to join)" });
|
|
43920
45732
|
}
|
|
43921
|
-
return /* @__PURE__ */
|
|
45733
|
+
return /* @__PURE__ */ jsxs15(Text16, { color: statusColor, bold: true, children: [
|
|
43922
45734
|
"\u25CF ",
|
|
43923
45735
|
status.toUpperCase()
|
|
43924
45736
|
] });
|
|
43925
45737
|
};
|
|
43926
45738
|
if (isUpdating2) {
|
|
43927
|
-
return /* @__PURE__ */
|
|
43928
|
-
/* @__PURE__ */
|
|
43929
|
-
/* @__PURE__ */
|
|
43930
|
-
|
|
45739
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
45740
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.primary, children: "\u2500 Dashboard \u2500" }) }),
|
|
45741
|
+
/* @__PURE__ */ jsxs15(
|
|
45742
|
+
Box16,
|
|
43931
45743
|
{
|
|
43932
45744
|
flexDirection: "column",
|
|
43933
45745
|
alignItems: "center",
|
|
43934
45746
|
justifyContent: "center",
|
|
43935
45747
|
flexGrow: 1,
|
|
43936
45748
|
children: [
|
|
43937
|
-
/* @__PURE__ */
|
|
43938
|
-
updateProgress && /* @__PURE__ */
|
|
43939
|
-
/* @__PURE__ */
|
|
45749
|
+
/* @__PURE__ */ jsx16(Spinner, { label: "Updating server..." }),
|
|
45750
|
+
updateProgress && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: updateProgress }) }),
|
|
45751
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Please wait, this may take several minutes..." }) })
|
|
43940
45752
|
]
|
|
43941
45753
|
}
|
|
43942
45754
|
)
|
|
43943
45755
|
] });
|
|
43944
45756
|
}
|
|
43945
45757
|
if (steamCmdInstalling) {
|
|
43946
|
-
return /* @__PURE__ */
|
|
43947
|
-
/* @__PURE__ */
|
|
43948
|
-
/* @__PURE__ */
|
|
43949
|
-
|
|
45758
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
45759
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.primary, children: "\u2500 Dashboard \u2500" }) }),
|
|
45760
|
+
/* @__PURE__ */ jsxs15(
|
|
45761
|
+
Box16,
|
|
43950
45762
|
{
|
|
43951
45763
|
flexDirection: "column",
|
|
43952
45764
|
alignItems: "center",
|
|
43953
45765
|
justifyContent: "center",
|
|
43954
45766
|
flexGrow: 1,
|
|
43955
45767
|
children: [
|
|
43956
|
-
/* @__PURE__ */
|
|
43957
|
-
steamCmdProgress && /* @__PURE__ */
|
|
45768
|
+
/* @__PURE__ */ jsx16(Spinner, { label: "Installing SteamCMD..." }),
|
|
45769
|
+
steamCmdProgress && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
|
|
43958
45770
|
steamCmdProgress,
|
|
43959
45771
|
steamCmdPercent > 0 && ` (${steamCmdPercent}%)`
|
|
43960
45772
|
] }) }),
|
|
43961
|
-
/* @__PURE__ */
|
|
45773
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Please wait..." }) })
|
|
43962
45774
|
]
|
|
43963
45775
|
}
|
|
43964
45776
|
)
|
|
43965
45777
|
] });
|
|
43966
45778
|
}
|
|
43967
45779
|
if (valheimInstalling) {
|
|
43968
|
-
return /* @__PURE__ */
|
|
43969
|
-
/* @__PURE__ */
|
|
43970
|
-
/* @__PURE__ */
|
|
43971
|
-
|
|
45780
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
45781
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.primary, children: "\u2500 Dashboard \u2500" }) }),
|
|
45782
|
+
/* @__PURE__ */ jsxs15(
|
|
45783
|
+
Box16,
|
|
43972
45784
|
{
|
|
43973
45785
|
flexDirection: "column",
|
|
43974
45786
|
alignItems: "center",
|
|
43975
45787
|
justifyContent: "center",
|
|
43976
45788
|
flexGrow: 1,
|
|
43977
45789
|
children: [
|
|
43978
|
-
/* @__PURE__ */
|
|
43979
|
-
valheimProgress && /* @__PURE__ */
|
|
45790
|
+
/* @__PURE__ */ jsx16(Spinner, { label: "Installing Valheim Dedicated Server..." }),
|
|
45791
|
+
valheimProgress && /* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsxs15(Text16, { dimColor: true, children: [
|
|
43980
45792
|
valheimProgress,
|
|
43981
45793
|
valheimPercent > 0 && ` (${valheimPercent}%)`
|
|
43982
45794
|
] }) }),
|
|
43983
|
-
/* @__PURE__ */
|
|
45795
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Please wait, this may take several minutes..." }) })
|
|
43984
45796
|
]
|
|
43985
45797
|
}
|
|
43986
45798
|
)
|
|
43987
45799
|
] });
|
|
43988
45800
|
}
|
|
43989
|
-
return /* @__PURE__ */
|
|
43990
|
-
/* @__PURE__ */
|
|
43991
|
-
/* @__PURE__ */
|
|
43992
|
-
/* @__PURE__ */
|
|
43993
|
-
/* @__PURE__ */
|
|
43994
|
-
/* @__PURE__ */
|
|
43995
|
-
/* @__PURE__ */
|
|
43996
|
-
steamCmdInstalled === null ? /* @__PURE__ */
|
|
45801
|
+
return /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
45802
|
+
/* @__PURE__ */ jsx16(Box16, { marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.primary, children: "\u2500 Dashboard \u2500" }) }),
|
|
45803
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45804
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "SteamCMD" }),
|
|
45805
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, flexDirection: "column", children: [
|
|
45806
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45807
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Status: " }),
|
|
45808
|
+
steamCmdInstalled === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking..." }) : steamCmdInstalled ? /* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "\u25CF Installed" }) : /* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "\u25CB Not Installed" })
|
|
43997
45809
|
] }),
|
|
43998
|
-
steamCmdInstalled && steamCmdPath && /* @__PURE__ */
|
|
43999
|
-
/* @__PURE__ */
|
|
44000
|
-
/* @__PURE__ */
|
|
45810
|
+
steamCmdInstalled && steamCmdPath && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45811
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Location: " }),
|
|
45812
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: steamCmdPath })
|
|
44001
45813
|
] })
|
|
44002
45814
|
] })
|
|
44003
45815
|
] }),
|
|
44004
|
-
/* @__PURE__ */
|
|
44005
|
-
/* @__PURE__ */
|
|
44006
|
-
/* @__PURE__ */
|
|
44007
|
-
/* @__PURE__ */
|
|
44008
|
-
/* @__PURE__ */
|
|
44009
|
-
steamCmdInstalled !== true ? /* @__PURE__ */
|
|
45816
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45817
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Valheim Dedicated Server" }),
|
|
45818
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, flexDirection: "column", children: [
|
|
45819
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45820
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Status: " }),
|
|
45821
|
+
steamCmdInstalled !== true ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Waiting for SteamCMD..." }) : valheimInstalled === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking..." }) : valheimInstalled ? /* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "\u25CF Installed" }) : /* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "\u25CB Not Installed" })
|
|
44010
45822
|
] }),
|
|
44011
|
-
valheimInstalled && valheimVerified !== null && /* @__PURE__ */
|
|
44012
|
-
/* @__PURE__ */
|
|
44013
|
-
valheimVerified ? /* @__PURE__ */
|
|
45823
|
+
valheimInstalled && valheimVerified !== null && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45824
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Verified: " }),
|
|
45825
|
+
valheimVerified ? /* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "\u25CF Yes" }) : /* @__PURE__ */ jsx16(Text16, { color: theme.error, children: "\u25CB Files Missing" })
|
|
44014
45826
|
] }),
|
|
44015
|
-
valheimInstalled && valheimBuildId && /* @__PURE__ */
|
|
44016
|
-
/* @__PURE__ */
|
|
44017
|
-
/* @__PURE__ */
|
|
45827
|
+
valheimInstalled && valheimBuildId && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45828
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Build ID: " }),
|
|
45829
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: valheimBuildId })
|
|
44018
45830
|
] }),
|
|
44019
|
-
valheimInstalled && valheimPath && /* @__PURE__ */
|
|
44020
|
-
/* @__PURE__ */
|
|
44021
|
-
/* @__PURE__ */
|
|
45831
|
+
valheimInstalled && valheimPath && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45832
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Location: " }),
|
|
45833
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: valheimPath })
|
|
44022
45834
|
] })
|
|
44023
45835
|
] })
|
|
44024
45836
|
] }),
|
|
44025
|
-
/* @__PURE__ */
|
|
44026
|
-
/* @__PURE__ */
|
|
44027
|
-
/* @__PURE__ */
|
|
44028
|
-
/* @__PURE__ */
|
|
44029
|
-
/* @__PURE__ */
|
|
45837
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45838
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Server Status" }),
|
|
45839
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, flexDirection: "column", children: [
|
|
45840
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45841
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Status: " }),
|
|
44030
45842
|
renderStatusWithLoading()
|
|
44031
45843
|
] }),
|
|
44032
|
-
/* @__PURE__ */
|
|
44033
|
-
/* @__PURE__ */
|
|
44034
|
-
/* @__PURE__ */
|
|
45844
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45845
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Server Name: " }),
|
|
45846
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.primary, children: config.serverName })
|
|
45847
|
+
] }),
|
|
45848
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45849
|
+
/* @__PURE__ */ jsx16(Text16, { children: "World: " }),
|
|
45850
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.primary, children: config.world })
|
|
44035
45851
|
] }),
|
|
44036
|
-
/* @__PURE__ */
|
|
44037
|
-
/* @__PURE__ */
|
|
44038
|
-
/* @__PURE__ */
|
|
45852
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45853
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Port: " }),
|
|
45854
|
+
/* @__PURE__ */ jsx16(Text16, { children: config.port })
|
|
44039
45855
|
] }),
|
|
44040
|
-
/* @__PURE__ */
|
|
44041
|
-
/* @__PURE__ */
|
|
44042
|
-
/* @__PURE__ */
|
|
45856
|
+
version && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45857
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Version: " }),
|
|
45858
|
+
/* @__PURE__ */ jsx16(Text16, { children: version }),
|
|
45859
|
+
updateAvailable && /* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: " (Update available!)" })
|
|
44043
45860
|
] }),
|
|
44044
|
-
|
|
44045
|
-
/* @__PURE__ */
|
|
44046
|
-
/* @__PURE__ */
|
|
44047
|
-
updateAvailable && /* @__PURE__ */ jsx11(Text11, { color: theme.warning, children: " (Update available!)" })
|
|
45861
|
+
pid && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45862
|
+
/* @__PURE__ */ jsx16(Text16, { children: "PID: " }),
|
|
45863
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: pid })
|
|
44048
45864
|
] }),
|
|
44049
|
-
|
|
44050
|
-
/* @__PURE__ */
|
|
44051
|
-
/* @__PURE__ */
|
|
45865
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45866
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Uptime: " }),
|
|
45867
|
+
/* @__PURE__ */ jsx16(Text16, { children: formatUptime2(uptime) })
|
|
44052
45868
|
] }),
|
|
44053
|
-
/* @__PURE__ */
|
|
44054
|
-
/* @__PURE__ */
|
|
44055
|
-
/* @__PURE__ */
|
|
45869
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45870
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Last Save: " }),
|
|
45871
|
+
/* @__PURE__ */ jsx16(Text16, { children: formatLastSave(lastSave) })
|
|
44056
45872
|
] }),
|
|
44057
|
-
/* @__PURE__ */
|
|
44058
|
-
/* @__PURE__ */
|
|
44059
|
-
/* @__PURE__ */
|
|
45873
|
+
memoryUsage !== null && memoryUsage > 0 && /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45874
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Memory: " }),
|
|
45875
|
+
/* @__PURE__ */ jsx16(Text16, { children: formatBytes(memoryUsage) })
|
|
45876
|
+
] })
|
|
45877
|
+
] })
|
|
45878
|
+
] }),
|
|
45879
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45880
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "RCON Status" }),
|
|
45881
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, flexDirection: "column", children: [
|
|
45882
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45883
|
+
/* @__PURE__ */ jsx16(Text16, { children: "RCON: " }),
|
|
45884
|
+
rconConfig.enabled ? rconConnected ? /* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "\u25CF Connected" }) : status === "online" ? /* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "\u25CB Connecting..." }) : /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u25CB Waiting for server..." }) : /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u25CB Disabled" })
|
|
44060
45885
|
] }),
|
|
44061
|
-
|
|
44062
|
-
/* @__PURE__ */
|
|
44063
|
-
|
|
45886
|
+
rconConfig.enabled && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
45887
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45888
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Port: " }),
|
|
45889
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: rconConfig.port })
|
|
45890
|
+
] }),
|
|
45891
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45892
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Auto-reconnect: " }),
|
|
45893
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: rconConfig.autoReconnect ? "Enabled" : "Disabled" })
|
|
45894
|
+
] })
|
|
44064
45895
|
] })
|
|
44065
45896
|
] })
|
|
44066
45897
|
] }),
|
|
44067
|
-
/* @__PURE__ */
|
|
44068
|
-
/* @__PURE__ */
|
|
45898
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45899
|
+
/* @__PURE__ */ jsxs15(Text16, { bold: true, children: [
|
|
44069
45900
|
"Players (",
|
|
44070
45901
|
players.length,
|
|
44071
45902
|
")"
|
|
44072
45903
|
] }),
|
|
44073
|
-
/* @__PURE__ */
|
|
45904
|
+
/* @__PURE__ */ jsx16(Box16, { marginLeft: 2, flexDirection: "column", children: players.length === 0 ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "No players connected" }) : players.map((player) => /* @__PURE__ */ jsx16(Box16, { flexShrink: 0, children: /* @__PURE__ */ jsxs15(Text16, { color: theme.primary, children: [
|
|
44074
45905
|
"\u2022 ",
|
|
44075
45906
|
player
|
|
44076
45907
|
] }) }, player)) })
|
|
44077
45908
|
] }),
|
|
44078
|
-
/* @__PURE__ */
|
|
44079
|
-
/* @__PURE__ */
|
|
44080
|
-
/* @__PURE__ */
|
|
44081
|
-
/* @__PURE__ */
|
|
44082
|
-
startupTaskRegistered === null ? /* @__PURE__ */
|
|
45909
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", marginBottom: 1, children: [
|
|
45910
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Auto-start" }),
|
|
45911
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, flexShrink: 0, children: [
|
|
45912
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Status: " }),
|
|
45913
|
+
startupTaskRegistered === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking..." }) : startupTaskRegistered ? /* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "\u25CF Enabled" }) : /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u25CB Disabled" })
|
|
44083
45914
|
] })
|
|
44084
45915
|
] }),
|
|
44085
|
-
/* @__PURE__ */
|
|
44086
|
-
/* @__PURE__ */
|
|
44087
|
-
/* @__PURE__ */
|
|
44088
|
-
steamCmdInstalled === false && /* @__PURE__ */
|
|
44089
|
-
/* @__PURE__ */
|
|
44090
|
-
/* @__PURE__ */
|
|
44091
|
-
/* @__PURE__ */
|
|
45916
|
+
/* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
45917
|
+
/* @__PURE__ */ jsx16(Text16, { bold: true, children: "Quick Actions" }),
|
|
45918
|
+
/* @__PURE__ */ jsxs15(Box16, { marginLeft: 2, marginTop: 1, flexDirection: "column", children: [
|
|
45919
|
+
steamCmdInstalled === false && /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45920
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[I] " }),
|
|
45921
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Install SteamCMD" }),
|
|
45922
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: " (required)" })
|
|
44092
45923
|
] }) }),
|
|
44093
|
-
steamCmdInstalled && valheimInstalled === false && !valheimInstalling && /* @__PURE__ */
|
|
44094
|
-
/* @__PURE__ */
|
|
44095
|
-
/* @__PURE__ */
|
|
44096
|
-
/* @__PURE__ */
|
|
45924
|
+
steamCmdInstalled && valheimInstalled === false && !valheimInstalling && /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45925
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[V] " }),
|
|
45926
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Install Valheim Server" }),
|
|
45927
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: " (required)" })
|
|
44097
45928
|
] }) }),
|
|
44098
|
-
steamCmdInstalled && valheimInstalled && valheimVerified === false && /* @__PURE__ */
|
|
44099
|
-
/* @__PURE__ */
|
|
44100
|
-
/* @__PURE__ */
|
|
44101
|
-
/* @__PURE__ */
|
|
45929
|
+
steamCmdInstalled && valheimInstalled && valheimVerified === false && /* @__PURE__ */ jsx16(Box16, { marginBottom: 1, flexShrink: 0, children: /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45930
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "[V] " }),
|
|
45931
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Reinstall Valheim Server" }),
|
|
45932
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.error, children: " (verification failed)" })
|
|
44102
45933
|
] }) }),
|
|
44103
|
-
steamCmdInstalled && valheimInstalled ? status === "offline" ? /* @__PURE__ */
|
|
44104
|
-
/* @__PURE__ */
|
|
44105
|
-
/* @__PURE__ */
|
|
44106
|
-
/* @__PURE__ */
|
|
45934
|
+
steamCmdInstalled && valheimInstalled ? status === "offline" ? /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
45935
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45936
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.success, children: "[S] " }),
|
|
45937
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Start Server" })
|
|
44107
45938
|
] }),
|
|
44108
|
-
/* @__PURE__ */
|
|
44109
|
-
/* @__PURE__ */
|
|
44110
|
-
/* @__PURE__ */
|
|
44111
|
-
updateAvailable && /* @__PURE__ */
|
|
45939
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45940
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[U] " }),
|
|
45941
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Update Server" }),
|
|
45942
|
+
updateAvailable && /* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: " \u2605" })
|
|
44112
45943
|
] }),
|
|
44113
|
-
/* @__PURE__ */
|
|
44114
|
-
/* @__PURE__ */
|
|
44115
|
-
/* @__PURE__ */
|
|
45944
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45945
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[V] " }),
|
|
45946
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Verify/Reinstall Server" })
|
|
44116
45947
|
] })
|
|
44117
|
-
] }) : status === "online" ? /* @__PURE__ */
|
|
44118
|
-
/* @__PURE__ */
|
|
44119
|
-
/* @__PURE__ */
|
|
44120
|
-
/* @__PURE__ */
|
|
45948
|
+
] }) : status === "online" ? /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
45949
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45950
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.error, children: "[X] " }),
|
|
45951
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Stop Server" })
|
|
45952
|
+
] }),
|
|
45953
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45954
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "[R] " }),
|
|
45955
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Restart Server" })
|
|
44121
45956
|
] }),
|
|
44122
|
-
/* @__PURE__ */
|
|
44123
|
-
/* @__PURE__ */
|
|
44124
|
-
/* @__PURE__ */
|
|
45957
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45958
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[F] " }),
|
|
45959
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Force Save" })
|
|
44125
45960
|
] }),
|
|
44126
|
-
/* @__PURE__ */
|
|
44127
|
-
/* @__PURE__ */
|
|
44128
|
-
/* @__PURE__ */
|
|
45961
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45962
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.error, children: "[K] " }),
|
|
45963
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Kill Process" })
|
|
44129
45964
|
] }),
|
|
44130
|
-
/* @__PURE__ */
|
|
44131
|
-
/* @__PURE__ */
|
|
44132
|
-
/* @__PURE__ */
|
|
45965
|
+
rconConnected && /* @__PURE__ */ jsxs15(Fragment4, { children: [
|
|
45966
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, flexShrink: 0, children: /* @__PURE__ */ jsx16(Text16, { bold: true, color: theme.primary, children: "RCON Admin:" }) }),
|
|
45967
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45968
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.primary, children: "[P] " }),
|
|
45969
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Player Manager" })
|
|
45970
|
+
] }),
|
|
45971
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45972
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[N] " }),
|
|
45973
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Server Info" })
|
|
45974
|
+
] }),
|
|
45975
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45976
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.warning, children: "[E] " }),
|
|
45977
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Event Manager" })
|
|
45978
|
+
] }),
|
|
45979
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45980
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.info, children: "[T] " }),
|
|
45981
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Time Control" })
|
|
45982
|
+
] }),
|
|
45983
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45984
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.primary, children: "[G] " }),
|
|
45985
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Boss Progress" })
|
|
45986
|
+
] }),
|
|
45987
|
+
/* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
45988
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.error, children: "[D] " }),
|
|
45989
|
+
/* @__PURE__ */ jsx16(Text16, { children: "Remove Drops" })
|
|
45990
|
+
] })
|
|
44133
45991
|
] })
|
|
44134
|
-
] }) : /* @__PURE__ */
|
|
44135
|
-
/* @__PURE__ */
|
|
44136
|
-
/* @__PURE__ */
|
|
44137
|
-
/* @__PURE__ */
|
|
44138
|
-
/* @__PURE__ */
|
|
45992
|
+
] }) : /* @__PURE__ */ jsxs15(Box16, { flexDirection: "column", children: [
|
|
45993
|
+
/* @__PURE__ */ jsx16(Spinner, { label: `Server is ${status}...` }),
|
|
45994
|
+
/* @__PURE__ */ jsxs15(Box16, { marginTop: 1, flexShrink: 0, children: [
|
|
45995
|
+
/* @__PURE__ */ jsx16(Text16, { color: theme.error, children: "[K] " }),
|
|
45996
|
+
/* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Force Kill (if stuck)" })
|
|
44139
45997
|
] })
|
|
44140
|
-
] }) : steamCmdInstalled === null ? /* @__PURE__ */
|
|
44141
|
-
/* @__PURE__ */
|
|
44142
|
-
/* @__PURE__ */
|
|
44143
|
-
|
|
45998
|
+
] }) : steamCmdInstalled === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking installation status..." }) : !steamCmdInstalled ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Install SteamCMD to manage server" }) : valheimInstalled === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking Valheim installation..." }) : /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Install Valheim to manage server" }),
|
|
45999
|
+
/* @__PURE__ */ jsx16(Box16, { marginTop: 1, flexShrink: 0, children: startupTaskProcessing ? /* @__PURE__ */ jsx16(Spinner, { label: "Updating auto-start..." }) : startupTaskRegistered === null ? /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "Checking auto-start status..." }) : /* @__PURE__ */ jsxs15(Box16, { flexShrink: 0, children: [
|
|
46000
|
+
/* @__PURE__ */ jsxs15(
|
|
46001
|
+
Text16,
|
|
44144
46002
|
{
|
|
44145
46003
|
color: startupTaskRegistered ? theme.warning : theme.success,
|
|
44146
46004
|
children: [
|
|
@@ -44149,7 +46007,7 @@ var Dashboard = () => {
|
|
|
44149
46007
|
]
|
|
44150
46008
|
}
|
|
44151
46009
|
),
|
|
44152
|
-
/* @__PURE__ */
|
|
46010
|
+
/* @__PURE__ */ jsxs15(Text16, { children: [
|
|
44153
46011
|
startupTaskRegistered ? "Disable" : "Enable",
|
|
44154
46012
|
" Auto-start"
|
|
44155
46013
|
] })
|
|
@@ -44160,13 +46018,13 @@ var Dashboard = () => {
|
|
|
44160
46018
|
};
|
|
44161
46019
|
|
|
44162
46020
|
// src/tui/screens/Settings.tsx
|
|
44163
|
-
import { Box as
|
|
44164
|
-
import { useCallback as useCallback5, useMemo as useMemo4, useState as
|
|
46021
|
+
import { Box as Box20, Text as Text20, useInput as useInput13 } from "ink";
|
|
46022
|
+
import { useCallback as useCallback5, useMemo as useMemo4, useState as useState15 } from "react";
|
|
44165
46023
|
|
|
44166
46024
|
// src/tui/components/NumberInput.tsx
|
|
44167
|
-
import { Box as
|
|
44168
|
-
import { useEffect as
|
|
44169
|
-
import { jsx as
|
|
46025
|
+
import { Box as Box17, Text as Text17, useInput as useInput10 } from "ink";
|
|
46026
|
+
import { useEffect as useEffect10, useState as useState12 } from "react";
|
|
46027
|
+
import { jsx as jsx17, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
44170
46028
|
var NumberInput = ({
|
|
44171
46029
|
value,
|
|
44172
46030
|
onChange,
|
|
@@ -44180,15 +46038,15 @@ var NumberInput = ({
|
|
|
44180
46038
|
label,
|
|
44181
46039
|
width = 10
|
|
44182
46040
|
}) => {
|
|
44183
|
-
const [editMode, setEditMode] =
|
|
44184
|
-
const [editBuffer, setEditBuffer] =
|
|
44185
|
-
const [cursorVisible, setCursorVisible] =
|
|
44186
|
-
|
|
46041
|
+
const [editMode, setEditMode] = useState12(false);
|
|
46042
|
+
const [editBuffer, setEditBuffer] = useState12("");
|
|
46043
|
+
const [cursorVisible, setCursorVisible] = useState12(true);
|
|
46044
|
+
useEffect10(() => {
|
|
44187
46045
|
if (editMode) {
|
|
44188
46046
|
setEditBuffer(String(value));
|
|
44189
46047
|
}
|
|
44190
46048
|
}, [editMode, value]);
|
|
44191
|
-
|
|
46049
|
+
useEffect10(() => {
|
|
44192
46050
|
if (!focus || !editMode) {
|
|
44193
46051
|
setCursorVisible(false);
|
|
44194
46052
|
return;
|
|
@@ -44209,7 +46067,7 @@ var NumberInput = ({
|
|
|
44209
46067
|
}
|
|
44210
46068
|
setEditMode(false);
|
|
44211
46069
|
};
|
|
44212
|
-
|
|
46070
|
+
useInput10(
|
|
44213
46071
|
(input, key) => {
|
|
44214
46072
|
if (!focus) return;
|
|
44215
46073
|
if (key.escape) {
|
|
@@ -44280,38 +46138,38 @@ var NumberInput = ({
|
|
|
44280
46138
|
const padding = " ".repeat(paddingLength);
|
|
44281
46139
|
const atMin = value <= min;
|
|
44282
46140
|
const atMax = value >= max;
|
|
44283
|
-
return /* @__PURE__ */
|
|
44284
|
-
label && /* @__PURE__ */
|
|
46141
|
+
return /* @__PURE__ */ jsxs16(Box17, { children: [
|
|
46142
|
+
label && /* @__PURE__ */ jsx17(Box17, { marginRight: 1, children: /* @__PURE__ */ jsxs16(Text17, { children: [
|
|
44285
46143
|
label,
|
|
44286
46144
|
":"
|
|
44287
46145
|
] }) }),
|
|
44288
|
-
/* @__PURE__ */
|
|
44289
|
-
focus && !editMode && /* @__PURE__ */
|
|
44290
|
-
/* @__PURE__ */
|
|
44291
|
-
|
|
46146
|
+
/* @__PURE__ */ jsxs16(Box17, { children: [
|
|
46147
|
+
focus && !editMode && /* @__PURE__ */ jsx17(Text17, { color: atMin ? theme.muted : theme.primary, children: atMin ? " " : "\u25C0" }),
|
|
46148
|
+
/* @__PURE__ */ jsx17(
|
|
46149
|
+
Box17,
|
|
44292
46150
|
{
|
|
44293
46151
|
borderStyle: editMode ? "single" : void 0,
|
|
44294
46152
|
borderColor: editMode ? theme.primary : void 0,
|
|
44295
46153
|
marginX: focus && !editMode ? 1 : 0,
|
|
44296
|
-
children: /* @__PURE__ */
|
|
46154
|
+
children: /* @__PURE__ */ jsxs16(Text17, { color: focus ? theme.primary : void 0, bold: focus, children: [
|
|
44297
46155
|
displayValue,
|
|
44298
|
-
editMode && cursorVisible && /* @__PURE__ */
|
|
44299
|
-
suffix && /* @__PURE__ */
|
|
46156
|
+
editMode && cursorVisible && /* @__PURE__ */ jsx17(Text17, { inverse: true, children: " " }),
|
|
46157
|
+
suffix && /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: suffix }),
|
|
44300
46158
|
padding
|
|
44301
46159
|
] })
|
|
44302
46160
|
}
|
|
44303
46161
|
),
|
|
44304
|
-
focus && !editMode && /* @__PURE__ */
|
|
46162
|
+
focus && !editMode && /* @__PURE__ */ jsx17(Text17, { color: atMax ? theme.muted : theme.primary, children: atMax ? " " : "\u25B6" })
|
|
44305
46163
|
] }),
|
|
44306
|
-
focus && !editMode && /* @__PURE__ */
|
|
44307
|
-
editMode && /* @__PURE__ */
|
|
46164
|
+
focus && !editMode && /* @__PURE__ */ jsx17(Box17, { marginLeft: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "[\u2191/\u2193 or type]" }) }),
|
|
46165
|
+
editMode && /* @__PURE__ */ jsx17(Box17, { marginLeft: 1, children: /* @__PURE__ */ jsx17(Text17, { dimColor: true, children: "[Enter to save, Esc to cancel]" }) })
|
|
44308
46166
|
] });
|
|
44309
46167
|
};
|
|
44310
46168
|
|
|
44311
46169
|
// src/tui/components/SelectInput.tsx
|
|
44312
|
-
import { Box as
|
|
44313
|
-
import { useMemo as useMemo3, useState as
|
|
44314
|
-
import { jsx as
|
|
46170
|
+
import { Box as Box18, Text as Text18, useInput as useInput11 } from "ink";
|
|
46171
|
+
import { useMemo as useMemo3, useState as useState13 } from "react";
|
|
46172
|
+
import { jsx as jsx18, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
44315
46173
|
function SelectInput({
|
|
44316
46174
|
options,
|
|
44317
46175
|
value,
|
|
@@ -44324,12 +46182,12 @@ function SelectInput({
|
|
|
44324
46182
|
maxVisible = 5,
|
|
44325
46183
|
expanded: initialExpanded = false
|
|
44326
46184
|
}) {
|
|
44327
|
-
const [expanded, setExpanded] =
|
|
44328
|
-
const [highlightIndex, setHighlightIndex] =
|
|
46185
|
+
const [expanded, setExpanded] = useState13(initialExpanded);
|
|
46186
|
+
const [highlightIndex, setHighlightIndex] = useState13(() => {
|
|
44329
46187
|
const idx = options.findIndex((opt) => opt.value === value);
|
|
44330
46188
|
return idx >= 0 ? idx : 0;
|
|
44331
46189
|
});
|
|
44332
|
-
const [filter, setFilter] =
|
|
46190
|
+
const [filter, setFilter] = useState13("");
|
|
44333
46191
|
const filteredOptions = useMemo3(() => {
|
|
44334
46192
|
if (!filter) return options;
|
|
44335
46193
|
const lowerFilter = filter.toLowerCase();
|
|
@@ -44349,7 +46207,7 @@ function SelectInput({
|
|
|
44349
46207
|
);
|
|
44350
46208
|
const selectedOption = options.find((opt) => opt.value === value);
|
|
44351
46209
|
const displayLabel = selectedOption?.label ?? placeholder;
|
|
44352
|
-
|
|
46210
|
+
useInput11(
|
|
44353
46211
|
(input, key) => {
|
|
44354
46212
|
if (!focus) return;
|
|
44355
46213
|
if (!expanded) {
|
|
@@ -44426,23 +46284,23 @@ function SelectInput({
|
|
|
44426
46284
|
},
|
|
44427
46285
|
{ isActive: focus }
|
|
44428
46286
|
);
|
|
44429
|
-
return /* @__PURE__ */
|
|
44430
|
-
/* @__PURE__ */
|
|
44431
|
-
label && /* @__PURE__ */
|
|
46287
|
+
return /* @__PURE__ */ jsxs17(Box18, { flexDirection: "column", children: [
|
|
46288
|
+
/* @__PURE__ */ jsxs17(Box18, { children: [
|
|
46289
|
+
label && /* @__PURE__ */ jsx18(Box18, { marginRight: 1, children: /* @__PURE__ */ jsxs17(Text18, { children: [
|
|
44432
46290
|
label,
|
|
44433
46291
|
":"
|
|
44434
46292
|
] }) }),
|
|
44435
|
-
/* @__PURE__ */
|
|
44436
|
-
/* @__PURE__ */
|
|
44437
|
-
/* @__PURE__ */
|
|
46293
|
+
/* @__PURE__ */ jsxs17(Box18, { children: [
|
|
46294
|
+
/* @__PURE__ */ jsx18(Text18, { color: focus ? theme.primary : void 0, bold: focus, children: displayLabel }),
|
|
46295
|
+
/* @__PURE__ */ jsxs17(Text18, { dimColor: true, children: [
|
|
44438
46296
|
" ",
|
|
44439
46297
|
expanded ? "\u25B2" : "\u25BC"
|
|
44440
46298
|
] })
|
|
44441
46299
|
] }),
|
|
44442
|
-
focus && !expanded && /* @__PURE__ */
|
|
46300
|
+
focus && !expanded && /* @__PURE__ */ jsx18(Box18, { marginLeft: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "[Enter to expand, \u2191/\u2193 to change]" }) })
|
|
44443
46301
|
] }),
|
|
44444
|
-
expanded && /* @__PURE__ */
|
|
44445
|
-
|
|
46302
|
+
expanded && /* @__PURE__ */ jsxs17(
|
|
46303
|
+
Box18,
|
|
44446
46304
|
{
|
|
44447
46305
|
flexDirection: "column",
|
|
44448
46306
|
marginTop: 1,
|
|
@@ -44450,22 +46308,22 @@ function SelectInput({
|
|
|
44450
46308
|
borderColor: theme.primary,
|
|
44451
46309
|
paddingX: 1,
|
|
44452
46310
|
children: [
|
|
44453
|
-
filter && /* @__PURE__ */
|
|
44454
|
-
/* @__PURE__ */
|
|
44455
|
-
/* @__PURE__ */
|
|
46311
|
+
filter && /* @__PURE__ */ jsxs17(Box18, { marginBottom: 1, children: [
|
|
46312
|
+
/* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "Filter: " }),
|
|
46313
|
+
/* @__PURE__ */ jsx18(Text18, { color: theme.primary, children: filter })
|
|
44456
46314
|
] }),
|
|
44457
|
-
scrollOffset > 0 && /* @__PURE__ */
|
|
46315
|
+
scrollOffset > 0 && /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsxs17(Text18, { dimColor: true, children: [
|
|
44458
46316
|
" \u2191 more (",
|
|
44459
46317
|
scrollOffset,
|
|
44460
46318
|
")"
|
|
44461
46319
|
] }) }),
|
|
44462
|
-
filteredOptions.length === 0 ? /* @__PURE__ */
|
|
46320
|
+
filteredOptions.length === 0 ? /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "No matches" }) }) : visibleOptions.map((option, visibleIdx) => {
|
|
44463
46321
|
const actualIdx = scrollOffset + visibleIdx;
|
|
44464
46322
|
const isHighlighted = actualIdx === highlightIndex;
|
|
44465
46323
|
const isSelected = option.value === value;
|
|
44466
|
-
return /* @__PURE__ */
|
|
44467
|
-
/* @__PURE__ */
|
|
44468
|
-
|
|
46324
|
+
return /* @__PURE__ */ jsxs17(Box18, { paddingY: 0, children: [
|
|
46325
|
+
/* @__PURE__ */ jsxs17(
|
|
46326
|
+
Text18,
|
|
44469
46327
|
{
|
|
44470
46328
|
color: isHighlighted ? theme.primary : isSelected ? theme.success : void 0,
|
|
44471
46329
|
bold: isHighlighted,
|
|
@@ -44476,18 +46334,18 @@ function SelectInput({
|
|
|
44476
46334
|
]
|
|
44477
46335
|
}
|
|
44478
46336
|
),
|
|
44479
|
-
option.description && /* @__PURE__ */
|
|
46337
|
+
option.description && /* @__PURE__ */ jsxs17(Text18, { dimColor: true, children: [
|
|
44480
46338
|
" - ",
|
|
44481
46339
|
option.description
|
|
44482
46340
|
] })
|
|
44483
46341
|
] }, String(option.value));
|
|
44484
46342
|
}),
|
|
44485
|
-
scrollOffset + maxVisible < filteredOptions.length && /* @__PURE__ */
|
|
46343
|
+
scrollOffset + maxVisible < filteredOptions.length && /* @__PURE__ */ jsx18(Box18, { children: /* @__PURE__ */ jsxs17(Text18, { dimColor: true, children: [
|
|
44486
46344
|
"\u2193 more (",
|
|
44487
46345
|
filteredOptions.length - scrollOffset - maxVisible,
|
|
44488
46346
|
")"
|
|
44489
46347
|
] }) }),
|
|
44490
|
-
/* @__PURE__ */
|
|
46348
|
+
/* @__PURE__ */ jsx18(Box18, { marginTop: 1, children: /* @__PURE__ */ jsx18(Text18, { dimColor: true, children: "\u2191/\u2193 navigate \u2022 Enter select \u2022 Esc close \u2022 type to filter" }) })
|
|
44491
46349
|
]
|
|
44492
46350
|
}
|
|
44493
46351
|
)
|
|
@@ -44495,9 +46353,9 @@ function SelectInput({
|
|
|
44495
46353
|
}
|
|
44496
46354
|
|
|
44497
46355
|
// src/tui/components/TextInput.tsx
|
|
44498
|
-
import { Box as
|
|
44499
|
-
import { useEffect as
|
|
44500
|
-
import { jsx as
|
|
46356
|
+
import { Box as Box19, Text as Text19, useInput as useInput12 } from "ink";
|
|
46357
|
+
import { useEffect as useEffect11, useState as useState14 } from "react";
|
|
46358
|
+
import { jsx as jsx19, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
44501
46359
|
var TextInput = ({
|
|
44502
46360
|
value,
|
|
44503
46361
|
onChange,
|
|
@@ -44510,12 +46368,12 @@ var TextInput = ({
|
|
|
44510
46368
|
focus = true,
|
|
44511
46369
|
label
|
|
44512
46370
|
}) => {
|
|
44513
|
-
const [cursorPosition, setCursorPosition] =
|
|
44514
|
-
const [cursorVisible, setCursorVisible] =
|
|
44515
|
-
|
|
46371
|
+
const [cursorPosition, setCursorPosition] = useState14(value.length);
|
|
46372
|
+
const [cursorVisible, setCursorVisible] = useState14(true);
|
|
46373
|
+
useEffect11(() => {
|
|
44516
46374
|
setCursorPosition(value.length);
|
|
44517
46375
|
}, [value.length]);
|
|
44518
|
-
|
|
46376
|
+
useEffect11(() => {
|
|
44519
46377
|
if (!focus) {
|
|
44520
46378
|
setCursorVisible(false);
|
|
44521
46379
|
return;
|
|
@@ -44526,7 +46384,7 @@ var TextInput = ({
|
|
|
44526
46384
|
setCursorVisible(true);
|
|
44527
46385
|
return () => clearInterval(interval);
|
|
44528
46386
|
}, [focus]);
|
|
44529
|
-
|
|
46387
|
+
useInput12(
|
|
44530
46388
|
(input, key) => {
|
|
44531
46389
|
if (!focus) return;
|
|
44532
46390
|
if (key.return) {
|
|
@@ -44582,29 +46440,29 @@ var TextInput = ({
|
|
|
44582
46440
|
const paddingLength = Math.max(0, width - contentLength);
|
|
44583
46441
|
const padding = " ".repeat(paddingLength);
|
|
44584
46442
|
const showPlaceholder = value.length === 0 && placeholder;
|
|
44585
|
-
return /* @__PURE__ */
|
|
44586
|
-
label && /* @__PURE__ */
|
|
46443
|
+
return /* @__PURE__ */ jsxs18(Box19, { children: [
|
|
46444
|
+
label && /* @__PURE__ */ jsx19(Box19, { marginRight: 1, children: /* @__PURE__ */ jsxs18(Text19, { children: [
|
|
44587
46445
|
label,
|
|
44588
46446
|
":"
|
|
44589
46447
|
] }) }),
|
|
44590
|
-
/* @__PURE__ */
|
|
44591
|
-
|
|
46448
|
+
/* @__PURE__ */ jsx19(
|
|
46449
|
+
Box19,
|
|
44592
46450
|
{
|
|
44593
46451
|
borderStyle: focus ? "single" : void 0,
|
|
44594
46452
|
borderColor: focus ? theme.primary : void 0,
|
|
44595
46453
|
paddingX: focus ? 0 : 0,
|
|
44596
|
-
children: showPlaceholder && !focus ? /* @__PURE__ */
|
|
44597
|
-
/* @__PURE__ */
|
|
44598
|
-
/* @__PURE__ */
|
|
44599
|
-
|
|
46454
|
+
children: showPlaceholder && !focus ? /* @__PURE__ */ jsx19(Text19, { dimColor: true, children: placeholder }) : /* @__PURE__ */ jsxs18(Text19, { children: [
|
|
46455
|
+
/* @__PURE__ */ jsx19(Text19, { children: beforeCursor }),
|
|
46456
|
+
/* @__PURE__ */ jsx19(
|
|
46457
|
+
Text19,
|
|
44600
46458
|
{
|
|
44601
46459
|
inverse: focus && cursorVisible,
|
|
44602
46460
|
color: focus ? theme.primary : void 0,
|
|
44603
46461
|
children: cursorChar
|
|
44604
46462
|
}
|
|
44605
46463
|
),
|
|
44606
|
-
/* @__PURE__ */
|
|
44607
|
-
/* @__PURE__ */
|
|
46464
|
+
/* @__PURE__ */ jsx19(Text19, { children: afterCursor }),
|
|
46465
|
+
/* @__PURE__ */ jsx19(Text19, { children: padding })
|
|
44608
46466
|
] })
|
|
44609
46467
|
}
|
|
44610
46468
|
)
|
|
@@ -44612,7 +46470,7 @@ var TextInput = ({
|
|
|
44612
46470
|
};
|
|
44613
46471
|
|
|
44614
46472
|
// src/tui/hooks/useWorlds.ts
|
|
44615
|
-
import { useCallback as useCallback4, useEffect as
|
|
46473
|
+
import { useCallback as useCallback4, useEffect as useEffect12 } from "react";
|
|
44616
46474
|
function useWorlds() {
|
|
44617
46475
|
const worlds = useStore((s) => s.worlds.worlds);
|
|
44618
46476
|
const loading = useStore((s) => s.worlds.loading);
|
|
@@ -44788,7 +46646,7 @@ function useWorlds() {
|
|
|
44788
46646
|
}
|
|
44789
46647
|
|
|
44790
46648
|
// src/tui/screens/Settings.tsx
|
|
44791
|
-
import { jsx as
|
|
46649
|
+
import { jsx as jsx20, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
44792
46650
|
var SECTION_LABELS = {
|
|
44793
46651
|
server: "Server",
|
|
44794
46652
|
modifiers: "Difficulty Modifiers",
|
|
@@ -44868,10 +46726,10 @@ var Settings = () => {
|
|
|
44868
46726
|
const editingField = useStore((s) => s.ui.editingField);
|
|
44869
46727
|
const setEditingField = useStore((s) => s.actions.setEditingField);
|
|
44870
46728
|
const addLog = useStore((s) => s.actions.addLog);
|
|
44871
|
-
const [selectedIndex, setSelectedIndex] =
|
|
44872
|
-
const [localValue, setLocalValue] =
|
|
44873
|
-
const [scrollOffset, setScrollOffset] =
|
|
44874
|
-
const [expandedSections, setExpandedSections] =
|
|
46729
|
+
const [selectedIndex, setSelectedIndex] = useState15(0);
|
|
46730
|
+
const [localValue, setLocalValue] = useState15("");
|
|
46731
|
+
const [scrollOffset, setScrollOffset] = useState15(0);
|
|
46732
|
+
const [expandedSections, setExpandedSections] = useState15(
|
|
44875
46733
|
() => /* @__PURE__ */ new Set(["server"])
|
|
44876
46734
|
);
|
|
44877
46735
|
const MAX_VISIBLE_ROWS = 15;
|
|
@@ -45235,7 +47093,7 @@ var Settings = () => {
|
|
|
45235
47093
|
const cancelEdit = useCallback5(() => {
|
|
45236
47094
|
setEditingField(null);
|
|
45237
47095
|
}, [setEditingField]);
|
|
45238
|
-
|
|
47096
|
+
useInput13(
|
|
45239
47097
|
(input, key) => {
|
|
45240
47098
|
if (isEditing) return;
|
|
45241
47099
|
if (key.upArrow || input === "k") {
|
|
@@ -45291,14 +47149,14 @@ var Settings = () => {
|
|
|
45291
47149
|
const itemCount = settings.filter(
|
|
45292
47150
|
(s) => s.section === item.section
|
|
45293
47151
|
).length;
|
|
45294
|
-
return /* @__PURE__ */
|
|
45295
|
-
/* @__PURE__ */
|
|
45296
|
-
/* @__PURE__ */
|
|
47152
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexShrink: 0, minHeight: 1, children: [
|
|
47153
|
+
/* @__PURE__ */ jsx20(Text20, { color: isSelected ? theme.primary : void 0, children: isSelected ? "\u25B6 " : " " }),
|
|
47154
|
+
/* @__PURE__ */ jsxs19(Text20, { bold: true, color: isSelected ? theme.primary : theme.secondary, children: [
|
|
45297
47155
|
isExpanded ? "\u25BC" : "\u25B6",
|
|
45298
47156
|
" ",
|
|
45299
47157
|
item.label
|
|
45300
47158
|
] }),
|
|
45301
|
-
/* @__PURE__ */
|
|
47159
|
+
/* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
|
|
45302
47160
|
" (",
|
|
45303
47161
|
itemCount,
|
|
45304
47162
|
")"
|
|
@@ -45312,13 +47170,13 @@ var Settings = () => {
|
|
|
45312
47170
|
switch (setting.type) {
|
|
45313
47171
|
case "text":
|
|
45314
47172
|
case "password":
|
|
45315
|
-
return /* @__PURE__ */
|
|
45316
|
-
/* @__PURE__ */
|
|
47173
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexShrink: 0, minHeight: 1, children: [
|
|
47174
|
+
/* @__PURE__ */ jsxs19(Text20, { color: theme.primary, children: [
|
|
45317
47175
|
" \u25B6 ",
|
|
45318
47176
|
setting.label,
|
|
45319
47177
|
": "
|
|
45320
47178
|
] }),
|
|
45321
|
-
/* @__PURE__ */
|
|
47179
|
+
/* @__PURE__ */ jsx20(
|
|
45322
47180
|
TextInput,
|
|
45323
47181
|
{
|
|
45324
47182
|
value: localValue,
|
|
@@ -45331,13 +47189,13 @@ var Settings = () => {
|
|
|
45331
47189
|
)
|
|
45332
47190
|
] }, setting.key);
|
|
45333
47191
|
case "number":
|
|
45334
|
-
return /* @__PURE__ */
|
|
45335
|
-
/* @__PURE__ */
|
|
47192
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexShrink: 0, minHeight: 1, children: [
|
|
47193
|
+
/* @__PURE__ */ jsxs19(Text20, { color: theme.primary, children: [
|
|
45336
47194
|
" \u25B6 ",
|
|
45337
47195
|
setting.label,
|
|
45338
47196
|
": "
|
|
45339
47197
|
] }),
|
|
45340
|
-
/* @__PURE__ */
|
|
47198
|
+
/* @__PURE__ */ jsx20(
|
|
45341
47199
|
NumberInput,
|
|
45342
47200
|
{
|
|
45343
47201
|
value: localValue,
|
|
@@ -45353,13 +47211,13 @@ var Settings = () => {
|
|
|
45353
47211
|
)
|
|
45354
47212
|
] }, setting.key);
|
|
45355
47213
|
case "select":
|
|
45356
|
-
return /* @__PURE__ */
|
|
45357
|
-
/* @__PURE__ */
|
|
47214
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", flexShrink: 0, children: [
|
|
47215
|
+
/* @__PURE__ */ jsx20(Box20, { flexShrink: 0, children: /* @__PURE__ */ jsxs19(Text20, { color: theme.primary, children: [
|
|
45358
47216
|
" \u25B6 ",
|
|
45359
47217
|
setting.label,
|
|
45360
47218
|
": "
|
|
45361
47219
|
] }) }),
|
|
45362
|
-
/* @__PURE__ */
|
|
47220
|
+
/* @__PURE__ */ jsx20(Box20, { marginLeft: 4, children: /* @__PURE__ */ jsx20(
|
|
45363
47221
|
SelectInput,
|
|
45364
47222
|
{
|
|
45365
47223
|
options: setting.options ?? [],
|
|
@@ -45382,15 +47240,15 @@ var Settings = () => {
|
|
|
45382
47240
|
}
|
|
45383
47241
|
}
|
|
45384
47242
|
const displayValue = setting.type === "toggle" ? value ? "Yes" : "No" : setting.type === "password" ? value ? "********" : "(none)" : setting.type === "select" ? setting.options?.find((o) => o.value === value)?.label ?? value : `${value}${setting.suffix ?? ""}`;
|
|
45385
|
-
return /* @__PURE__ */
|
|
45386
|
-
/* @__PURE__ */
|
|
45387
|
-
/* @__PURE__ */
|
|
47243
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexShrink: 0, minHeight: 1, children: [
|
|
47244
|
+
/* @__PURE__ */ jsx20(Text20, { color: isSelected ? theme.primary : void 0, children: isSelected ? " \u25B6 " : " " }),
|
|
47245
|
+
/* @__PURE__ */ jsxs19(Text20, { bold: isSelected, color: isSelected ? theme.primary : void 0, children: [
|
|
45388
47246
|
setting.label,
|
|
45389
47247
|
":"
|
|
45390
47248
|
] }),
|
|
45391
|
-
/* @__PURE__ */
|
|
45392
|
-
/* @__PURE__ */
|
|
45393
|
-
|
|
47249
|
+
/* @__PURE__ */ jsx20(Text20, { children: " " }),
|
|
47250
|
+
/* @__PURE__ */ jsx20(
|
|
47251
|
+
Text20,
|
|
45394
47252
|
{
|
|
45395
47253
|
color: setting.type === "toggle" ? value ? theme.success : theme.error : theme.secondary,
|
|
45396
47254
|
children: displayValue
|
|
@@ -45398,36 +47256,36 @@ var Settings = () => {
|
|
|
45398
47256
|
)
|
|
45399
47257
|
] }, setting.key);
|
|
45400
47258
|
};
|
|
45401
|
-
return /* @__PURE__ */
|
|
45402
|
-
/* @__PURE__ */
|
|
45403
|
-
/* @__PURE__ */
|
|
45404
|
-
hasMoreAbove && /* @__PURE__ */
|
|
47259
|
+
return /* @__PURE__ */ jsxs19(Box20, { flexDirection: "column", flexGrow: 1, padding: 1, overflow: "hidden", children: [
|
|
47260
|
+
/* @__PURE__ */ jsx20(Box20, { marginBottom: 1, flexShrink: 0, minHeight: 1, children: /* @__PURE__ */ jsx20(Text20, { bold: true, color: theme.primary, children: "\u2500 Settings \u2500" }) }),
|
|
47261
|
+
/* @__PURE__ */ jsx20(Box20, { marginBottom: 1, flexShrink: 0, minHeight: 1, children: /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: isEditing ? "Enter to save, Esc to cancel" : "\u2191/\u2193 navigate \u2022 Enter expand/edit \u2022 Tab collapse all" }) }),
|
|
47262
|
+
hasMoreAbove && /* @__PURE__ */ jsx20(Box20, { flexShrink: 0, minHeight: 1, children: /* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
|
|
45405
47263
|
" \u2191 ",
|
|
45406
47264
|
scrollOffset,
|
|
45407
47265
|
" more above"
|
|
45408
47266
|
] }) }),
|
|
45409
|
-
/* @__PURE__ */
|
|
47267
|
+
/* @__PURE__ */ jsx20(Box20, { flexDirection: "column", flexGrow: 1, overflow: "hidden", children: visibleItems.map((item, visibleIdx) => {
|
|
45410
47268
|
const globalIndex = scrollOffset + visibleIdx;
|
|
45411
47269
|
return renderNavItem(item, globalIndex);
|
|
45412
47270
|
}) }),
|
|
45413
|
-
hasMoreBelow && /* @__PURE__ */
|
|
47271
|
+
hasMoreBelow && /* @__PURE__ */ jsx20(Box20, { flexShrink: 0, minHeight: 1, children: /* @__PURE__ */ jsxs19(Text20, { dimColor: true, children: [
|
|
45414
47272
|
" ",
|
|
45415
47273
|
"\u2193 ",
|
|
45416
47274
|
navItems.length - scrollOffset - MAX_VISIBLE_ROWS,
|
|
45417
47275
|
" more below"
|
|
45418
47276
|
] }) }),
|
|
45419
|
-
rcon.enabled && /* @__PURE__ */
|
|
45420
|
-
/* @__PURE__ */
|
|
45421
|
-
/* @__PURE__ */
|
|
47277
|
+
rcon.enabled && /* @__PURE__ */ jsxs19(Box20, { marginTop: 1, flexShrink: 0, minHeight: 1, children: [
|
|
47278
|
+
/* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "RCON Status: " }),
|
|
47279
|
+
/* @__PURE__ */ jsx20(Text20, { color: rcon.connected ? theme.success : theme.error, children: rcon.connected ? "Connected" : "Disconnected" })
|
|
45422
47280
|
] }),
|
|
45423
|
-
/* @__PURE__ */
|
|
47281
|
+
/* @__PURE__ */ jsx20(Box20, { marginTop: 1, flexShrink: 0, minHeight: 1, children: /* @__PURE__ */ jsx20(Text20, { dimColor: true, children: "Note: Restart server for changes to take effect" }) })
|
|
45424
47282
|
] });
|
|
45425
47283
|
};
|
|
45426
47284
|
|
|
45427
47285
|
// src/tui/screens/Worlds.tsx
|
|
45428
|
-
import { Box as
|
|
45429
|
-
import { useCallback as useCallback6, useEffect as
|
|
45430
|
-
import { Fragment, jsx as
|
|
47286
|
+
import { Box as Box21, Text as Text21, useInput as useInput14 } from "ink";
|
|
47287
|
+
import { useCallback as useCallback6, useEffect as useEffect13, useState as useState16 } from "react";
|
|
47288
|
+
import { Fragment as Fragment5, jsx as jsx21, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
45431
47289
|
function formatBytes2(bytes) {
|
|
45432
47290
|
if (bytes === 0) return "0 B";
|
|
45433
47291
|
const k = 1024;
|
|
@@ -45480,16 +47338,16 @@ var Worlds = () => {
|
|
|
45480
47338
|
const serverStatus = useStore((s) => s.server.status);
|
|
45481
47339
|
const worldGenerating = useStore((s) => s.server.worldGenerating);
|
|
45482
47340
|
const setEditingField = useStore((s) => s.actions.setEditingField);
|
|
45483
|
-
const [mode, setModeInternal] =
|
|
45484
|
-
const [inputPath, setInputPath] =
|
|
45485
|
-
const [newWorldName, setNewWorldName] =
|
|
45486
|
-
const [newWorldSeed, setNewWorldSeed] =
|
|
45487
|
-
const [createStep, setCreateStep] =
|
|
45488
|
-
const [loading, setLoading] =
|
|
45489
|
-
const [operationStatus, setOperationStatus] =
|
|
45490
|
-
const [pendingConfigSelected, setPendingConfigSelected] =
|
|
45491
|
-
const [prevServerStatus, setPrevServerStatus] =
|
|
45492
|
-
const [prevWorldGenerating, setPrevWorldGenerating] =
|
|
47341
|
+
const [mode, setModeInternal] = useState16("list");
|
|
47342
|
+
const [inputPath, setInputPath] = useState16("");
|
|
47343
|
+
const [newWorldName, setNewWorldName] = useState16("");
|
|
47344
|
+
const [newWorldSeed, setNewWorldSeed] = useState16("");
|
|
47345
|
+
const [createStep, setCreateStep] = useState16("name");
|
|
47346
|
+
const [loading, setLoading] = useState16(false);
|
|
47347
|
+
const [operationStatus, setOperationStatus] = useState16("");
|
|
47348
|
+
const [pendingConfigSelected, setPendingConfigSelected] = useState16(false);
|
|
47349
|
+
const [prevServerStatus, setPrevServerStatus] = useState16(serverStatus);
|
|
47350
|
+
const [prevWorldGenerating, setPrevWorldGenerating] = useState16(worldGenerating);
|
|
45493
47351
|
const hasPendingConfig = config.world && !worlds.some((w) => w.name === config.world);
|
|
45494
47352
|
const otherPendingWorlds = pendingWorldNames.filter(
|
|
45495
47353
|
(name) => name !== config.world && !worlds.some((w) => w.name === name)
|
|
@@ -45502,10 +47360,10 @@ var Worlds = () => {
|
|
|
45502
47360
|
},
|
|
45503
47361
|
[setEditingField]
|
|
45504
47362
|
);
|
|
45505
|
-
|
|
47363
|
+
useEffect13(() => {
|
|
45506
47364
|
refresh();
|
|
45507
47365
|
}, [refresh]);
|
|
45508
|
-
|
|
47366
|
+
useEffect13(() => {
|
|
45509
47367
|
if (prevServerStatus !== "online" && serverStatus === "online") {
|
|
45510
47368
|
const timer = setTimeout(() => {
|
|
45511
47369
|
refresh();
|
|
@@ -45515,14 +47373,14 @@ var Worlds = () => {
|
|
|
45515
47373
|
}
|
|
45516
47374
|
setPrevServerStatus(serverStatus);
|
|
45517
47375
|
}, [serverStatus, prevServerStatus, refresh, addLog]);
|
|
45518
|
-
|
|
47376
|
+
useEffect13(() => {
|
|
45519
47377
|
if (prevWorldGenerating && !worldGenerating) {
|
|
45520
47378
|
refresh();
|
|
45521
47379
|
addLog("info", "Refreshed worlds list after world generation completed");
|
|
45522
47380
|
}
|
|
45523
47381
|
setPrevWorldGenerating(worldGenerating);
|
|
45524
47382
|
}, [worldGenerating, prevWorldGenerating, refresh, addLog]);
|
|
45525
|
-
|
|
47383
|
+
useEffect13(() => {
|
|
45526
47384
|
if (!hasPendingConfig && pendingConfigSelected) {
|
|
45527
47385
|
setPendingConfigSelected(false);
|
|
45528
47386
|
}
|
|
@@ -45669,7 +47527,7 @@ var Worlds = () => {
|
|
|
45669
47527
|
},
|
|
45670
47528
|
[closeModal, getSelectedWorld, config.world, deleteWorld2, refresh, addLog]
|
|
45671
47529
|
);
|
|
45672
|
-
|
|
47530
|
+
useInput14(
|
|
45673
47531
|
(input, key) => {
|
|
45674
47532
|
if (modalOpen || loading) return;
|
|
45675
47533
|
if (key.upArrow || input === "k") {
|
|
@@ -45722,7 +47580,7 @@ var Worlds = () => {
|
|
|
45722
47580
|
const world = getSelectedWorld();
|
|
45723
47581
|
if (world && world.name !== config.world) {
|
|
45724
47582
|
openModal(
|
|
45725
|
-
/* @__PURE__ */
|
|
47583
|
+
/* @__PURE__ */ jsx21(
|
|
45726
47584
|
DeleteWorldModal,
|
|
45727
47585
|
{
|
|
45728
47586
|
worldName: world.name,
|
|
@@ -45787,27 +47645,27 @@ var Worlds = () => {
|
|
|
45787
47645
|
handleBackup
|
|
45788
47646
|
]);
|
|
45789
47647
|
if (worldsLoading && worlds.length === 0) {
|
|
45790
|
-
return /* @__PURE__ */
|
|
45791
|
-
/* @__PURE__ */
|
|
45792
|
-
/* @__PURE__ */
|
|
47648
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
47649
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { bold: true, color: theme.primary, children: "\u2500 Worlds \u2500" }) }),
|
|
47650
|
+
/* @__PURE__ */ jsx21(Spinner, { label: "Loading worlds..." })
|
|
45793
47651
|
] });
|
|
45794
47652
|
}
|
|
45795
47653
|
if (error2 && worlds.length === 0) {
|
|
45796
|
-
return /* @__PURE__ */
|
|
45797
|
-
/* @__PURE__ */
|
|
45798
|
-
/* @__PURE__ */
|
|
47654
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
47655
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { bold: true, color: theme.primary, children: "\u2500 Worlds \u2500" }) }),
|
|
47656
|
+
/* @__PURE__ */ jsxs20(Text21, { color: theme.error, children: [
|
|
45799
47657
|
"Error: ",
|
|
45800
47658
|
error2
|
|
45801
47659
|
] }),
|
|
45802
|
-
/* @__PURE__ */
|
|
47660
|
+
/* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Press R to retry" }) })
|
|
45803
47661
|
] });
|
|
45804
47662
|
}
|
|
45805
47663
|
if (mode === "create") {
|
|
45806
|
-
return /* @__PURE__ */
|
|
45807
|
-
/* @__PURE__ */
|
|
45808
|
-
loading ? /* @__PURE__ */
|
|
45809
|
-
/* @__PURE__ */
|
|
45810
|
-
/* @__PURE__ */
|
|
47664
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
47665
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { bold: true, color: theme.primary, children: "\u2500 Create New World \u2500" }) }),
|
|
47666
|
+
loading ? /* @__PURE__ */ jsx21(Spinner, { label: operationStatus }) : createStep === "name" ? /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
47667
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { children: "Enter a name for your new world:" }) }),
|
|
47668
|
+
/* @__PURE__ */ jsx21(Box21, { children: /* @__PURE__ */ jsx21(
|
|
45811
47669
|
TextInput,
|
|
45812
47670
|
{
|
|
45813
47671
|
value: newWorldName,
|
|
@@ -45818,14 +47676,14 @@ var Worlds = () => {
|
|
|
45818
47676
|
focus: true
|
|
45819
47677
|
}
|
|
45820
47678
|
) }),
|
|
45821
|
-
/* @__PURE__ */
|
|
45822
|
-
] }) : /* @__PURE__ */
|
|
45823
|
-
/* @__PURE__ */
|
|
45824
|
-
/* @__PURE__ */
|
|
45825
|
-
/* @__PURE__ */
|
|
47679
|
+
/* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Enter to continue, Esc to cancel" }) })
|
|
47680
|
+
] }) : /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
47681
|
+
/* @__PURE__ */ jsxs20(Box21, { marginBottom: 1, children: [
|
|
47682
|
+
/* @__PURE__ */ jsx21(Text21, { children: "World: " }),
|
|
47683
|
+
/* @__PURE__ */ jsx21(Text21, { color: theme.primary, bold: true, children: newWorldName })
|
|
45826
47684
|
] }),
|
|
45827
|
-
/* @__PURE__ */
|
|
45828
|
-
/* @__PURE__ */
|
|
47685
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { children: "Enter a seed (optional, leave empty for random):" }) }),
|
|
47686
|
+
/* @__PURE__ */ jsx21(Box21, { children: /* @__PURE__ */ jsx21(
|
|
45829
47687
|
TextInput,
|
|
45830
47688
|
{
|
|
45831
47689
|
value: newWorldSeed,
|
|
@@ -45836,26 +47694,26 @@ var Worlds = () => {
|
|
|
45836
47694
|
focus: true
|
|
45837
47695
|
}
|
|
45838
47696
|
) }),
|
|
45839
|
-
/* @__PURE__ */
|
|
45840
|
-
/* @__PURE__ */
|
|
47697
|
+
/* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Enter to create world, Esc to cancel" }) }),
|
|
47698
|
+
/* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: "Note: Start the server to generate the world files" }) })
|
|
45841
47699
|
] })
|
|
45842
47700
|
] });
|
|
45843
47701
|
}
|
|
45844
47702
|
if (mode !== "list") {
|
|
45845
47703
|
const modeTitle = mode === "import" ? "Import World" : mode === "export" ? "Export World" : "Backup World";
|
|
45846
47704
|
const modeHint = mode === "import" ? "Enter path to world folder or .db file" : mode === "export" ? `Export "${selectedWorld}" to path` : `Backup "${selectedWorld}" to path`;
|
|
45847
|
-
return /* @__PURE__ */
|
|
45848
|
-
/* @__PURE__ */
|
|
47705
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexGrow: 1, padding: 1, children: [
|
|
47706
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text21, { bold: true, color: theme.primary, children: [
|
|
45849
47707
|
"\u2500 ",
|
|
45850
47708
|
modeTitle,
|
|
45851
47709
|
" \u2500"
|
|
45852
47710
|
] }) }),
|
|
45853
|
-
loading ? /* @__PURE__ */
|
|
45854
|
-
/* @__PURE__ */
|
|
47711
|
+
loading ? /* @__PURE__ */ jsx21(Spinner, { label: operationStatus }) : /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
47712
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsxs20(Text21, { children: [
|
|
45855
47713
|
modeHint,
|
|
45856
47714
|
":"
|
|
45857
47715
|
] }) }),
|
|
45858
|
-
/* @__PURE__ */
|
|
47716
|
+
/* @__PURE__ */ jsx21(Box21, { children: /* @__PURE__ */ jsx21(
|
|
45859
47717
|
TextInput,
|
|
45860
47718
|
{
|
|
45861
47719
|
value: inputPath,
|
|
@@ -45866,19 +47724,19 @@ var Worlds = () => {
|
|
|
45866
47724
|
focus: true
|
|
45867
47725
|
}
|
|
45868
47726
|
) }),
|
|
45869
|
-
/* @__PURE__ */
|
|
47727
|
+
/* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Enter to confirm, Esc to cancel" }) })
|
|
45870
47728
|
] })
|
|
45871
47729
|
] });
|
|
45872
47730
|
}
|
|
45873
|
-
return /* @__PURE__ */
|
|
45874
|
-
/* @__PURE__ */
|
|
45875
|
-
/* @__PURE__ */
|
|
45876
|
-
worldsLoading && /* @__PURE__ */
|
|
47731
|
+
return /* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", flexGrow: 1, padding: 1, overflow: "hidden", children: [
|
|
47732
|
+
/* @__PURE__ */ jsxs20(Box21, { marginBottom: 1, children: [
|
|
47733
|
+
/* @__PURE__ */ jsx21(Text21, { bold: true, color: theme.primary, children: "\u2500 Worlds \u2500" }),
|
|
47734
|
+
worldsLoading && /* @__PURE__ */ jsx21(Spinner, {})
|
|
45877
47735
|
] }),
|
|
45878
|
-
/* @__PURE__ */
|
|
45879
|
-
/* @__PURE__ */
|
|
45880
|
-
hasPendingConfig && /* @__PURE__ */
|
|
45881
|
-
|
|
47736
|
+
/* @__PURE__ */ jsx21(Box21, { marginBottom: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "\u2191/\u2193 Navigate | Enter Set Active | N New | I Import | E Export | B Backup | D Delete | R Refresh" }) }),
|
|
47737
|
+
/* @__PURE__ */ jsxs20(Box21, { flexDirection: "column", overflow: "hidden", children: [
|
|
47738
|
+
hasPendingConfig && /* @__PURE__ */ jsxs20(
|
|
47739
|
+
Box21,
|
|
45882
47740
|
{
|
|
45883
47741
|
flexDirection: "column",
|
|
45884
47742
|
flexShrink: 0,
|
|
@@ -45887,33 +47745,33 @@ var Worlds = () => {
|
|
|
45887
47745
|
paddingX: pendingConfigSelected || worlds.length === 0 ? 1 : 0,
|
|
45888
47746
|
marginBottom: 1,
|
|
45889
47747
|
children: [
|
|
45890
|
-
/* @__PURE__ */
|
|
45891
|
-
/* @__PURE__ */
|
|
45892
|
-
|
|
47748
|
+
/* @__PURE__ */ jsxs20(Box21, { flexShrink: 0, children: [
|
|
47749
|
+
/* @__PURE__ */ jsx21(
|
|
47750
|
+
Text21,
|
|
45893
47751
|
{
|
|
45894
47752
|
color: pendingConfigSelected ? theme.primary : void 0,
|
|
45895
47753
|
bold: true,
|
|
45896
47754
|
children: config.world
|
|
45897
47755
|
}
|
|
45898
47756
|
),
|
|
45899
|
-
/* @__PURE__ */
|
|
45900
|
-
worldGenerating ? /* @__PURE__ */
|
|
45901
|
-
/* @__PURE__ */
|
|
45902
|
-
/* @__PURE__ */
|
|
45903
|
-
] }) : serverStatus === "starting" ? /* @__PURE__ */
|
|
45904
|
-
/* @__PURE__ */
|
|
45905
|
-
/* @__PURE__ */
|
|
45906
|
-
] }) : /* @__PURE__ */
|
|
47757
|
+
/* @__PURE__ */ jsx21(Text21, { color: theme.success, children: " (Active)" }),
|
|
47758
|
+
worldGenerating ? /* @__PURE__ */ jsxs20(Box21, { marginLeft: 1, children: [
|
|
47759
|
+
/* @__PURE__ */ jsx21(Spinner, {}),
|
|
47760
|
+
/* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: " Generating..." })
|
|
47761
|
+
] }) : serverStatus === "starting" ? /* @__PURE__ */ jsxs20(Box21, { marginLeft: 1, children: [
|
|
47762
|
+
/* @__PURE__ */ jsx21(Spinner, {}),
|
|
47763
|
+
/* @__PURE__ */ jsx21(Text21, { color: theme.info, children: " Server starting..." })
|
|
47764
|
+
] }) : /* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: " - Not generated" })
|
|
45907
47765
|
] }),
|
|
45908
|
-
/* @__PURE__ */
|
|
47766
|
+
/* @__PURE__ */ jsx21(Box21, { marginLeft: 2, flexShrink: 0, children: worldGenerating ? /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "New world is being generated (this may take ~1 minute)" }) : /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Start the server to generate this world" }) })
|
|
45909
47767
|
]
|
|
45910
47768
|
}
|
|
45911
47769
|
),
|
|
45912
|
-
worlds.length === 0 && !config.world ? /* @__PURE__ */
|
|
47770
|
+
worlds.length === 0 && !config.world ? /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "No worlds found. Press N to create one." }) : worlds.map((world) => {
|
|
45913
47771
|
const isSelected = world.name === selectedWorld;
|
|
45914
47772
|
const isActive = world.name === config.world;
|
|
45915
|
-
return /* @__PURE__ */
|
|
45916
|
-
|
|
47773
|
+
return /* @__PURE__ */ jsxs20(
|
|
47774
|
+
Box21,
|
|
45917
47775
|
{
|
|
45918
47776
|
flexDirection: "column",
|
|
45919
47777
|
flexShrink: 0,
|
|
@@ -45922,11 +47780,11 @@ var Worlds = () => {
|
|
|
45922
47780
|
paddingX: isSelected ? 1 : 0,
|
|
45923
47781
|
marginBottom: 1,
|
|
45924
47782
|
children: [
|
|
45925
|
-
/* @__PURE__ */
|
|
45926
|
-
/* @__PURE__ */
|
|
45927
|
-
isActive && /* @__PURE__ */
|
|
45928
|
-
world.pendingSave && /* @__PURE__ */
|
|
45929
|
-
world.backups && world.backups.length > 0 && /* @__PURE__ */
|
|
47783
|
+
/* @__PURE__ */ jsxs20(Box21, { flexShrink: 0, children: [
|
|
47784
|
+
/* @__PURE__ */ jsx21(Text21, { color: isSelected ? theme.primary : void 0, bold: true, children: world.name }),
|
|
47785
|
+
isActive && /* @__PURE__ */ jsx21(Text21, { color: theme.success, children: " (Active)" }),
|
|
47786
|
+
world.pendingSave && /* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: " - Pending Save" }),
|
|
47787
|
+
world.backups && world.backups.length > 0 && /* @__PURE__ */ jsxs20(Text21, { color: theme.info, children: [
|
|
45930
47788
|
" ",
|
|
45931
47789
|
"[",
|
|
45932
47790
|
world.backups.length,
|
|
@@ -45935,27 +47793,27 @@ var Worlds = () => {
|
|
|
45935
47793
|
"]"
|
|
45936
47794
|
] })
|
|
45937
47795
|
] }),
|
|
45938
|
-
/* @__PURE__ */
|
|
45939
|
-
/* @__PURE__ */
|
|
47796
|
+
/* @__PURE__ */ jsx21(Box21, { marginLeft: 2, flexShrink: 0, children: world.pendingSave ? /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "World generated but not yet saved to disk" }) : /* @__PURE__ */ jsxs20(Fragment5, { children: [
|
|
47797
|
+
/* @__PURE__ */ jsxs20(Text21, { dimColor: true, children: [
|
|
45940
47798
|
"Size: ",
|
|
45941
47799
|
formatBytes2(world.size)
|
|
45942
47800
|
] }),
|
|
45943
|
-
/* @__PURE__ */
|
|
45944
|
-
/* @__PURE__ */
|
|
47801
|
+
/* @__PURE__ */ jsx21(Text21, { dimColor: true, children: " | " }),
|
|
47802
|
+
/* @__PURE__ */ jsx21(Text21, { dimColor: true, children: getSourceLabel(world.source) })
|
|
45945
47803
|
] }) }),
|
|
45946
|
-
!world.pendingSave && /* @__PURE__ */
|
|
47804
|
+
!world.pendingSave && /* @__PURE__ */ jsx21(Box21, { marginLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsxs20(Text21, { dimColor: true, children: [
|
|
45947
47805
|
"Modified: ",
|
|
45948
47806
|
formatDate(world.modified)
|
|
45949
47807
|
] }) }),
|
|
45950
|
-
isSelected && world.backups && world.backups.length > 0 && /* @__PURE__ */
|
|
45951
|
-
/* @__PURE__ */
|
|
45952
|
-
world.backups.slice(0, 3).map((backup) => /* @__PURE__ */
|
|
47808
|
+
isSelected && world.backups && world.backups.length > 0 && /* @__PURE__ */ jsxs20(Box21, { marginLeft: 2, flexDirection: "column", flexShrink: 0, children: [
|
|
47809
|
+
/* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Backups:" }),
|
|
47810
|
+
world.backups.slice(0, 3).map((backup) => /* @__PURE__ */ jsx21(Box21, { marginLeft: 2, children: /* @__PURE__ */ jsxs20(Text21, { dimColor: true, children: [
|
|
45953
47811
|
"\u2022",
|
|
45954
47812
|
" ",
|
|
45955
47813
|
formatBackupTimestamp(backup.backupTimestamp ?? ""),
|
|
45956
47814
|
backup.pendingSave ? " (pending)" : ` (${formatBytes2(backup.size)})`
|
|
45957
47815
|
] }) }, backup.name)),
|
|
45958
|
-
world.backups.length > 3 && /* @__PURE__ */
|
|
47816
|
+
world.backups.length > 3 && /* @__PURE__ */ jsx21(Box21, { marginLeft: 2, children: /* @__PURE__ */ jsxs20(Text21, { dimColor: true, children: [
|
|
45959
47817
|
"... and ",
|
|
45960
47818
|
world.backups.length - 3,
|
|
45961
47819
|
" more"
|
|
@@ -45966,30 +47824,30 @@ var Worlds = () => {
|
|
|
45966
47824
|
world.name
|
|
45967
47825
|
);
|
|
45968
47826
|
}),
|
|
45969
|
-
otherPendingWorlds.map((name) => /* @__PURE__ */
|
|
45970
|
-
|
|
47827
|
+
otherPendingWorlds.map((name) => /* @__PURE__ */ jsxs20(
|
|
47828
|
+
Box21,
|
|
45971
47829
|
{
|
|
45972
47830
|
flexDirection: "column",
|
|
45973
47831
|
flexShrink: 0,
|
|
45974
47832
|
marginBottom: 1,
|
|
45975
47833
|
children: [
|
|
45976
|
-
/* @__PURE__ */
|
|
45977
|
-
/* @__PURE__ */
|
|
45978
|
-
/* @__PURE__ */
|
|
47834
|
+
/* @__PURE__ */ jsxs20(Box21, { flexShrink: 0, children: [
|
|
47835
|
+
/* @__PURE__ */ jsx21(Text21, { dimColor: true, children: name }),
|
|
47836
|
+
/* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: " - Not generated" })
|
|
45979
47837
|
] }),
|
|
45980
|
-
/* @__PURE__ */
|
|
47838
|
+
/* @__PURE__ */ jsx21(Box21, { marginLeft: 2, flexShrink: 0, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: "Set as active and start the server to generate" }) })
|
|
45981
47839
|
]
|
|
45982
47840
|
},
|
|
45983
47841
|
`pending-${name}`
|
|
45984
47842
|
))
|
|
45985
47843
|
] }),
|
|
45986
|
-
operationStatus && /* @__PURE__ */
|
|
45987
|
-
serverStatus !== "offline" && /* @__PURE__ */
|
|
47844
|
+
operationStatus && /* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { dimColor: true, children: operationStatus }) }),
|
|
47845
|
+
serverStatus !== "offline" && /* @__PURE__ */ jsx21(Box21, { marginTop: 1, children: /* @__PURE__ */ jsx21(Text21, { color: theme.warning, children: "Stop the server to change or delete worlds" }) })
|
|
45988
47846
|
] });
|
|
45989
47847
|
};
|
|
45990
47848
|
|
|
45991
47849
|
// src/tui/App.tsx
|
|
45992
|
-
import { jsx as
|
|
47850
|
+
import { jsx as jsx22, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
45993
47851
|
var screens = {
|
|
45994
47852
|
dashboard: Dashboard,
|
|
45995
47853
|
settings: Settings,
|
|
@@ -46006,11 +47864,11 @@ var App = () => {
|
|
|
46006
47864
|
const openModal = useStore((s) => s.actions.openModal);
|
|
46007
47865
|
const addLog = useStore((s) => s.actions.addLog);
|
|
46008
47866
|
useConfigSync();
|
|
46009
|
-
|
|
47867
|
+
useEffect14(() => {
|
|
46010
47868
|
addLog("info", "TUI started");
|
|
46011
47869
|
addLog("info", "Press 1-4 to navigate, ? for help, Q to quit");
|
|
46012
47870
|
}, [addLog]);
|
|
46013
|
-
|
|
47871
|
+
useInput15((input, key) => {
|
|
46014
47872
|
if (modalOpen || editingField) return;
|
|
46015
47873
|
if (input === "q" || input === "Q" || key.ctrl && input === "c") {
|
|
46016
47874
|
addLog("info", "Shutting down...");
|
|
@@ -46019,7 +47877,7 @@ var App = () => {
|
|
|
46019
47877
|
return;
|
|
46020
47878
|
}
|
|
46021
47879
|
if (input === "?") {
|
|
46022
|
-
openModal(/* @__PURE__ */
|
|
47880
|
+
openModal(/* @__PURE__ */ jsx22(HelpOverlay, {}));
|
|
46023
47881
|
return;
|
|
46024
47882
|
}
|
|
46025
47883
|
if (input === "1") setScreen("dashboard");
|
|
@@ -46028,25 +47886,25 @@ var App = () => {
|
|
|
46028
47886
|
if (input === "4") setScreen("console");
|
|
46029
47887
|
});
|
|
46030
47888
|
const ScreenComponent = screens[activeScreen] ?? Dashboard;
|
|
46031
|
-
return /* @__PURE__ */
|
|
46032
|
-
/* @__PURE__ */
|
|
46033
|
-
/* @__PURE__ */
|
|
46034
|
-
/* @__PURE__ */
|
|
46035
|
-
/* @__PURE__ */
|
|
46036
|
-
|
|
47889
|
+
return /* @__PURE__ */ jsxs21(Box22, { flexDirection: "column", height: "100%", width: "100%", children: [
|
|
47890
|
+
/* @__PURE__ */ jsx22(Header, {}),
|
|
47891
|
+
/* @__PURE__ */ jsxs21(Box22, { flexGrow: 1, flexDirection: "row", height: "100%", children: [
|
|
47892
|
+
/* @__PURE__ */ jsx22(Menu, {}),
|
|
47893
|
+
/* @__PURE__ */ jsx22(
|
|
47894
|
+
Box22,
|
|
46037
47895
|
{
|
|
46038
47896
|
flexGrow: 1,
|
|
46039
47897
|
borderStyle: "single",
|
|
46040
47898
|
borderColor: theme.muted,
|
|
46041
47899
|
flexDirection: "column",
|
|
46042
47900
|
height: "100%",
|
|
46043
|
-
children: /* @__PURE__ */
|
|
47901
|
+
children: /* @__PURE__ */ jsx22(ScreenComponent, {})
|
|
46044
47902
|
}
|
|
46045
47903
|
)
|
|
46046
47904
|
] }),
|
|
46047
|
-
/* @__PURE__ */
|
|
46048
|
-
modalOpen && modalContent && /* @__PURE__ */
|
|
46049
|
-
|
|
47905
|
+
/* @__PURE__ */ jsx22(LogFeed, {}),
|
|
47906
|
+
modalOpen && modalContent && /* @__PURE__ */ jsx22(
|
|
47907
|
+
Box22,
|
|
46050
47908
|
{
|
|
46051
47909
|
position: "absolute",
|
|
46052
47910
|
flexDirection: "column",
|
|
@@ -46064,20 +47922,20 @@ var App = () => {
|
|
|
46064
47922
|
// src/tui/components/PathInput.tsx
|
|
46065
47923
|
import fs10 from "fs/promises";
|
|
46066
47924
|
import path8 from "path";
|
|
46067
|
-
import { Box as
|
|
46068
|
-
import { useEffect as
|
|
46069
|
-
import { jsx as
|
|
47925
|
+
import { Box as Box23, Text as Text22, useInput as useInput16 } from "ink";
|
|
47926
|
+
import { useEffect as useEffect15, useState as useState17 } from "react";
|
|
47927
|
+
import { jsx as jsx23, jsxs as jsxs22 } from "react/jsx-runtime";
|
|
46070
47928
|
|
|
46071
47929
|
// src/tui/components/StatusBar.tsx
|
|
46072
|
-
import { Box as
|
|
46073
|
-
import { jsx as
|
|
47930
|
+
import { Box as Box24, Text as Text23 } from "ink";
|
|
47931
|
+
import { jsx as jsx24, jsxs as jsxs23 } from "react/jsx-runtime";
|
|
46074
47932
|
|
|
46075
47933
|
// src/tui/components/Toggle.tsx
|
|
46076
|
-
import { Box as
|
|
46077
|
-
import { jsx as
|
|
47934
|
+
import { Box as Box25, Text as Text24, useInput as useInput17 } from "ink";
|
|
47935
|
+
import { jsx as jsx25, jsxs as jsxs24 } from "react/jsx-runtime";
|
|
46078
47936
|
|
|
46079
47937
|
// src/tui/hooks/useLogs.ts
|
|
46080
|
-
import { useCallback as useCallback7, useEffect as
|
|
47938
|
+
import { useCallback as useCallback7, useEffect as useEffect16 } from "react";
|
|
46081
47939
|
|
|
46082
47940
|
// src/tui/mod.ts
|
|
46083
47941
|
function launchTui() {
|
|
@@ -46085,7 +47943,7 @@ function launchTui() {
|
|
|
46085
47943
|
}
|
|
46086
47944
|
|
|
46087
47945
|
// src/mod.ts
|
|
46088
|
-
var VERSION2 = "1.
|
|
47946
|
+
var VERSION2 = "1.8.0";
|
|
46089
47947
|
var APP_NAME = "Land of OZ - Valheim DSM";
|
|
46090
47948
|
|
|
46091
47949
|
// main.ts
|