Switch to wine, add winetricks

This commit is contained in:
zontreck 2024-06-03 18:33:13 -07:00
parent 0c30b3f820
commit 0ae098318a
10 changed files with 104 additions and 79 deletions

View file

@ -24,12 +24,10 @@ void main() async {
PathHelper steamCmd = PathHelper steamCmd =
PathHelper.builder(helper.build()).resolve("steamcmd").mkdir(); PathHelper.builder(helper.build()).resolve("steamcmd").mkdir();
PathHelper game = PathHelper.builder(helper.build()).resolve("game").mkdir(); PathHelper game = PathHelper.builder(helper.build()).resolve("game").mkdir();
PathHelper proton = PathHelper(pth: helper.build()).resolve("proton").mkdir();
settings.base_path = helper.build(); settings.base_path = helper.build();
settings.game_path = game.build(); settings.game_path = game.build();
settings.steamcmd_path = steamCmd.build(); settings.steamcmd_path = steamCmd.build();
settings.proton_path = proton.build();
print("Setup of local system variables completed"); print("Setup of local system variables completed");
@ -38,7 +36,11 @@ void main() async {
print("Initializing SteamCMD"); print("Initializing SteamCMD");
await settings.initializeSteamCmd(); await settings.initializeSteamCmd();
print("Initialized Steamcmd and Proton"); print("Initialized Steamcmd");
print("Running winetricks");
await settings.initializeWine();
print("Finished installing needed DLLs");
print("Checking for game server updates..."); print("Checking for game server updates...");
await settings.RunUpdate(valid: false); await settings.RunUpdate(valid: false);
@ -69,7 +71,17 @@ void main() async {
} }
print("Starting up server manager server wrapper"); print("Starting up server manager server wrapper");
await PacketServer.start(); 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);
print("Server stopping"); print("Server stopping");
} }

View file

@ -54,7 +54,16 @@ class ServerPage extends StatelessWidget {
// Send login packet to server // Send login packet to server
Settings settings = Settings(); Settings settings = Settings();
settings.client = PacketClient(); settings.client = PacketClient();
await settings.client!.startConnect(serverIP.text); int port = 25306;
String ip = serverIP.text;
List<String> ipParts = ip.split(':');
if (ipParts.length == 2) {
ip = ipParts[0];
port = int.parse(ipParts[1]);
}
print("Attempting to connect to FQDN/IP : ${ip} on port ${port}");
await settings.client!.startConnect(ip, port);
C2SLoginPacket login = C2SLoginPacket(); C2SLoginPacket login = C2SLoginPacket();
login.username = username.text; login.username = username.text;

View file

@ -10,25 +10,26 @@ class ServerSettingsPage extends StatefulWidget {
} }
class ServerSettingsState extends State<ServerSettingsPage> { class ServerSettingsState extends State<ServerSettingsPage> {
bool firstRun = true;
String pass = ""; String pass = "";
TextEditingController passwordController = TextEditingController(); TextEditingController passwordController = TextEditingController();
int rconPort = 0; int rconPort = 0;
int gPort = 0; int gPort = 0;
int qPort = 0; int qPort = 0;
int wPort = 25306;
@override
void didChangeDependencies() {
var args = ModalRoute.of(context)!.settings.arguments as ServerSettings;
passwordController.text = args.RconPassword;
rconPort = args.RconPort;
gPort = args.GamePort;
qPort = args.QueryPort;
wPort = args.WrapperPort;
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (firstRun) {
var args = ModalRoute.of(context)!.settings.arguments as ServerSettings;
passwordController.text = args.RconPassword;
rconPort = args.RconPort;
gPort = args.GamePort;
qPort = args.QueryPort;
firstRun = false;
}
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text("Server Settings"), title: Text("Server Settings"),
@ -130,7 +131,8 @@ class ServerSettingsState extends State<ServerSettingsPage> {
RconPassword: passwordController.text, RconPassword: passwordController.text,
RconPort: rconPort, RconPort: rconPort,
GamePort: gPort, GamePort: gPort,
QueryPort: qPort)); QueryPort: qPort,
WrapperPort: wPort));
}, },
child: Text("Submit")) child: Text("Submit"))
], ],

View file

@ -55,11 +55,11 @@ class HomePageState extends State<HomePage> {
child: Column( child: Column(
children: [ children: [
if (Platform.isLinux) if (Platform.isLinux)
ListTile( SwitchListTile(
title: Text("Proton"), value: true,
leading: Icon(CupertinoIcons.gear), onChanged: (V) {},
subtitle: Text("Linux Proton: ${settings.proton_path}"), title: Text("Wine"),
), // Not yet implemented ),
ListTile( ListTile(
title: Text("SteamCMD"), title: Text("SteamCMD"),
leading: Icon(Icons.comment_rounded), leading: Icon(Icons.comment_rounded),

View file

@ -4,10 +4,10 @@ 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:servermanager/game.dart'; import 'package:servermanager/game.dart';
import 'package:servermanager/proton.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';
import 'package:servermanager/structs/settings.dart'; import 'package:servermanager/structs/settings.dart';
import 'package:servermanager/wine.dart';
enum States { enum States {
Idle, // For when the state machine is waiting for a state change Idle, // For when the state machine is waiting for a state change
@ -145,7 +145,7 @@ class StateMachine {
conanArgs, conanArgs,
workingDirectory: settings.getServerPath()); workingDirectory: settings.getServerPath());
} else { } else {
runDetachedProton( runDetachedWine(
PathHelper.combine( PathHelper.combine(
settings.getServerPath(), "ConanSandboxServer.exe"), settings.getServerPath(), "ConanSandboxServer.exe"),
conanArgs, conanArgs,

View file

@ -7,19 +7,22 @@ class ServerSettings {
final int RconPort; final int RconPort;
final int GamePort; final int GamePort;
final int QueryPort; final int QueryPort;
final int WrapperPort;
const ServerSettings( const ServerSettings(
{required this.RconPassword, {required this.RconPassword,
required this.RconPort, required this.RconPort,
required this.GamePort, required this.GamePort,
required this.QueryPort}); required this.QueryPort,
required this.WrapperPort});
static ServerSettings deserialize(CompoundTag tag) { static ServerSettings deserialize(CompoundTag tag) {
return ServerSettings( return ServerSettings(
RconPassword: tag.get(TAG_PASSWORD)?.asString() ?? "", RconPassword: tag.get(TAG_PASSWORD)?.asString() ?? "",
RconPort: tag.get(TAG_RCON_PORT)?.asInt() ?? 25565, RconPort: tag.get(TAG_RCON_PORT)?.asInt() ?? 25565,
GamePort: tag.get(TAG_GAME_PORT)?.asInt() ?? 0, GamePort: tag.get(TAG_GAME_PORT)?.asInt() ?? 0,
QueryPort: tag.get(TAG_QUERY_PORT)?.asInt() ?? 0); QueryPort: tag.get(TAG_QUERY_PORT)?.asInt() ?? 0,
WrapperPort: tag.get(TAG_WRAPPER_PORT)?.asInt() ?? 25306);
} }
CompoundTag serialize() { CompoundTag serialize() {
@ -28,6 +31,7 @@ class ServerSettings {
tag.put(TAG_RCON_PORT, IntTag.valueOf(RconPort)); tag.put(TAG_RCON_PORT, IntTag.valueOf(RconPort));
tag.put(TAG_GAME_PORT, IntTag.valueOf(GamePort)); tag.put(TAG_GAME_PORT, IntTag.valueOf(GamePort));
tag.put(TAG_QUERY_PORT, IntTag.valueOf(QueryPort)); tag.put(TAG_QUERY_PORT, IntTag.valueOf(QueryPort));
tag.put(TAG_WRAPPER_PORT, IntTag.valueOf(WrapperPort));
return tag; return tag;
} }
@ -37,4 +41,5 @@ class ServerSettings {
static const TAG_RCON_PORT = "rcon"; static const TAG_RCON_PORT = "rcon";
static const TAG_GAME_PORT = "game"; static const TAG_GAME_PORT = "game";
static const TAG_QUERY_PORT = "query"; static const TAG_QUERY_PORT = "query";
static const TAG_WRAPPER_PORT = "wrapper";
} }

View file

@ -15,8 +15,7 @@ import 'package:servermanager/statemachine.dart';
import 'package:servermanager/structs/credentials.dart'; import 'package:servermanager/structs/credentials.dart';
import 'package:servermanager/structs/mod.dart'; import 'package:servermanager/structs/mod.dart';
import 'package:servermanager/structs/settingsEntry.dart'; import 'package:servermanager/structs/settingsEntry.dart';
import 'package:servermanager/wine.dart';
import '../proton.dart';
class Settings { class Settings {
final String windows = final String windows =
@ -27,9 +26,6 @@ class Settings {
final String Base2FAPath = final String Base2FAPath =
"https://github.com/zontreck/steamcmd-2fa/releases/download/0.2.0/steamcmd-2fa"; "https://github.com/zontreck/steamcmd-2fa/releases/download/0.2.0/steamcmd-2fa";
final String PROTON_URL =
"https://github.com/GloriousEggroll/proton-ge-custom/releases/download/GE-Proton9-5/GE-Proton9-5.tar.gz";
Settings._(); Settings._();
static final Settings Instance = Settings._(); static final Settings Instance = Settings._();
@ -37,7 +33,6 @@ class Settings {
String steamcmd_path = ""; String steamcmd_path = "";
String game_path = ""; String game_path = "";
String proton_path = "";
String base_path = ""; String base_path = "";
bool FTS = true; bool FTS = true;
Credentials serverLoginCreds = Credentials serverLoginCreds =
@ -56,7 +51,6 @@ class Settings {
CompoundTag tag = CompoundTag(); CompoundTag tag = CompoundTag();
tag.put("steamcmd", StringTag.valueOf(steamcmd_path)); tag.put("steamcmd", StringTag.valueOf(steamcmd_path));
tag.put("game", StringTag.valueOf(game_path)); tag.put("game", StringTag.valueOf(game_path));
tag.put("proton", StringTag.valueOf(proton_path));
tag.put("base", StringTag.valueOf(base_path)); tag.put("base", StringTag.valueOf(base_path));
NbtUtils.writeBoolean(tag, "fts", FTS); NbtUtils.writeBoolean(tag, "fts", FTS);
@ -79,7 +73,6 @@ 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();
proton_path = tag.get("proton")!.asString();
base_path = tag.get("proton")!.asString(); base_path = tag.get("proton")!.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
@ -170,18 +163,6 @@ class Settings {
return PathHelper(pth: base_path).resolve("mods.jail").build(); return PathHelper(pth: base_path).resolve("mods.jail").build();
} }
String getProtonPath() {
return PathHelper(pth: base_path).resolve("proton").build();
}
String getProtonExecutablePath() {
return PathHelper(pth: base_path)
.resolve("proton")
.resolve("GE-Proton9-5")
.resolve("proton")
.build();
}
Future<void> createModFolderIfNotExists() async { Future<void> createModFolderIfNotExists() async {
if (Directory(getModPath()).existsSync()) { if (Directory(getModPath()).existsSync()) {
return; return;
@ -269,10 +250,6 @@ class Settings {
flush: true, mode: FileMode.writeOnly); flush: true, mode: FileMode.writeOnly);
} }
Future<void> initializeProtonPrefix() async {
runProton("echo", ["hello"]);
}
Future<bool> sendRconCommand(String command) async { Future<bool> sendRconCommand(String command) async {
try { try {
createSocket("127.0.0.1", port: inst!.serverSettings.RconPort); createSocket("127.0.0.1", port: inst!.serverSettings.RconPort);
@ -289,19 +266,13 @@ class Settings {
} }
} }
Future<void> initializeProton() async { Future<void> initializeWine() async {
Dio dio = Dio(); await runWinetrick("win10");
print("Downloading proton..."); await runWinetrick("vcrun2015");
final path = PathHelper(pth: getProtonPath()).resolve("proton.tar.gz"); await runWinetrick("vcrun2017");
await dio.download(PROTON_URL, path.build()); await runWinetrick("vcrun2019");
await runWinetrick("vcrun2022");
String oldWD = Directory.current.path; await runWinetrick("corefonts");
Directory.current = getProtonPath();
Process.runSync("tar", ["-xvf", "proton.tar.gz"]);
Process.runSync("rm", ["-f", "proton.tar.gz"]);
Directory.current = oldWD; // Restore the old working directory
print("Finished!");
} }
Future<void> initializeSteamCmd() async { Future<void> initializeSteamCmd() async {
@ -377,9 +348,6 @@ class Settings {
Process.runSync("touch", ["cxinit"]); Process.runSync("touch", ["cxinit"]);
Process.runSync("./steamcmd.sh", ["+quit"]); Process.runSync("./steamcmd.sh", ["+quit"]);
print("Completed. Initializing Proton");
await initializeProton();
} }
Directory.current = Directory(game_path); Directory.current = Directory(game_path);

View file

@ -18,7 +18,8 @@ class SettingsEntry {
RconPassword: "Password01234", RconPassword: "Password01234",
RconPort: 7779, RconPort: 7779,
GamePort: 7780, GamePort: 7780,
QueryPort: 7782); QueryPort: 7782,
WrapperPort: 25306);
static SettingsEntry deserialize(CompoundTag tag) { static SettingsEntry deserialize(CompoundTag tag) {
SettingsEntry st = SettingsEntry(); SettingsEntry st = SettingsEntry();

View file

@ -4,7 +4,7 @@ import 'package:libac_dart/utils/IOTools.dart';
import 'package:servermanager/statemachine.dart'; import 'package:servermanager/statemachine.dart';
import 'package:servermanager/structs/settings.dart'; import 'package:servermanager/structs/settings.dart';
Future<void> runProton(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(PathHelper.builder(settings.base_path).resolve("pfx").build()); Directory(PathHelper.builder(settings.base_path).resolve("pfx").build());
@ -15,15 +15,14 @@ Future<void> runProton(String command, List<String> argx) 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["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = "~/.steam"; env["WINEPREFIX"] = dir.path;
env["STEAM_COMPAT_DATA_PATH"] = dir.path;
try { try {
List<String> args = ["run", command]; List<String> args = [command];
args.addAll(argx); args.addAll(argx);
ProcessResult res = await Process.run( ProcessResult res = await Process.run(
settings.getProtonExecutablePath(), "wine",
args, // Run arbitrary command with arguments args, // Run arbitrary command with arguments
environment: env, environment: env,
); );
@ -36,7 +35,37 @@ Future<void> runProton(String command, List<String> argx) async {
} }
} }
Future<void> runDetachedProton( Future<void> runWinetrick(String trick) async {
Settings settings = Settings();
Directory dir =
Directory(PathHelper.builder(settings.base_path).resolve("pfx").build());
if (dir.existsSync()) {
await dir.delete(recursive: true);
}
await dir.create(recursive: true);
Map<String, String> env = Map.from(Platform.environment);
env["WINEPREFIX"] = dir.path;
try {
List<String> args = ["-q", trick];
ProcessResult res = await Process.run(
"winetricks",
args, // Run arbitrary command with arguments
environment: env,
);
print('Exit code: ${res.exitCode}');
print('stdout: ${res.stdout}');
print('stderr: ${res.stderr}');
} catch (e) {
print('Error executing command: $e');
}
}
Future<void> runDetachedWine(
String command, List<String> argx, String workingDir) async { String command, List<String> argx, String workingDir) async {
Settings settings = Settings(); Settings settings = Settings();
Directory dir = Directory dir =
@ -48,15 +77,14 @@ Future<void> runDetachedProton(
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["STEAM_COMPAT_CLIENT_INSTALL_PATH"] = "~/.steam"; env["WINEPREFIX"] = dir.path;
env["STEAM_COMPAT_DATA_PATH"] = dir.path;
try { try {
List<String> args = ["run", command]; List<String> args = [command];
args.addAll(argx); args.addAll(argx);
StateMachine.PROC = await Process.start(settings.getProtonExecutablePath(), StateMachine.PROC = await Process.start(
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.normal);

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.22 version: 1.0.26
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: