Get wrapper working
This commit is contained in:
parent
0ae098318a
commit
26434a9123
19 changed files with 211 additions and 255 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -53,3 +53,6 @@ build
|
||||||
|
|
||||||
out
|
out
|
||||||
RELEASE
|
RELEASE
|
||||||
|
|
||||||
|
*.DEPS
|
||||||
|
*.dat
|
29
Dockerfile
29
Dockerfile
|
@ -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
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
FROM debian:latest
|
FROM ghcr.io/parkervcp/yolks:debian
|
||||||
|
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
|
@ -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"]
|
|
|
@ -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}
|
|
|
@ -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
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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")
|
||||||
],
|
],
|
||||||
))
|
)),
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -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"))
|
||||||
],
|
],
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
5
scripts/rcon.py
Normal 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]))
|
Loading…
Reference in a new issue