Get wrapper working

This commit is contained in:
zontreck 2024-06-04 21:51:53 -07:00
parent 0ae098318a
commit 26434a9123
19 changed files with 211 additions and 255 deletions

3
.gitignore vendored
View file

@ -53,3 +53,6 @@ build
out out
RELEASE RELEASE
*.DEPS
*.dat

View file

@ -7,9 +7,36 @@ WORKDIR /app
RUN rm RELEASE RUN rm RELEASE
RUN PATH=$PATH:/flutter/bin /bin/bash /app/compile.sh RUN PATH=$PATH:/flutter/bin /bin/bash /app/compile.sh
FROM git.zontreck.com/ariascreations/conanservermanager:runtimedeps FROM git.zontreck.com/ariascreations/conanservermanager:builddeps AS RCONBUILD
WORKDIR /app
RUN apt-get -y install build-essential git
RUN git clone https://github.com/Tiiffi/mcrcon
WORKDIR /app/mcrcon
RUN make
FROM ghcr.io/parkervcp/yolks:wine_latest
WORKDIR /app WORKDIR /app
COPY --from=BUILDER /app/out/server /app/server COPY --from=BUILDER /app/out/server /app/server
COPY --from=RCONBUILD /app/mcrcon/mcrcon /app/rcon
EXPOSE 25306/tcp
EXPOSE 7779/tcp
EXPOSE 7779/udp
EXPOSE 7780/udp
EXPOSE 7780/tcp
EXPOSE 7781/udp
EXPOSE 7781/tcp
EXPOSE 7782/udp
EXPOSE 7782/tcp
USER container

View file

@ -39,7 +39,8 @@ void main() async {
print("Initialized Steamcmd"); print("Initialized Steamcmd");
print("Running winetricks"); print("Running winetricks");
await settings.initializeWine(); //if (!Directory(settings.getWinePrefixPath()).existsSync())
// await settings.initializeWine();
print("Finished installing needed DLLs"); print("Finished installing needed DLLs");
print("Checking for game server updates..."); print("Checking for game server updates...");
@ -71,15 +72,6 @@ void main() async {
} }
print("Starting up server manager server wrapper"); print("Starting up server manager server wrapper");
File logFile = File(PathHelper.builder(settings.getServerPath())
.resolve("ConanSandbox")
.resolve("Saved")
.resolve("Logs")
.resolve("ConanSandbox.log")
.build());
await logFile.create(recursive: true);
tailAndPrint(logFile);
await PacketServer.start(settings.inst?.serverSettings.WrapperPort ?? 25306); await PacketServer.start(settings.inst?.serverSettings.WrapperPort ?? 25306);

View file

@ -18,18 +18,12 @@ then
docker build -t git.zontreck.com/ariascreations/conanservermanager:builddeps docker_images/builddeps docker build -t git.zontreck.com/ariascreations/conanservermanager:builddeps docker_images/builddeps
docker push git.zontreck.com/ariascreations/conanservermanager:builddeps docker push git.zontreck.com/ariascreations/conanservermanager:builddeps
docker build -t git.zontreck.com/ariascreations/conanservermanager:runtimedeps docker_images/runtimedeps
docker push git.zontreck.com/ariascreations/conanservermanager:runtimedeps
rm RELEASE.DEPS rm RELEASE.DEPS
fi fi
docker build -t git.zontreck.com/ariascreations/conanservermanager:stable $(pwd) docker build -t git.zontreck.com/ariascreations/conanservermanager:stable $(pwd)
docker push git.zontreck.com/ariascreations/conanservermanager:stable docker push git.zontreck.com/ariascreations/conanservermanager:stable
docker build -t git.zontreck.com/ariascreations/conanservermanager:latest docker_images/latest
docker push git.zontreck.com/ariascreations/conanservermanager:latest
docker build -t git.zontreck.com/ariascreations/conanservermanager:installer docker_images/installer docker build -t git.zontreck.com/ariascreations/conanservermanager:installer docker_images/installer
docker push git.zontreck.com/ariascreations/conanservermanager:installer docker push git.zontreck.com/ariascreations/conanservermanager:installer

View file

@ -1,4 +1,4 @@
FROM debian:latest FROM ghcr.io/parkervcp/yolks:debian
WORKDIR / WORKDIR /

View file

@ -1,20 +0,0 @@
FROM debian:latest
RUN apt-get update -y
RUN apt-get upgrade -y
RUN apt-get install -y gnupg2 numactl tzdata software-properties-common libntlm0 winbind xvfb xauth python3 libncurses5:i386 libncurses6:i386 libsdl2-2.0-0 libsdl2-2.0-0:i386
FROM git.zontreck.com/ariascreations/conanservermanager:stable
COPY ./entrypoint.sh /entrypoint.sh
EXPOSE 25306/tcp
EXPOSE 7779/tcp
EXPOSE 7779/udp
EXPOSE 7780/udp
EXPOSE 7780/tcp
EXPOSE 7781/udp
EXPOSE 7781/tcp
EXPOSE 7782/udp
EXPOSE 7782/tcp
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]

View file

@ -1,60 +0,0 @@
#!/bin/bash
# Copied the entrypoint and slightly altered, from parkervcp's dart yolk.
cd /home/container
# Information output
echo "Running on Debian $(cat /etc/debian_version)"
echo "Current timezone: $(cat /etc/timezone)"
wine --version
if [[ $XVFB == 1 ]]; then
Xvfb :0 -screen 0 ${DISPLAY_WIDTH}x${DISPLAY_HEIGHT}x${DISPLAY_DEPTH} &
fi
# Install necessary to run packages
echo "First launch will throw some errors. Ignore them"
mkdir -p $WINEPREFIX
# Check if wine-gecko required and install it if so
echo "Installing Gecko"
WINETRICKS_RUN=${WINETRICKS_RUN/gecko}
if [ ! -f "$WINEPREFIX/gecko_x86.msi" ]; then
wget -q -O $WINEPREFIX/gecko_x86.msi http://dl.winehq.org/wine/wine-gecko/2.47.4/wine_gecko-2.47.4-x86.msi
fi
if [ ! -f "$WINEPREFIX/gecko_x86_64.msi" ]; then
wget -q -O $WINEPREFIX/gecko_x86_64.msi http://dl.winehq.org/wine/wine-gecko/2.47.4/wine_gecko-2.47.4-x86_64.msi
fi
wine msiexec /i $WINEPREFIX/gecko_x86.msi /qn /quiet /norestart /log $WINEPREFIX/gecko_x86_install.log
wine msiexec /i $WINEPREFIX/gecko_x86_64.msi /qn /quiet /norestart /log $WINEPREFIX/gecko_x86_64_install.log
# Check if wine-mono required and install it if so
echo "Installing mono"
WINETRICKS_RUN=${WINETRICKS_RUN/mono}
if [ ! -f "$WINEPREFIX/mono.msi" ]; then
wget -q -O $WINEPREFIX/mono.msi https://dl.winehq.org/wine/wine-mono/9.1.0/wine-mono-9.1.0-x86.msi
fi
wine msiexec /i $WINEPREFIX/mono.msi /qn /quiet /norestart /log $WINEPREFIX/mono_install.log
# List and install other packages
for trick in $WINETRICKS_RUN; do
echo "Installing $trick"
winetricks -q $trick
done
# Replace startup variables
MODIFIED_STARTUP=$(echo -e ${STARTUP} | sed -e 's/{{/${/g' -e 's/}}/}/g')
echo ":/home/container$ ${MODIFIED_STARTUP}"
# Run the server
eval ${MODIFIED_STARTUP}

View file

@ -1,53 +0,0 @@
FROM debian:latest
RUN dpkg --add-architecture i386 \
&& apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y tar curl gcc g++ lib32gcc-s1 libgcc-12-dev libgcc-11-dev libcurl4-gnutls-dev:i386 libssl-dev:i386 libcurl4:i386 lib32tinfo6 libtinfo6:i386 lib32z1 lib32stdc++6 libncurses5:i386 libcurl3-gnutls:i386 libsdl2-2.0-0:i386 libsdl2-2.0-0 iproute2 gdb libsdl1.2debian libfontconfig1 telnet net-tools netcat-traditional tzdata numactl xvfb wget tini \
&& useradd -m -d /home/container container
## install rcon
RUN cd /tmp/ \
&& curl -sSL https://github.com/gorcon/rcon-cli/releases/download/v0.10.3/rcon-0.10.3-amd64_linux.tar.gz > rcon.tar.gz \
&& tar xvf rcon.tar.gz \
&& mv rcon-0.10.3-amd64_linux/rcon /usr/local/bin/
# Temp fix for things that still need libssl1.1
RUN if [ "$(uname -m)" = "x86_64" ]; then \
wget http://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.0g-2ubuntu4_amd64.deb && \
dpkg -i libssl1.1_1.1.0g-2ubuntu4_amd64.deb && \
rm libssl1.1_1.1.0g-2ubuntu4_amd64.deb; \
fi
# Merge wine dockerfiles
## install required packages
RUN dpkg --add-architecture i386 \
&& apt update -y \
&& apt install -y --no-install-recommends gnupg2 numactl tzdata software-properties-common libntlm0 winbind xvfb xauth python3 libncurses5:i386 libncurses6:i386 libsdl2-2.0-0 libsdl2-2.0-0:i386
# Install wine with recommends
RUN mkdir -pm755 /etc/apt/keyrings
RUN wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
RUN wget -NP /etc/apt/sources.list.d/ https://dl.winehq.org/wine-builds/debian/dists/bookworm/winehq-bookworm.sources
RUN apt update
RUN apt install --install-recommends winehq-stable cabextract wine-binfmt -y
# Set up Winetricks
RUN wget -q -O /usr/sbin/winetricks https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks \
&& chmod +x /usr/sbin/winetricks
ENV HOME=/home/container
ENV WINEPREFIX=/home/container/.wine
ENV WINETRICKS_RUN="vcrun2013 vcrun2015 corefonts"
ENV WINEDLLOVERRIDES="mscoree,mshtml="
ENV DISPLAY=:0
ENV DISPLAY_WIDTH=1024
ENV DISPLAY_HEIGHT=768
ENV DISPLAY_DEPTH=16
ENV XVFB=1
USER container
ENV USER=container HOME=/home/container
WORKDIR /home/container
STOPSIGNAL SIGINT

View file

@ -18,6 +18,7 @@ Future<void> doDownloadMods(bool jail) async {
"anonymous", "anonymous",
]; ];
for (Mod M in settings.inst!.mods) { for (Mod M in settings.inst!.mods) {
if (!M.enabled) continue;
manifest.add("+workshop_download_item"); manifest.add("+workshop_download_item");
manifest.add("440900"); manifest.add("440900");
manifest.add("${M.mod_id}"); manifest.add("${M.mod_id}");
@ -42,6 +43,11 @@ Future<List<Mod>> doScanMods(bool jail) async {
List<Mod> ret = []; List<Mod> ret = [];
for (Mod M in settings.inst!.mods.toList()) { for (Mod M in settings.inst!.mods.toList()) {
if (!M.enabled) {
ret.add(M);
continue;
}
var index = settings.inst!.mods.indexOf(M); var index = settings.inst!.mods.indexOf(M);
// Assemble final path. // Assemble final path.
String modsPath = PathHelper.builder( String modsPath = PathHelper.builder(

View file

@ -73,7 +73,7 @@ class GameServerPageState extends State<GameServerPage> {
context, "/server/ports", context, "/server/ports",
arguments: settings.inst!.serverSettings); arguments: settings.inst!.serverSettings);
if (reply != null) return; // If null, no change. if (reply == null) return; // If null, no change.
setState(() { setState(() {
settings.inst!.serverSettings = reply as ServerSettings; settings.inst!.serverSettings = reply as ServerSettings;

View file

@ -54,7 +54,8 @@ class ModManagerState extends State<ModManager> {
padding: EdgeInsets.all(12), padding: EdgeInsets.all(12),
child: ListTile( child: ListTile(
title: Text(mod.mod_name), title: Text(mod.mod_name),
subtitle: Text("ID: ${mod.mod_id}\nLoad Order: ${idx}"), subtitle: Text(
"ID: ${mod.mod_id}\nLoad Order: ${idx}\nEnabled: ${mod.enabled}"),
onTap: () async { onTap: () async {
final reply = await Navigator.pushNamed( final reply = await Navigator.pushNamed(
context, "/server/mods/edit", context, "/server/mods/edit",
@ -63,7 +64,8 @@ class ModManagerState extends State<ModManager> {
mod_name: mod.mod_name, mod_name: mod.mod_name,
mod_pak: mod.mod_pak, mod_pak: mod.mod_pak,
mod_hash: mod.mod_hash, mod_hash: mod.mod_hash,
newMod: false)); newMod: false,
enabled: mod.enabled));
if (reply != null) { if (reply != null) {
if (reply is bool) { if (reply is bool) {
@ -100,8 +102,12 @@ class ModManagerState extends State<ModManager> {
} }
} }
class ModPage extends StatelessWidget { class ModPage extends StatefulWidget {
bool initDone = false; @override
State<StatefulWidget> createState() => ModPageState();
}
class ModPageState extends State<ModPage> {
TextEditingController id = TextEditingController(); TextEditingController id = TextEditingController();
TextEditingController name = TextEditingController(); TextEditingController name = TextEditingController();
String instance = ""; String instance = "";
@ -109,23 +115,25 @@ class ModPage extends StatelessWidget {
bool willDelete = false; bool willDelete = false;
String pak = "Not initialized"; String pak = "Not initialized";
String hash = ""; String hash = "";
bool enabled = false;
@override
void didChangeDependencies() {
final args = ModalRoute.of(context)!.settings.arguments as Mod?;
if (args != null) {
id.text = args.mod_id.toString();
name.text = args.mod_name;
isNewMod = args.newMod;
instance = args.mod_instance_id();
pak = args.mod_pak;
hash = args.mod_hash;
enabled = args.enabled;
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final args = ModalRoute.of(context)!.settings.arguments as Mod?;
if (!initDone) {
initDone = true;
if (args != null) {
id.text = args.mod_id.toString();
name.text = args.mod_name;
isNewMod = args.newMod;
instance = args.mod_instance_id();
pak = args.mod_pak;
hash = args.mod_hash;
}
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Mod Editor"), title: Text("Mod Editor"),
@ -142,8 +150,13 @@ class ModPage extends StatelessWidget {
if (willDelete) { if (willDelete) {
Navigator.pop(context, true); Navigator.pop(context, true);
} else { } else {
Navigator.pop(context, Navigator.pop(
Mod(mod_id: idVal, mod_name: name.text, newMod: false)); context,
Mod(
mod_id: idVal,
mod_name: name.text,
newMod: false,
enabled: enabled));
} }
}, },
), ),
@ -202,6 +215,16 @@ class ModPage extends StatelessWidget {
title: Text("Mod Hash"), title: Text("Mod Hash"),
subtitle: Text(hash), subtitle: Text(hash),
), ),
SwitchListTile(
value: enabled,
onChanged: (V) {
setState(() {
enabled = V;
});
},
title: Text("Enabled"),
subtitle: Text("Whether mod is enabled or not"),
),
if (!isNewMod) if (!isNewMod)
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
@ -216,7 +239,7 @@ class ModPage extends StatelessWidget {
), ),
Text("Remove Mod") Text("Remove Mod")
], ],
)) )),
]), ]),
), ),
); );

View file

@ -12,20 +12,21 @@ class ServerSettingsPage extends StatefulWidget {
class ServerSettingsState extends State<ServerSettingsPage> { class ServerSettingsState extends State<ServerSettingsPage> {
String pass = ""; String pass = "";
TextEditingController passwordController = TextEditingController(); TextEditingController passwordController = TextEditingController();
int rconPort = 0; TextEditingController rconPortController = TextEditingController();
int gPort = 0; TextEditingController qPortController = TextEditingController();
int qPort = 0; TextEditingController wrapperPortController = TextEditingController();
int wPort = 25306;
TextEditingController gPortController = TextEditingController();
@override @override
void didChangeDependencies() { void didChangeDependencies() {
var args = ModalRoute.of(context)!.settings.arguments as ServerSettings; var args = ModalRoute.of(context)!.settings.arguments as ServerSettings;
passwordController.text = args.RconPassword; passwordController.text = args.RconPassword;
rconPort = args.RconPort; rconPortController.text = "${args.RconPort}";
gPort = args.GamePort; gPortController.text = "${args.GamePort}";
qPort = args.QueryPort; qPortController.text = "${args.QueryPort}";
wPort = args.WrapperPort; wrapperPortController.text = "${args.WrapperPort}";
} }
@override @override
@ -61,20 +62,9 @@ class ServerSettingsState extends State<ServerSettingsPage> {
width: 256, width: 256,
child: ListTile( child: ListTile(
title: Text("Rcon Port"), title: Text("Rcon Port"),
subtitle: Text("$rconPort"),
), ),
), ),
Expanded( Expanded(child: TextField(controller: rconPortController))
child: Slider(
value: rconPort.toDouble(),
onChanged: (value) {
setState(() {
rconPort = value.toInt();
});
},
min: 5000,
max: 8000,
))
], ],
), ),
Row( Row(
@ -83,20 +73,9 @@ class ServerSettingsState extends State<ServerSettingsPage> {
width: 256, width: 256,
child: ListTile( child: ListTile(
title: Text("Game Port"), title: Text("Game Port"),
subtitle: Text("$gPort"),
), ),
), ),
Expanded( Expanded(child: TextField(controller: gPortController))
child: Slider(
value: gPort.toDouble(),
onChanged: (value) {
setState(() {
gPort = value.toInt();
});
},
min: 5000,
max: 8000,
))
], ],
), ),
Row( Row(
@ -105,22 +84,28 @@ class ServerSettingsState extends State<ServerSettingsPage> {
width: 256, width: 256,
child: ListTile( child: ListTile(
title: Text("Query Port"), title: Text("Query Port"),
subtitle: Text("$qPort"),
), ),
), ),
Expanded( Expanded(
child: Slider( child: TextField(
value: qPort.toDouble(), controller: qPortController,
onChanged: (value) {
setState(() {
qPort = value.toInt();
});
},
min: 5000,
max: 8000,
)) ))
], ],
), ),
Row(
children: [
SizedBox(
width: 256,
child: ListTile(
title: Text("Wrapper Port"),
),
),
Expanded(
child: TextField(
controller: wrapperPortController,
)),
],
),
Row( Row(
children: [ children: [
ElevatedButton( ElevatedButton(
@ -129,10 +114,11 @@ class ServerSettingsState extends State<ServerSettingsPage> {
context, context,
ServerSettings( ServerSettings(
RconPassword: passwordController.text, RconPassword: passwordController.text,
RconPort: rconPort, RconPort: int.parse(rconPortController.text),
GamePort: gPort, GamePort: int.parse(gPortController.text),
QueryPort: qPort, QueryPort: int.parse(qPortController.text),
WrapperPort: wPort)); WrapperPort:
int.parse(wrapperPortController.text)));
}, },
child: Text("Submit")) child: Text("Submit"))
], ],

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:libac_dart/packets/packets.dart'; import 'package:libac_dart/packets/packets.dart';
import 'package:libac_dart/utils/IOTools.dart'; import 'package:libac_dart/utils/IOTools.dart';
import 'package:libac_dart/utils/TimeUtils.dart';
import 'package:servermanager/game.dart'; import 'package:servermanager/game.dart';
import 'package:servermanager/structs/SessionData.dart'; import 'package:servermanager/structs/SessionData.dart';
import 'package:servermanager/structs/mod.dart'; import 'package:servermanager/structs/mod.dart';
@ -81,11 +82,20 @@ class StateMachine {
} }
static Future<void> monitorProcess() async { static Future<void> monitorProcess() async {
try { Settings settings = Settings();
int code = await PROC!.exitCode; // Ping RCON. If we can connect, the server is alive.
DeadProcKillswitch.complete(); // Only start pinging once a minute, after the first 10 minutes.
} catch (E) { if (SessionData.operating_time.getTotalSeconds() >
DeadProcKillswitch.complete(); Time(minutes: 10, hours: 0, seconds: 0).getTotalSeconds() &&
!SessionData.canPingServer) {
SessionData.canPingServer = true;
}
if (SessionData.canPingServer) {
SessionData.timeSinceLastPing.tickUp();
if (SessionData.timeSinceLastPing.getTotalSeconds() > 60) {
SessionData.timeSinceLastPing.apply(0);
}
} }
} }
@ -114,6 +124,7 @@ class StateMachine {
print("Sending killswitch to server"); print("Sending killswitch to server");
PROC!.kill(ProcessSignal.sigkill); PROC!.kill(ProcessSignal.sigkill);
PacketServer.socket!.close();
}); });
changeState(States.Inactive); changeState(States.Inactive);
@ -138,20 +149,34 @@ class StateMachine {
"-console" "-console"
]; ];
// Start the server now // Start the server now
String executable = PathHelper.builder(settings.getServerPath())
.resolve("ConanSandbox")
.resolve("Binaries")
.resolve("Win64")
.resolve("ConanSandboxServer-Win64-Shipping.exe")
.build();
if (Platform.isWindows) { if (Platform.isWindows) {
PROC = await Process.start( PROC = await Process.start(executable, conanArgs,
PathHelper.combine(
settings.getServerPath(), "ConanSandboxServer.exe"),
conanArgs,
workingDirectory: settings.getServerPath()); workingDirectory: settings.getServerPath());
} else { } else {
runDetachedWine( runDetachedWine(executable, conanArgs, settings.getServerPath());
PathHelper.combine(
settings.getServerPath(), "ConanSandboxServer.exe"),
conanArgs,
settings.getServerPath());
} }
Timer.periodic(Duration(seconds: 20), (timer) async {
File logFile = File(PathHelper.builder(settings.getServerPath())
.resolve("ConanSandbox")
.resolve("Saved")
.resolve("Logs")
.resolve("ConanSandbox.log")
.build());
await logFile.create(recursive: true);
tailAndPrint(logFile);
timer.cancel();
});
changeState(States.Idle); changeState(States.Idle);
} else if (currentState == States.PreStart) { } else if (currentState == States.PreStart) {
// Perform Backup Task // Perform Backup Task

View file

@ -18,6 +18,9 @@ class SessionData {
static Time mod_update_check_tracker = Time(hours: 0, minutes: 0, seconds: 0); static Time mod_update_check_tracker = Time(hours: 0, minutes: 0, seconds: 0);
static bool enableRestartTimer = false; static bool enableRestartTimer = false;
static bool canPingServer = false;
static Time timeSinceLastPing = Time(hours: 0, minutes: 0, seconds: 0);
static void resetModUpdateChecker() { static void resetModUpdateChecker() {
mod_update_check_tracker = Time(hours: 0, minutes: 0, seconds: 0); mod_update_check_tracker = Time(hours: 0, minutes: 0, seconds: 0);

View file

@ -1,3 +1,4 @@
import 'package:libac_dart/nbt/NbtUtils.dart';
import 'package:libac_dart/nbt/impl/CompoundTag.dart'; import 'package:libac_dart/nbt/impl/CompoundTag.dart';
import 'package:libac_dart/nbt/impl/LongTag.dart'; import 'package:libac_dart/nbt/impl/LongTag.dart';
import 'package:libac_dart/nbt/impl/StringTag.dart'; import 'package:libac_dart/nbt/impl/StringTag.dart';
@ -8,6 +9,7 @@ class Mod {
int mod_id = 0; int mod_id = 0;
String mod_pak = ""; String mod_pak = "";
String mod_hash = ""; String mod_hash = "";
bool enabled = true;
bool newMod = false; bool newMod = false;
UUID _id = UUID.ZERO; UUID _id = UUID.ZERO;
@ -24,7 +26,8 @@ class Mod {
this.mod_id = 0, this.mod_id = 0,
this.newMod = false, this.newMod = false,
this.mod_pak = "Not Initialized", this.mod_pak = "Not Initialized",
this.mod_hash = ""}); this.mod_hash = "",
this.enabled = true});
CompoundTag serialize() { CompoundTag serialize() {
CompoundTag tag = CompoundTag(); CompoundTag tag = CompoundTag();
@ -32,6 +35,7 @@ class Mod {
tag.put("id", LongTag.valueOf(mod_id)); tag.put("id", LongTag.valueOf(mod_id));
tag.put("pak", StringTag.valueOf(mod_pak)); tag.put("pak", StringTag.valueOf(mod_pak));
tag.put("hash", StringTag.valueOf(mod_hash)); tag.put("hash", StringTag.valueOf(mod_hash));
NbtUtils.writeBoolean(tag, "enabled", enabled);
return tag; return tag;
} }
@ -43,6 +47,7 @@ class Mod {
mod_name: ct.get("name")!.asString(), mod_name: ct.get("name")!.asString(),
mod_id: ct.get("id")!.asLong(), mod_id: ct.get("id")!.asLong(),
mod_pak: ct.get("pak")!.asString(), mod_pak: ct.get("pak")!.asString(),
mod_hash: ct.get("hash")!.asString()); mod_hash: ct.get("hash")!.asString(),
enabled: NbtUtils.readBoolean(tag, "enabled"));
} }
} }

View file

@ -8,7 +8,6 @@ import 'package:libac_dart/nbt/impl/CompoundTag.dart';
import 'package:libac_dart/nbt/impl/StringTag.dart'; import 'package:libac_dart/nbt/impl/StringTag.dart';
import 'package:libac_dart/packets/packets.dart'; import 'package:libac_dart/packets/packets.dart';
import 'package:libac_dart/utils/IOTools.dart'; import 'package:libac_dart/utils/IOTools.dart';
import 'package:libac_dart/utils/rcon/rcon_api.dart';
import 'package:libac_dart/utils/uuid/NbtUUID.dart'; import 'package:libac_dart/utils/uuid/NbtUUID.dart';
import 'package:libac_dart/utils/uuid/UUID.dart'; import 'package:libac_dart/utils/uuid/UUID.dart';
import 'package:servermanager/statemachine.dart'; import 'package:servermanager/statemachine.dart';
@ -73,7 +72,7 @@ class Settings {
steamcmd_path = tag.get("steamcmd")!.asString(); steamcmd_path = tag.get("steamcmd")!.asString();
game_path = tag.get("game")!.asString(); game_path = tag.get("game")!.asString();
base_path = tag.get("proton")!.asString(); base_path = tag.get("base")!.asString();
FTS = NbtUtils.readBoolean(tag, "fts"); // First Time Setup. FTS = NbtUtils.readBoolean(tag, "fts"); // First Time Setup.
// FTS should be disabled by the client when sending it back to the server in a C2SApplySettingsPacket // FTS should be disabled by the client when sending it back to the server in a C2SApplySettingsPacket
@ -185,6 +184,10 @@ class Settings {
.existsSync(); .existsSync();
} }
String getWinePrefixPath() {
return PathHelper.builder(base_path).resolve("pfx").build();
}
Future<ProcessResult> RunUpdate({bool valid = true}) { Future<ProcessResult> RunUpdate({bool valid = true}) {
return Process.run(getSteamCmd(), [ return Process.run(getSteamCmd(), [
"+@sSteamCmdForcePlatformType", "+@sSteamCmdForcePlatformType",
@ -228,6 +231,7 @@ class Settings {
List<String> paths = []; List<String> paths = [];
for (Mod mod in inst!.mods) { for (Mod mod in inst!.mods) {
if (!mod.enabled) continue;
var pth = PathHelper(pth: getModPath()) var pth = PathHelper(pth: getModPath())
.resolve("steamapps") .resolve("steamapps")
.resolve("workshop") .resolve("workshop")
@ -252,27 +256,35 @@ class Settings {
Future<bool> sendRconCommand(String command) async { Future<bool> sendRconCommand(String command) async {
try { try {
createSocket("127.0.0.1", port: inst!.serverSettings.RconPort); Process.run("/app/rcon", [
"-H",
if (inst!.serverSettings.RconPassword.isNotEmpty) "127.0.0.1",
login(inst!.serverSettings.RconPassword); "-p",
inst!.serverSettings.RconPassword,
return sendCommand(command); "-P",
"${inst!.serverSettings.RconPort}",
command
]);
return true;
} catch (E) { } catch (E) {
// Sending rcon failed // Sending rcon failed
return false; return false;
} finally {
close();
} }
} }
Future<void> initializeWine() async { Future<void> initializeWine() async {
await runWinetrick("win10"); await runWinetrick("win10");
await runWinetrick("w_workaround_wine_bug-50894");
await runWinetrick("cmd");
await runWinetrick("vcrun2013");
await runWinetrick("vcrun2015"); await runWinetrick("vcrun2015");
await runWinetrick("vcrun2017"); await runWinetrick("vcrun2017");
await runWinetrick("vcrun2019"); await runWinetrick("vcrun2019");
await runWinetrick("vcrun2022"); await runWinetrick("vcrun2022");
await runWinetrick("corefonts"); await runWinetrick("andale");
await runWinetrick("allfonts");
await runWinetrick("gdiplus");
await runWinetrick("dxsdk_jun2010");
} }
Future<void> initializeSteamCmd() async { Future<void> initializeSteamCmd() async {

View file

@ -1,3 +1,4 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:libac_dart/utils/IOTools.dart'; import 'package:libac_dart/utils/IOTools.dart';
@ -6,8 +7,7 @@ import 'package:servermanager/structs/settings.dart';
Future<void> runWine(String command, List<String> argx) async { Future<void> runWine(String command, List<String> argx) async {
Settings settings = Settings(); Settings settings = Settings();
Directory dir = Directory dir = Directory(settings.getWinePrefixPath());
Directory(PathHelper.builder(settings.base_path).resolve("pfx").build());
if (dir.existsSync()) { if (dir.existsSync()) {
await dir.delete(recursive: true); await dir.delete(recursive: true);
@ -37,8 +37,7 @@ Future<void> runWine(String command, List<String> argx) async {
Future<void> runWinetrick(String trick) async { Future<void> runWinetrick(String trick) async {
Settings settings = Settings(); Settings settings = Settings();
Directory dir = Directory dir = Directory(settings.getWinePrefixPath());
Directory(PathHelper.builder(settings.base_path).resolve("pfx").build());
if (dir.existsSync()) { if (dir.existsSync()) {
await dir.delete(recursive: true); await dir.delete(recursive: true);
@ -46,7 +45,7 @@ Future<void> runWinetrick(String trick) async {
await dir.create(recursive: true); await dir.create(recursive: true);
Map<String, String> env = Map.from(Platform.environment); Map<String, String> env = Map.from(Platform.environment);
env["WINEPREFIX"] = dir.path; //env["WINEPREFIX"] = dir.path;
try { try {
List<String> args = ["-q", trick]; List<String> args = ["-q", trick];
@ -77,7 +76,7 @@ Future<void> runDetachedWine(
await dir.create(recursive: true); await dir.create(recursive: true);
Map<String, String> env = Map.from(Platform.environment); Map<String, String> env = Map.from(Platform.environment);
env["WINEPREFIX"] = dir.path; //env["WINEPREFIX"] = dir.path;
try { try {
List<String> args = [command]; List<String> args = [command];
@ -87,7 +86,16 @@ Future<void> runDetachedWine(
"wine", args, // Run arbitrary command with arguments "wine", args, // Run arbitrary command with arguments
environment: env, environment: env,
workingDirectory: workingDir, workingDirectory: workingDir,
mode: ProcessStartMode.normal); mode: ProcessStartMode.detachedWithStdio);
StateMachine.PROC!.stdout
.transform(utf8.decoder)
.transform(LineSplitter())
.forEach((line) {});
StateMachine.PROC!.stderr
.transform(utf8.decoder)
.transform(LineSplitter())
.forEach((line) {});
StateMachine.monitorProcess(); StateMachine.monitorProcess();
} catch (e) { } catch (e) {

View file

@ -40,7 +40,7 @@ dependencies:
crypto: crypto:
libac_dart: libac_dart:
hosted: https://git.zontreck.com/api/packages/AriasCreations/pub/ hosted: https://git.zontreck.com/api/packages/AriasCreations/pub/
version: 1.0.26 version: 1.0.30
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:

5
scripts/rcon.py Normal file
View file

@ -0,0 +1,5 @@
from mcrcon import MCRcon
import sys
with MCRcon(sys.argv[1], sys.argv[2], sys.argv[3]) as mcr:
print(mcr.command(sys.argv[4]))